From 80768d64d4fe630a1f929b8cfddf52219128a10b Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 30 Jul 2026 18:49:25 +0200 Subject: [PATCH] fix: security issues #192 #191 #190 #189 #188 #187 --- src/client/scanner.c | 56 +++++++++++++++++++++++++++++++++++++++++-- src/shared/chunk.c | 20 ++++++++++++++++ src/shared/protocol.h | 3 +++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/client/scanner.c b/src/client/scanner.c index 0bcd851..1a4a944 100644 --- a/src/client/scanner.c +++ b/src/client/scanner.c @@ -345,11 +345,63 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata char* cur_path = path_cat(root_directory, entry->d_name); if (!cur_path) continue; - struct stat st; - if (stat(cur_path, &st) != 0) { + struct stat lstats; + if (lstat(cur_path, &lstats) != 0) { free(cur_path); continue; } + bool is_symlink = S_ISLNK(lstats.st_mode); + + // Skip symlinks unless the user explicitly enabled following/copying them. + if (is_symlink && !follow_symlinks && !copy_links && !safe_links && + !copy_unsafe_links) { + free(cur_path); + continue; + } + + // --safe-links: reject symlinks pointing outside the source tree. + if (is_symlink && safe_links) { + char link_target[4096]; + ssize_t len = readlink(cur_path, link_target, sizeof(link_target) - 1); + if (len < 0) { + free(cur_path); + continue; + } + link_target[len] = ''; + if (link_target[0] == '/') { + free(cur_path); + continue; + } + } + + // --copy-unsafe-links (without --copy-links): only copy absolute symlinks. + if (is_symlink && copy_unsafe_links && !copy_links) { + char link_target[4096]; + ssize_t len = readlink(cur_path, link_target, sizeof(link_target) - 1); + if (len < 0) { + free(cur_path); + continue; + } + link_target[len] = ''; + bool unsafe = (link_target[0] == '/'); + if (!unsafe) { + free(cur_path); + continue; + } + } + + // Determine whether to use lstat or stat results for the entry. + struct stat st; + bool use_lstat_res = is_symlink && follow_symlinks && !copy_links; + if (use_lstat_res) { + st = lstats; + } else { + if (stat(cur_path, &st) != 0) { + free(cur_path); + continue; + } + } + if (S_ISDIR(st.st_mode)) { array_list_add(subdirs, cur_path); } else { diff --git a/src/shared/chunk.c b/src/shared/chunk.c index 6c5adbd..10b275a 100644 --- a/src/shared/chunk.c +++ b/src/shared/chunk.c @@ -12,6 +12,9 @@ #include "metadata.h" #include "protocol.h" +/* Maximum individual file data size within a chunk (64 MB) */ +#define MAX_FILE_DATA_SIZE (64ULL * 1024 * 1024) + Chunk* chunk_create(File** items, int element_count) { Chunk* chunk = (Chunk*)malloc(sizeof(Chunk)); if (chunk == NULL) { @@ -157,6 +160,14 @@ Chunk* chunk_deserialize(Data* data, bool use_metadata) { return NULL; } + // Reject individual file data larger than the maximum allowed size. + if (file_data_size > MAX_FILE_DATA_SIZE) { + log_message(LOG_LEVEL_ERROR, "File data size %zu exceeds maximum %llu", + file_data_size, (unsigned long long)MAX_FILE_DATA_SIZE); + array_list_delete(files); + return NULL; + } + void* file_data = malloc(file_data_size); if (file_data == NULL) { perror("Could not allocate memory for file data"); @@ -210,6 +221,15 @@ Chunk* receive_chunk_data(int fd, const Config* config) { return NULL; } } + + // Reject chunks larger than the maximum allowed size to prevent OOM. + if (data_to_process->size > MAX_CHUNK_SIZE) { + log_message(LOG_LEVEL_ERROR, "Chunk size %zu exceeds maximum %llu", + data_to_process->size, (unsigned long long)MAX_CHUNK_SIZE); + data_destroy(data_to_process); + return NULL; + } + Chunk* chunk = chunk_deserialize(data_to_process, config->use_metadata); data_destroy(data_to_process); if (chunk == NULL) diff --git a/src/shared/protocol.h b/src/shared/protocol.h index b65584d..9173fd3 100644 --- a/src/shared/protocol.h +++ b/src/shared/protocol.h @@ -11,6 +11,9 @@ /* Maximum allowed data payload size for receive_data (100 MB) */ #define MAX_DATA_PAYLOAD_SIZE (100ULL * 1024 * 1024) +/* Maximum chunk size (64 MB) — prevents unbounded allocation from the wire */ +#define MAX_CHUNK_SIZE (64ULL * 1024 * 1024) + typedef struct ssl_st SSL; typedef int Status;