Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c75ccfd80b |
+99
-9
@@ -99,7 +99,13 @@ static int read_patterns_from_file(const char* filepath, char*** patterns, int*
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
*patterns = tmp;
|
*patterns = tmp;
|
||||||
(*patterns)[(*count)++] = str_dup(p);
|
(*patterns)[*count] = str_dup(p);
|
||||||
|
if (!(*patterns)[*count]) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for pattern\n");
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(*count)++;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -142,7 +148,15 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--dry-run") == 0) {
|
} else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--dry-run") == 0) {
|
||||||
config->dry_run = true;
|
config->dry_run = true;
|
||||||
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
config->ssh_port = atoi(argv[++i]);
|
char* end;
|
||||||
|
errno = 0;
|
||||||
|
long val = strtol(argv[++i], &end, 10);
|
||||||
|
if (errno != 0 || *end != '\0' || val <= 0 || val > 65535) {
|
||||||
|
fprintf(stderr, "Error: -p must be a valid port number (1-65535)\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
config->ssh_port = (int)val;
|
||||||
} else if (strcmp(argv[i], "--delete") == 0) {
|
} else if (strcmp(argv[i], "--delete") == 0) {
|
||||||
config->use_delete = true;
|
config->use_delete = true;
|
||||||
} else if (strcmp(argv[i], "--exclude") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--exclude") == 0 && i + 1 < argc) {
|
||||||
@@ -153,7 +167,13 @@ int main(int argc, char* argv[]) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
config->exclude_patterns = tmp;
|
config->exclude_patterns = tmp;
|
||||||
config->exclude_patterns[config->exclude_count++] = str_dup(argv[++i]);
|
config->exclude_patterns[config->exclude_count] = str_dup(argv[++i]);
|
||||||
|
if (!config->exclude_patterns[config->exclude_count]) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for exclude pattern\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
config->exclude_count++;
|
||||||
} else if (strcmp(argv[i], "--include") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--include") == 0 && i + 1 < argc) {
|
||||||
char** tmp = realloc(config->include_patterns, (config->include_count + 1) * sizeof(char*));
|
char** tmp = realloc(config->include_patterns, (config->include_count + 1) * sizeof(char*));
|
||||||
if (!tmp) {
|
if (!tmp) {
|
||||||
@@ -162,11 +182,31 @@ int main(int argc, char* argv[]) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
config->include_patterns = tmp;
|
config->include_patterns = tmp;
|
||||||
config->include_patterns[config->include_count++] = str_dup(argv[++i]);
|
config->include_patterns[config->include_count] = str_dup(argv[++i]);
|
||||||
|
if (!config->include_patterns[config->include_count]) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for include pattern\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
config->include_count++;
|
||||||
} else if (strcmp(argv[i], "--max-size") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--max-size") == 0 && i + 1 < argc) {
|
||||||
config->max_size = strtoull(argv[++i], NULL, 10);
|
char* end;
|
||||||
|
errno = 0;
|
||||||
|
config->max_size = strtoull(argv[++i], &end, 10);
|
||||||
|
if (errno != 0 || *end != '\0') {
|
||||||
|
fprintf(stderr, "Error: --max-size must be a valid non-negative integer\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--min-size") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--min-size") == 0 && i + 1 < argc) {
|
||||||
config->min_size = strtoull(argv[++i], NULL, 10);
|
char* end;
|
||||||
|
errno = 0;
|
||||||
|
config->min_size = strtoull(argv[++i], &end, 10);
|
||||||
|
if (errno != 0 || *end != '\0') {
|
||||||
|
fprintf(stderr, "Error: --min-size must be a valid non-negative integer\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--incremental") == 0) {
|
} else if (strcmp(argv[i], "--incremental") == 0) {
|
||||||
config->use_incremental = true;
|
config->use_incremental = true;
|
||||||
} else if (strcmp(argv[i], "--delta") == 0) {
|
} else if (strcmp(argv[i], "--delta") == 0) {
|
||||||
@@ -198,9 +238,19 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "--source-dir") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--source-dir") == 0 && i + 1 < argc) {
|
||||||
free(config->send_directory);
|
free(config->send_directory);
|
||||||
config->send_directory = str_dup(argv[++i]);
|
config->send_directory = str_dup(argv[++i]);
|
||||||
|
if (!config->send_directory) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--dest-dir") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--dest-dir") == 0 && i + 1 < argc) {
|
||||||
free(config->receive_root_directory);
|
free(config->receive_root_directory);
|
||||||
config->receive_root_directory = str_dup(argv[++i]);
|
config->receive_root_directory = str_dup(argv[++i]);
|
||||||
|
if (!config->receive_root_directory) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--save-to-disk") == 0) {
|
} else if (strcmp(argv[i], "--save-to-disk") == 0) {
|
||||||
config->save_to_disk = true;
|
config->save_to_disk = true;
|
||||||
} else if (strcmp(argv[i], "-M") == 0 || strcmp(argv[i], "--preserve") == 0) {
|
} else if (strcmp(argv[i], "-M") == 0 || strcmp(argv[i], "--preserve") == 0) {
|
||||||
@@ -218,8 +268,21 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "--server-host") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--server-host") == 0 && i + 1 < argc) {
|
||||||
free(config->server_host);
|
free(config->server_host);
|
||||||
config->server_host = str_dup(argv[++i]);
|
config->server_host = str_dup(argv[++i]);
|
||||||
|
if (!config->server_host) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--server-port") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--server-port") == 0 && i + 1 < argc) {
|
||||||
config->server_port = atoi(argv[++i]);
|
char* end;
|
||||||
|
errno = 0;
|
||||||
|
long val = strtol(argv[++i], &end, 10);
|
||||||
|
if (errno != 0 || *end != '\0' || val <= 0 || val > 65535) {
|
||||||
|
fprintf(stderr, "Error: --server-port must be a valid port number (1-65535)\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
config->server_port = (int)val;
|
||||||
} else if (strcmp(argv[i], "--bwlimit") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--bwlimit") == 0 && i + 1 < argc) {
|
||||||
char* end;
|
char* end;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
@@ -274,6 +337,11 @@ int main(int argc, char* argv[]) {
|
|||||||
config->backup = true;
|
config->backup = true;
|
||||||
} else if (strcmp(argv[i], "--backup-dir") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--backup-dir") == 0 && i + 1 < argc) {
|
||||||
config->backup_dir = str_dup(argv[++i]);
|
config->backup_dir = str_dup(argv[++i]);
|
||||||
|
if (!config->backup_dir) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "--stats") == 0) {
|
} else if (strcmp(argv[i], "--stats") == 0) {
|
||||||
config->stats = true;
|
config->stats = true;
|
||||||
} else if (strcmp(argv[i], "--max-depth") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--max-depth") == 0 && i + 1 < argc) {
|
||||||
@@ -316,6 +384,11 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (strcmp(argv[i], "--fastsync-server-path") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--fastsync-server-path") == 0 && i + 1 < argc) {
|
||||||
free(config->fastsync_server_path);
|
free(config->fastsync_server_path);
|
||||||
config->fastsync_server_path = str_dup(argv[++i]);
|
config->fastsync_server_path = str_dup(argv[++i]);
|
||||||
|
if (!config->fastsync_server_path) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
|
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
|
||||||
set_log_level(LOG_LEVEL_DEBUG);
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
} else if (argv[i][0] == '-') {
|
} else if (argv[i][0] == '-') {
|
||||||
@@ -340,6 +413,11 @@ int main(int argc, char* argv[]) {
|
|||||||
free(config->receive_root_directory);
|
free(config->receive_root_directory);
|
||||||
config->send_directory = str_dup(argv[positional_args[0]]);
|
config->send_directory = str_dup(argv[positional_args[0]]);
|
||||||
config->receive_root_directory = str_dup(argv[positional_args[1]]);
|
config->receive_root_directory = str_dup(argv[positional_args[1]]);
|
||||||
|
if (!config->send_directory || !config->receive_root_directory) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for directory paths\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
config->save_to_disk = true;
|
config->save_to_disk = true;
|
||||||
|
|
||||||
config_parse_ssh_dest(config);
|
config_parse_ssh_dest(config);
|
||||||
@@ -349,10 +427,22 @@ int main(int argc, char* argv[]) {
|
|||||||
exit_code = 1;
|
exit_code = 1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
} else {
|
} else {
|
||||||
if (!config->send_directory && env_source)
|
if (!config->send_directory && env_source) {
|
||||||
config->send_directory = str_dup((char*)env_source);
|
config->send_directory = str_dup((char*)env_source);
|
||||||
if (!config->receive_root_directory && env_dest)
|
if (!config->send_directory) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for source directory\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!config->receive_root_directory && env_dest) {
|
||||||
config->receive_root_directory = str_dup((char*)env_dest);
|
config->receive_root_directory = str_dup((char*)env_dest);
|
||||||
|
if (!config->receive_root_directory) {
|
||||||
|
fprintf(stderr, "Error: memory allocation failed for destination directory\n");
|
||||||
|
exit_code = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->send_directory || !config->receive_root_directory) {
|
if (!config->send_directory || !config->receive_root_directory) {
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ int receive_files(Config* config, int fd) {
|
|||||||
}
|
}
|
||||||
char* full_path = path_cat(config->receive_root_directory, check_path);
|
char* full_path = path_cat(config->receive_root_directory, check_path);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
bool has_old = full_path && lstat(full_path, &st) == 0;
|
bool has_old = full_path && stat(full_path, &st) == 0;
|
||||||
bool match = has_old && (unsigned long long)st.st_size == check_size &&
|
bool match = has_old && (unsigned long long)st.st_size == check_size &&
|
||||||
(long long)st.st_mtime == check_mtime;
|
(long long)st.st_mtime == check_mtime;
|
||||||
if (match)
|
if (match)
|
||||||
|
|||||||
@@ -78,21 +78,14 @@ Data* data_decompress(Data* compressed_data) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZSTD_CONTENTSIZE_UNKNOWN (~2^64) can cause massive allocation;
|
|
||||||
// fall back to a conservative estimate (3x compressed size) when unknown.
|
|
||||||
if (dst_size == ZSTD_CONTENTSIZE_UNKNOWN) {
|
|
||||||
dst_size = compressed_data->size * 3;
|
|
||||||
if (dst_size < INITIAL_DECOMPRESS_BUF_SIZE)
|
|
||||||
dst_size = INITIAL_DECOMPRESS_BUF_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZSTD_DCtx* dctx = ZSTD_createDCtx();
|
ZSTD_DCtx* dctx = ZSTD_createDCtx();
|
||||||
if (!dctx) {
|
if (!dctx) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create ZSTD decompression context");
|
log_message(LOG_LEVEL_ERROR, "Failed to create ZSTD decompression context");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t buf_size = (dst_size > 0) ? (size_t)dst_size : INITIAL_DECOMPRESS_BUF_SIZE;
|
size_t buf_size =
|
||||||
|
(!ZSTD_isError(dst_size) && dst_size > 0) ? (size_t)dst_size : INITIAL_DECOMPRESS_BUF_SIZE;
|
||||||
Data* uncompressed_data = data_create_empty(buf_size);
|
Data* uncompressed_data = data_create_empty(buf_size);
|
||||||
if (!uncompressed_data) {
|
if (!uncompressed_data) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to allocate decompression buffer");
|
log_message(LOG_LEVEL_ERROR, "Failed to allocate decompression buffer");
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "delta.h"
|
#include "delta.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -8,10 +7,6 @@
|
|||||||
#define XXH_IMPLEMENTATION
|
#define XXH_IMPLEMENTATION
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
/* Maximum number of blocks/instructions allowed from the wire to prevent OOM */
|
|
||||||
#define MAX_DELTA_BLOCKS (1024U * 1024U) /* 1M signature blocks */
|
|
||||||
#define MAX_DELTA_INSTRUCTIONS (1024U * 1024U) /* 1M delta instructions */
|
|
||||||
|
|
||||||
uint32_t delta_adler32(const void* data, uint32_t len) {
|
uint32_t delta_adler32(const void* data, uint32_t len) {
|
||||||
const uint8_t* p = (const uint8_t*)data;
|
const uint8_t* p = (const uint8_t*)data;
|
||||||
uint32_t s1 = 1;
|
uint32_t s1 = 1;
|
||||||
@@ -106,14 +101,6 @@ DeltaSignature* delta_signature_deserialize(const Data* data) {
|
|||||||
memcpy(&sig->block_count, buf + pos, sizeof(uint32_t));
|
memcpy(&sig->block_count, buf + pos, sizeof(uint32_t));
|
||||||
pos += sizeof(uint32_t);
|
pos += sizeof(uint32_t);
|
||||||
|
|
||||||
// Reject unreasonably large block counts to prevent OOM
|
|
||||||
if (sig->block_count > MAX_DELTA_BLOCKS) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Delta signature block count %u exceeds maximum %u",
|
|
||||||
sig->block_count, MAX_DELTA_BLOCKS);
|
|
||||||
free(sig);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t expected = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t) +
|
uint64_t expected = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t) +
|
||||||
(uint64_t)sig->block_count * (sizeof(uint32_t) + sizeof(uint32_t));
|
(uint64_t)sig->block_count * (sizeof(uint32_t) + sizeof(uint32_t));
|
||||||
if (data->size < expected) {
|
if (data->size < expected) {
|
||||||
@@ -353,14 +340,6 @@ Delta* delta_deserialize(const Data* data) {
|
|||||||
memcpy(&delta->instruction_count, buf + pos, sizeof(uint32_t));
|
memcpy(&delta->instruction_count, buf + pos, sizeof(uint32_t));
|
||||||
pos += sizeof(uint32_t);
|
pos += sizeof(uint32_t);
|
||||||
|
|
||||||
// Reject unreasonably large instruction counts to prevent OOM
|
|
||||||
if (delta->instruction_count > MAX_DELTA_INSTRUCTIONS) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Delta instruction count %u exceeds maximum %u",
|
|
||||||
delta->instruction_count, MAX_DELTA_INSTRUCTIONS);
|
|
||||||
free(delta);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
delta->instructions = malloc(delta->instruction_count * sizeof(DeltaInstruction));
|
delta->instructions = malloc(delta->instruction_count * sizeof(DeltaInstruction));
|
||||||
if (!delta->instructions) {
|
if (!delta->instructions) {
|
||||||
free(delta);
|
free(delta);
|
||||||
|
|||||||
+3
-58
@@ -134,64 +134,9 @@ bool file_save_to_disk(const char* root_directory, File* file, const Config* con
|
|||||||
log_message(LOG_LEVEL_ERROR, "Path traversal detected in file path: %s", file->path);
|
log_message(LOG_LEVEL_ERROR, "Path traversal detected in file path: %s", file->path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
char* disk_path = path_cat((char*)root_directory, file->path);
|
||||||
// Resolve the destination root to its real path, preventing symlink-based escapes.
|
if (disk_path == NULL)
|
||||||
// If the root does not yet exist, try to create it so realpath can succeed.
|
|
||||||
char* resolved_root = realpath(root_directory, NULL);
|
|
||||||
if (resolved_root == NULL) {
|
|
||||||
if (mkdir_r(root_directory)) {
|
|
||||||
resolved_root = realpath(root_directory, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resolved_root == NULL) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to resolve destination root: %s", root_directory);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
char* disk_path = path_cat(resolved_root, file->path);
|
|
||||||
if (disk_path == NULL) {
|
|
||||||
free(resolved_root);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the target directory exists so the parent can be resolved for path safety.
|
|
||||||
char* dir_dup = str_dup(disk_path);
|
|
||||||
if (!dir_dup) {
|
|
||||||
free(resolved_root);
|
|
||||||
free(disk_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char* dir_str = dirname(dir_dup);
|
|
||||||
// Create the directory if needed (no-op if it already exists) so realpath can resolve it.
|
|
||||||
if (!mkdir_r(dir_str)) {
|
|
||||||
free(dir_dup);
|
|
||||||
free(resolved_root);
|
|
||||||
free(disk_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char* resolved_dir = realpath(dir_str, NULL);
|
|
||||||
free(dir_dup);
|
|
||||||
if (resolved_dir == NULL) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to resolve directory for: %s", disk_path);
|
|
||||||
free(resolved_root);
|
|
||||||
free(disk_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the resolved directory is inside the resolved root.
|
|
||||||
// Both are canonical absolute paths — this prevents symlink-based escapes.
|
|
||||||
size_t root_len = strlen(resolved_root);
|
|
||||||
if (strncmp(resolved_dir, resolved_root, root_len) != 0 ||
|
|
||||||
(resolved_dir[root_len] != '\0' && resolved_dir[root_len] != '/')) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Path escape detected: %s is outside %s", disk_path, root_directory);
|
|
||||||
free(resolved_dir);
|
|
||||||
free(resolved_root);
|
|
||||||
free(disk_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
free(resolved_dir);
|
|
||||||
free(resolved_root);
|
|
||||||
|
|
||||||
bool ok = to_disk(disk_path, file->data->data, file->data->size);
|
bool ok = to_disk(disk_path, file->data->data, file->data->size);
|
||||||
if (ok)
|
if (ok)
|
||||||
file_restore_metadata(disk_path, file->metadata);
|
file_restore_metadata(disk_path, file->metadata);
|
||||||
@@ -396,7 +341,7 @@ File* receive_incremental_check(int fd, const Config* config, bool* skipped) {
|
|||||||
|
|
||||||
char* full_path = path_cat(config->receive_root_directory, check_path);
|
char* full_path = path_cat(config->receive_root_directory, check_path);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
bool has_old_file = (full_path && lstat(full_path, &st) == 0);
|
bool has_old_file = (full_path && stat(full_path, &st) == 0);
|
||||||
unsigned long long old_size = has_old_file ? (unsigned long long)st.st_size : 0;
|
unsigned long long old_size = has_old_file ? (unsigned long long)st.st_size : 0;
|
||||||
|
|
||||||
bool match = has_old_file && (unsigned long long)st.st_size == check_size &&
|
bool match = has_old_file && (unsigned long long)st.st_size == check_size &&
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define MAX_DATA_SIZE (100ULL * 1024 * 1024) /* 100 MB max per data message */
|
#define MAX_DATA_SIZE (256ULL * 1024 * 1024) /* 256 MB max per message */
|
||||||
#define RECEIVE_TIMEOUT_SEC 60 /* 60 second per-message timeout */
|
#define RECEIVE_TIMEOUT_SEC 60 /* 60 second per-message timeout */
|
||||||
#define MAX_CONNECTION_MEMORY (1024ULL * 1024 * 1024) /* 1 GB total per connection */
|
#define MAX_CONNECTION_MEMORY (1024ULL * 1024 * 1024) /* 1 GB total per connection */
|
||||||
|
|
||||||
@@ -186,9 +186,9 @@ char* receive_str(int file_descriptor) {
|
|||||||
size_t size;
|
size_t size;
|
||||||
if (!receive_n_data(file_descriptor, &size, sizeof(size_t)))
|
if (!receive_n_data(file_descriptor, &size, sizeof(size_t)))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (size > MAX_STRING_SIZE) {
|
if (size > MAX_DATA_SIZE) {
|
||||||
log_message(LOG_LEVEL_ERROR, "String size %zu exceeds maximum %llu", size,
|
log_message(LOG_LEVEL_ERROR, "String size %zu exceeds maximum %llu", size,
|
||||||
(unsigned long long)MAX_STRING_SIZE);
|
(unsigned long long)MAX_DATA_SIZE);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
char* data = (char*)malloc(size + 1);
|
char* data = (char*)malloc(size + 1);
|
||||||
|
|||||||
@@ -5,11 +5,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/* Maximum allowed string size for receive_str (64 KB) */
|
/* Maximum allowed string size for receive_str (10 MB) */
|
||||||
#define MAX_STRING_SIZE (64 * 1024)
|
#define MAX_STRING_SIZE (10 * 1024 * 1024)
|
||||||
|
|
||||||
/* Maximum allowed data payload size for receive_data (100 MB) */
|
|
||||||
#define MAX_DATA_PAYLOAD_SIZE (100ULL * 1024 * 1024)
|
|
||||||
|
|
||||||
typedef struct ssl_st SSL;
|
typedef struct ssl_st SSL;
|
||||||
|
|
||||||
|
|||||||
+13
-30
@@ -79,38 +79,25 @@ static SSL_CTX* create_ssl_ctx(bool is_server, const char* cert, const char* key
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SSL* wrap_fd_with_ssl(int fd, SSL_CTX* ctx, bool is_server, const char* hostname) {
|
static SSL* wrap_fd_with_ssl(int fd, SSL_CTX* ctx, bool is_server) {
|
||||||
SSL* ssl = SSL_new(ctx);
|
SSL* ssl = SSL_new(ctx);
|
||||||
if (!ssl) {
|
if (!ssl) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to create SSL object");
|
log_message(LOG_LEVEL_ERROR, "Failed to create SSL object");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
SSL_set_fd(ssl, fd);
|
SSL_set_fd(ssl, fd);
|
||||||
|
|
||||||
// Enable hostname verification for client connections when a hostname is provided.
|
|
||||||
// Must be done before SSL_connect to take effect during the handshake.
|
|
||||||
if (!is_server && hostname) {
|
|
||||||
SSL_set1_host(ssl, hostname);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry SSL_accept/SSL_connect on WANT_READ/WANT_WRITE (non-blocking handshake)
|
|
||||||
int ret;
|
int ret;
|
||||||
do {
|
if (is_server)
|
||||||
if (is_server)
|
ret = SSL_accept(ssl);
|
||||||
ret = SSL_accept(ssl);
|
else
|
||||||
else
|
ret = SSL_connect(ssl);
|
||||||
ret = SSL_connect(ssl);
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
int ssl_err = SSL_get_error(ssl, ret);
|
log_message(LOG_LEVEL_ERROR, "SSL %s failed", is_server ? "accept" : "connect");
|
||||||
if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE)
|
log_ssl_errors();
|
||||||
continue;
|
SSL_free(ssl);
|
||||||
log_message(LOG_LEVEL_ERROR, "SSL %s failed", is_server ? "accept" : "connect");
|
return NULL;
|
||||||
log_ssl_errors();
|
}
|
||||||
SSL_free(ssl);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} while (ret <= 0);
|
|
||||||
return ssl;
|
return ssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +117,7 @@ struct tls_child_ctx {
|
|||||||
|
|
||||||
static void tls_child_fn(int fd, void* arg) {
|
static void tls_child_fn(int fd, void* arg) {
|
||||||
struct tls_child_ctx* ctx = (struct tls_child_ctx*)arg;
|
struct tls_child_ctx* ctx = (struct tls_child_ctx*)arg;
|
||||||
SSL* ssl = wrap_fd_with_ssl(fd, ctx->ssl_ctx, true, NULL);
|
SSL* ssl = wrap_fd_with_ssl(fd, ctx->ssl_ctx, true);
|
||||||
if (!ssl)
|
if (!ssl)
|
||||||
return;
|
return;
|
||||||
io_set_ssl(ssl);
|
io_set_ssl(ssl);
|
||||||
@@ -164,16 +151,12 @@ bool client_connect_tls(Client* client, char* host, int port, const char* cert_p
|
|||||||
return false;
|
return false;
|
||||||
client->ssl_ctx = ctx;
|
client->ssl_ctx = ctx;
|
||||||
|
|
||||||
// Pass the server hostname for TLS hostname verification (SSL_set1_host
|
SSL* ssl = wrap_fd_with_ssl(client->file_descriptor, ctx, false);
|
||||||
// is called inside wrap_fd_with_ssl before the handshake when ca_path is set).
|
|
||||||
const char* verify_host = ca_path ? host : NULL;
|
|
||||||
SSL* ssl = wrap_fd_with_ssl(client->file_descriptor, ctx, false, verify_host);
|
|
||||||
if (!ssl) {
|
if (!ssl) {
|
||||||
SSL_CTX_free(ctx);
|
SSL_CTX_free(ctx);
|
||||||
client->ssl_ctx = NULL;
|
client->ssl_ctx = NULL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->ssl = ssl;
|
client->ssl = ssl;
|
||||||
io_set_ssl(ssl);
|
io_set_ssl(ssl);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+1
-7
@@ -126,13 +126,7 @@ static void delete_extras_walk(const char* abs_path, const char* rel_path, Array
|
|||||||
char* child_abs = path_cat((char*)abs_path, entry->d_name);
|
char* child_abs = path_cat((char*)abs_path, entry->d_name);
|
||||||
char* child_rel = path_cat((char*)rel_path, entry->d_name);
|
char* child_rel = path_cat((char*)rel_path, entry->d_name);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(child_abs, &st) != 0) {
|
if (stat(child_abs, &st) != 0) {
|
||||||
free(child_abs);
|
|
||||||
free(child_rel);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Skip symlinks to prevent following them outside the destination tree
|
|
||||||
if (S_ISLNK(st.st_mode)) {
|
|
||||||
free(child_abs);
|
free(child_abs);
|
||||||
free(child_rel);
|
free(child_rel);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -37,23 +37,15 @@ def _generate_certs(cert_dir):
|
|||||||
], check=True, capture_output=True)
|
], check=True, capture_output=True)
|
||||||
|
|
||||||
# Server key + CSR + cert (signed by CA)
|
# Server key + CSR + cert (signed by CA)
|
||||||
# Use a config file to include IP SAN 127.0.0.1 so hostname verification passes
|
|
||||||
san_config = os.path.join(cert_dir, "server_san.conf")
|
|
||||||
with open(san_config, "w") as f:
|
|
||||||
f.write("[req]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n\n")
|
|
||||||
f.write("[req_distinguished_name]\nCN = localhost\n\n")
|
|
||||||
f.write("[v3_req]\nsubjectAltName = @alt_names\n\n")
|
|
||||||
f.write("[alt_names]\nDNS.1 = localhost\nIP.1 = 127.0.0.1\n")
|
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
"openssl", "req", "-newkey", "rsa:2048", "-nodes",
|
"openssl", "req", "-newkey", "rsa:2048", "-nodes",
|
||||||
"-keyout", server_key, "-out", os.path.join(cert_dir, "server.csr"),
|
"-keyout", server_key, "-out", os.path.join(cert_dir, "server.csr"),
|
||||||
"-subj", "/CN=localhost", "-config", san_config,
|
"-subj", "/CN=localhost",
|
||||||
], check=True, capture_output=True)
|
], check=True, capture_output=True)
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
"openssl", "x509", "-req", "-in", os.path.join(cert_dir, "server.csr"),
|
"openssl", "x509", "-req", "-in", os.path.join(cert_dir, "server.csr"),
|
||||||
"-CA", ca_cert, "-CAkey", ca_key, "-CAcreateserial",
|
"-CA", ca_cert, "-CAkey", ca_key, "-CAcreateserial",
|
||||||
"-out", server_cert, "-days", "1",
|
"-out", server_cert, "-days", "1",
|
||||||
"-extfile", san_config, "-extensions", "v3_req",
|
|
||||||
], check=True, capture_output=True)
|
], check=True, capture_output=True)
|
||||||
|
|
||||||
# Client key + CSR + cert (signed by CA)
|
# Client key + CSR + cert (signed by CA)
|
||||||
|
|||||||
Reference in New Issue
Block a user