Fix path traversal in static file server

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-05-18 15:31:39 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-05-18 15:31:39 +0200
Commit 7ff03bdcdedbc9fdad7d6b054eb5c7cacb6845d6 (patch)
-rw-r--r-- http.c 69
1 files changed, 47 insertions, 22 deletions
diff --git a/http.c b/http.c
...
3
#include <sys/socket.h>
3
#include <sys/socket.h>
4
#include <sys/stat.h>
4
#include <sys/stat.h>
5
#include <netinet/in.h>
5
#include <netinet/in.h>
  
6
#include <limits.h>
6
  
7
  
7
#include <wolfssl/options.h>
8
#include <wolfssl/options.h>
8
#include <wolfssl/wolfcrypt/hash.h>
9
#include <wolfssl/wolfcrypt/hash.h>
...
721
				if (!client->response_sent) {
722
				if (!client->response_sent) {
722
					int found = 0;
723
					int found = 0;
723
					if (static_dir && strcasecmp(method, "GET") == 0) {
724
					if (static_dir && strcasecmp(method, "GET") == 0) {
724
						char full_path_buf[1024];
725
						char requested_path[PATH_MAX];
725
						if (!strstr(path, "..")) {
726
						char resolved_path[PATH_MAX];
726
							snprintf(full_path_buf, sizeof(full_path_buf), "%s%s", static_dir, path);
727
						snprintf(requested_path, sizeof(requested_path), "%s/%s", static_dir, path);
727
							struct stat st;
728
  
728
							if (stat(full_path_buf, &st) == 0 && S_ISDIR(st.st_mode)) {
729
						if (realpath(requested_path, resolved_path)) {
729
								strncat(full_path_buf, "/index.html", sizeof(full_path_buf) - strlen(full_path_buf) - 1);
730
							size_t static_dir_len = strlen(static_dir);
730
							}
731
							if (strncmp(resolved_path, static_dir, static_dir_len) == 0 &&
731
							FILE *fp = fopen(full_path_buf, "rb");
732
								(resolved_path[static_dir_len] == '\0' || resolved_path[static_dir_len] == '/')) {
732
							if (fp) {
733
  
733
								fseek(fp, 0, SEEK_END);
734
								struct stat st;
734
								size_t fsize = ftell(fp);
735
								if (stat(resolved_path, &st) == 0 && S_ISDIR(st.st_mode)) {
735
								fseek(fp, 0, SEEK_SET);
736
									char index_path[PATH_MAX];
736
								char *fcontent = malloc(fsize + 1);
737
									snprintf(index_path, sizeof(index_path), "%s/index.html", resolved_path);
737
								if (fcontent) {
738
									if (realpath(index_path, resolved_path)) {
738
									if (fread(fcontent, 1, fsize, fp) == fsize) {
739
										if (!(strncmp(resolved_path, static_dir, static_dir_len) == 0 &&
739
										const char *mime = get_mime_type(full_path_buf);
740
											(resolved_path[static_dir_len] == '\0' || resolved_path[static_dir_len] == '/'))) {
740
										client->status_code = 200;
741
											resolved_path[0] = '\0';
741
										send_response(client, fcontent, fsize, mime);
742
										}
742
										found = 1;
743
									} else {
  
744
										resolved_path[0] = '\0';
  
745
									}
  
746
								}
  
747
  
  
748
								if (resolved_path[0] != '\0') {
  
749
									FILE *fp = fopen(resolved_path, "rb");
  
750
									if (fp) {
  
751
										fseek(fp, 0, SEEK_END);
  
752
										size_t fsize = ftell(fp);
  
753
										fseek(fp, 0, SEEK_SET);
  
754
										char *fcontent = malloc(fsize + 1);
  
755
										if (fcontent) {
  
756
											if (fread(fcontent, 1, fsize, fp) == fsize) {
  
757
												const char *mime = get_mime_type(resolved_path);
  
758
												client->status_code = 200;
  
759
												send_response(client, fcontent, fsize, mime);
  
760
												found = 1;
  
761
											}
  
762
											free(fcontent);
  
763
										}
  
764
										fclose(fp);
743
									}
765
									}
744
									free(fcontent);
  
745
								}
766
								}
746
								fclose(fp);
  
747
							}
767
							}
748
						}
768
						}
749
					}
769
					}
...
834
	if (static_dir) {
854
	if (static_dir) {
835
		free(static_dir);
855
		free(static_dir);
836
	}
856
	}
837
	static_dir = strdup(path);
857
	char resolved[PATH_MAX];
  
858
	if (realpath(path, resolved)) {
  
859
		static_dir = strdup(resolved);
  
860
	} else {
  
861
		static_dir = strdup(path);
  
862
	}
838
	return 0;
863
	return 0;
839
}
864
}
840
  
865
  
...