Fix integration issues: ssl_ctx init + test API updates #89
@@ -4,6 +4,7 @@
|
|||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -247,10 +248,20 @@ Config* config_receive(int file_descriptor) {
|
|||||||
config->follow_symlinks = false;
|
config->follow_symlinks = false;
|
||||||
config->partial = false;
|
config->partial = false;
|
||||||
|
|
||||||
|
#define MAX_PATTERN_COUNT 10000
|
||||||
|
|
||||||
// Receive exclude patterns
|
// Receive exclude patterns
|
||||||
int ec;
|
int ec;
|
||||||
if (!receive_int(file_descriptor, &ec))
|
if (!receive_int(file_descriptor, &ec))
|
||||||
goto error;
|
goto error;
|
||||||
|
if (ec > MAX_PATTERN_COUNT) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Exclude pattern count %d exceeds maximum %d", ec, MAX_PATTERN_COUNT);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if ((size_t)ec > SIZE_MAX / sizeof(char*)) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Exclude pattern count %d would cause integer overflow", ec);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
config->exclude_count = ec;
|
config->exclude_count = ec;
|
||||||
if (ec > 0) {
|
if (ec > 0) {
|
||||||
config->exclude_patterns = malloc((size_t)ec * sizeof(char*));
|
config->exclude_patterns = malloc((size_t)ec * sizeof(char*));
|
||||||
@@ -275,6 +286,14 @@ Config* config_receive(int file_descriptor) {
|
|||||||
int ic;
|
int ic;
|
||||||
if (!receive_int(file_descriptor, &ic))
|
if (!receive_int(file_descriptor, &ic))
|
||||||
goto error;
|
goto error;
|
||||||
|
if (ic > MAX_PATTERN_COUNT) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Include pattern count %d exceeds maximum %d", ic, MAX_PATTERN_COUNT);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if ((size_t)ic > SIZE_MAX / sizeof(char*)) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Include pattern count %d would cause integer overflow", ic);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
config->include_count = ic;
|
config->include_count = ic;
|
||||||
if (ic > 0) {
|
if (ic > 0) {
|
||||||
config->include_patterns = malloc((size_t)ic * sizeof(char*));
|
config->include_patterns = malloc((size_t)ic * sizeof(char*));
|
||||||
|
|||||||
+30
-1
@@ -126,7 +126,11 @@ static bool file_send_streaming(File* file, int file_descriptor) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[STREAM_CHUNK_SIZE];
|
char* buf = malloc(STREAM_CHUNK_SIZE);
|
||||||
|
if (!buf) {
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
unsigned long long remaining = total_size;
|
unsigned long long remaining = total_size;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
size_t to_read = (size_t)((remaining < STREAM_CHUNK_SIZE) ? remaining : STREAM_CHUNK_SIZE);
|
size_t to_read = (size_t)((remaining < STREAM_CHUNK_SIZE) ? remaining : STREAM_CHUNK_SIZE);
|
||||||
@@ -135,15 +139,18 @@ static bool file_send_streaming(File* file, int file_descriptor) {
|
|||||||
if (ferror(fp)) {
|
if (ferror(fp)) {
|
||||||
perror("Read error during streaming");
|
perror("Read error during streaming");
|
||||||
}
|
}
|
||||||
|
free(buf);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!send_n_data(file_descriptor, buf, nread)) {
|
if (!send_n_data(file_descriptor, buf, nread)) {
|
||||||
|
free(buf);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
remaining -= (unsigned long long)nread;
|
remaining -= (unsigned long long)nread;
|
||||||
}
|
}
|
||||||
|
free(buf);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -194,9 +201,19 @@ bool file_send_single_calls(File* file, int file_descriptor, bool use_metadata,
|
|||||||
|
|
||||||
bool file_save_to_disk(const char* root_directory, File* file) {
|
bool file_save_to_disk(const char* root_directory, File* file) {
|
||||||
if (file->type == FILE_TYPE_SYMLINK && file->link_target) {
|
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) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Path traversal blocked in symlink target: %s", file->link_target);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
char* disk_path = path_cat((char*)root_directory, file->path);
|
char* disk_path = path_cat((char*)root_directory, file->path);
|
||||||
if (disk_path == NULL)
|
if (disk_path == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
if (strstr(disk_path, "..") != NULL) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Path traversal blocked: %s", disk_path);
|
||||||
|
free(disk_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
unlink(disk_path);
|
unlink(disk_path);
|
||||||
bool ok = (symlink(file->link_target, disk_path) == 0);
|
bool ok = (symlink(file->link_target, disk_path) == 0);
|
||||||
if (ok && file->metadata)
|
if (ok && file->metadata)
|
||||||
@@ -208,6 +225,11 @@ bool file_save_to_disk(const char* root_directory, File* file) {
|
|||||||
char* disk_path = path_cat((char*)root_directory, file->path);
|
char* disk_path = path_cat((char*)root_directory, file->path);
|
||||||
if (disk_path == NULL)
|
if (disk_path == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
if (strstr(disk_path, "..") != NULL) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Path traversal blocked: %s", disk_path);
|
||||||
|
free(disk_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
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);
|
||||||
@@ -423,6 +445,13 @@ 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);
|
||||||
|
if (full_path && strstr(full_path, "..") != NULL) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Path traversal blocked: %s", full_path);
|
||||||
|
free(full_path);
|
||||||
|
free(check_path);
|
||||||
|
send_status(fd, STATUS_ERROR);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
struct stat st;
|
struct stat st;
|
||||||
bool has_old_file = (full_path && stat(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;
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
|||||||
void file_restore_metadata(const char* path, FileMetadata* metadata) {
|
void file_restore_metadata(const char* path, FileMetadata* metadata) {
|
||||||
if (metadata == NULL)
|
if (metadata == NULL)
|
||||||
return;
|
return;
|
||||||
if (chmod(path, metadata->mode & 07777) != 0)
|
if (chmod(path, metadata->mode & 07777 & ~(S_ISUID | S_ISGID)) != 0)
|
||||||
log_message(LOG_LEVEL_WARNING, "Failed to chmod %s: %s", path, strerror(errno));
|
log_message(LOG_LEVEL_WARNING, "Failed to chmod %s: %s", path, strerror(errno));
|
||||||
if (chown(path, metadata->uid, metadata->gid) != 0)
|
if (chown(path, metadata->uid, metadata->gid) != 0)
|
||||||
log_message(LOG_LEVEL_WARNING, "Failed to chown %s: %s", path, strerror(errno));
|
log_message(LOG_LEVEL_WARNING, "Failed to chown %s: %s", path, strerror(errno));
|
||||||
|
|||||||
@@ -197,10 +197,16 @@ bool send_data(int file_descriptor, const Data* data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MAX_DATA_SIZE (1024ULL * 1024 * 1024) // 1 GB
|
||||||
|
|
||||||
Data* receive_data(int file_descriptor) {
|
Data* receive_data(int file_descriptor) {
|
||||||
unsigned long long size = 0;
|
unsigned long long size = 0;
|
||||||
if (!receive_n_data(file_descriptor, &size, sizeof(unsigned long long)))
|
if (!receive_n_data(file_descriptor, &size, sizeof(unsigned long long)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (size > MAX_DATA_SIZE) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "receive_data: size %llu exceeds maximum", size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
void* data = malloc((size_t)size);
|
void* data = malloc((size_t)size);
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -161,8 +161,10 @@ static void accept_loop(Server* server, void (*child_fn)(int, void*), void* chil
|
|||||||
socklen_t client_len = sizeof(client_addr);
|
socklen_t client_len = sizeof(client_addr);
|
||||||
int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len);
|
int fd = accept(server->file_descriptor, (struct sockaddr*)&client_addr, &client_len);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR) {
|
||||||
break;
|
if (g_tcp_cleanup_requested) break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
perror("Could not accept the connection");
|
perror("Could not accept the connection");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,6 +168,14 @@ bool client_connect_tls(Client* client, char* host, int port, const char* cert_p
|
|||||||
client->ssl_ctx = NULL;
|
client->ssl_ctx = NULL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set SNI and enable hostname verification
|
||||||
|
SSL_set_tlsext_host_name(ssl, host);
|
||||||
|
X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
|
||||||
|
if (param) {
|
||||||
|
X509_VERIFY_PARAM_set1_host(param, host, 0);
|
||||||
|
}
|
||||||
|
|
||||||
client->ssl = ssl;
|
client->ssl = ssl;
|
||||||
io_set_ssl(ssl);
|
io_set_ssl(ssl);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user