diff --git a/src/shared/file.c b/src/shared/file.c index 5a412cd..13e6f2b 100644 --- a/src/shared/file.c +++ b/src/shared/file.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -199,10 +200,25 @@ bool file_send_single_calls(File* file, int file_descriptor, bool use_metadata, return true; } +static bool has_path_traversal(const char* path) { + // Check if ".." appears as a path component — at start, end, or between '/' + const char* p = path; + while ((p = strstr(p, "..")) != NULL) { + bool at_start = (p == path); + bool after_slash = (p > path && p[-1] == '/'); + bool at_end = (p[2] == '\0'); + bool before_slash = (p[2] == '/'); + if ((at_start || after_slash) && (at_end || before_slash)) + return true; + p += 2; + } + return false; +} + bool file_save_to_disk(const char* root_directory, File* file) { if (file->type == FILE_TYPE_SYMLINK && file->link_target) { // Validate link_target — reject absolute paths or traversal - if (file->link_target[0] == '/' || strstr(file->link_target, "..") != NULL) { + if (file->link_target[0] == '/' || has_path_traversal(file->link_target)) { log_message(LOG_LEVEL_ERROR, "Path traversal blocked in symlink target: %s", file->link_target); return false; @@ -210,7 +226,7 @@ bool file_save_to_disk(const char* root_directory, File* file) { char* disk_path = path_cat((char*)root_directory, file->path); if (disk_path == NULL) return false; - if (strstr(disk_path, "..") != NULL) { + if (has_path_traversal(disk_path)) { log_message(LOG_LEVEL_ERROR, "Path traversal blocked: %s", disk_path); free(disk_path); return false; @@ -226,7 +242,7 @@ bool file_save_to_disk(const char* root_directory, File* file) { char* disk_path = path_cat((char*)root_directory, file->path); if (disk_path == NULL) return false; - if (strstr(disk_path, "..") != NULL) { + if (has_path_traversal(disk_path)) { log_message(LOG_LEVEL_ERROR, "Path traversal blocked: %s", disk_path); free(disk_path); return false; @@ -625,6 +641,8 @@ bool file_send_sendfile(File* file, int file_descriptor, bool use_metadata, int while ((unsigned long long)offset < file_size) { ssize_t sent = sendfile(file_descriptor, fd, &offset, file_size - offset); if (sent == -1) { + if (errno == EINTR) + continue; perror("sendfile failed"); close(fd); return false;