From 15fafc42c919f75cc64493660cffa9085bed175b Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 22 Oct 2026 14:46:49 +0200 Subject: [PATCH] Use validated paths for media submission and backfill --- src/chat/chat_msg.c | 79 ++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 54 deletions(-) diff --git a/src/chat/chat_msg.c b/src/chat/chat_msg.c index 32853745..5e86f5f1 100644 --- a/src/chat/chat_msg.c +++ b/src/chat/chat_msg.c @@ -375,9 +375,14 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_ if (req) chat_event_submitted(inst, req->request_id, 0); return; } + char media_base[MEDIA_BASE_MAX], location[MEDIA_PATH_MAX], destination[MEDIA_PATH_MAX]; + if (media_index_get_base(cc->db, media_base) || media_index_location(req->media_dest, media_base, location) + || media_index_path(media_base, location, destination)) { + chat_event_submitted(inst, req->request_id, 0); return; + } for (struct media_submit_ctx* pending = cc->media_submissions; pending; pending = pending->next) { if ((!strcmp(pending->channel_id, req->channel_id) && pending->delete_pending) - || !strcmp(pending->media_dest, req->media_dest)) { + || !strcmp(pending->media_dest, destination)) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "media submit rejected: channel deleting/destination busy ch=%s path=%s", req->channel_id, req->media_dest); chat_event_submitted(inst, req->request_id, 0); return; @@ -419,9 +424,7 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_ } /* build media_base from db_path: dirname(db_path) e.g. /path/to/data */ - char media_base[MEDIA_BASE_MAX], location[MEDIA_PATH_MAX]; - if (media_index_get_base(cc->db, media_base) || media_index_location(req->media_dest, media_base, location) - || media_index_track_preparation(cc->db, req->channel_id, req->media_dest, media_base)) { + if (media_index_track_preparation(cc->db, req->channel_id, destination, media_base)) { chat_event_submitted(inst, req->request_id, 0); return; } @@ -437,7 +440,7 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_ memcpy(mctx->reply_hex, reply_hex, strlen(reply_hex) + 1); snprintf(mctx->channel_id, sizeof(mctx->channel_id), "%s", req->channel_id); snprintf(mctx->content_type, sizeof(mctx->content_type), "%s", req->content_type); - snprintf(mctx->media_dest, sizeof(mctx->media_dest), "%s", req->media_dest); + snprintf(mctx->media_dest, sizeof(mctx->media_dest), "%s", destination); mctx->timestamp = req->timestamp; mctx->duration_ms = req->duration_ms; mctx->width = req->width; @@ -448,7 +451,7 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_ } if (req->media_video) { - if (video_transcode_start(inst->media_async, inst->ua, req->media_src, req->media_dest, on_video_prepared, mctx) < 0) + if (video_transcode_start(inst->media_async, inst->ua, req->media_src, destination, on_video_prepared, mctx) < 0) on_video_prepared(mctx, -1, NULL); return; } @@ -457,7 +460,7 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_ inst->media_async, inst->ua, cc->db, cc->my_node_id, inst->my_ed25519_privkey, req->channel_id, - req->media_src, req->media_dest, req->media_copy ? 1 : 0, + req->media_src, destination, req->media_copy ? 1 : 0, media_base, on_media_registered, mctx); } @@ -910,16 +913,10 @@ static int md_start_download(struct UTUN_INSTANCE* inst, char final_name[256]; media_final_name(&result, content_type, base_filename, final_name, sizeof(final_name)); - char dest[1024], media_base[512]; - { - const char* last_slash = strrchr(cc->db_path, '/'); - if (last_slash) { - snprintf(media_base, sizeof(media_base), "%.*s", (int)(last_slash - cc->db_path), cc->db_path); - } else { - snprintf(media_base, sizeof(media_base), "%s", cc->db_path); - } - } - snprintf(dest, sizeof(dest), "%s/media/%s/%s", media_base, ch_id, final_name); + char dest[MEDIA_PATH_MAX], media_base[MEDIA_BASE_MAX], relative[MEDIA_PATH_MAX]; + int length = snprintf(relative, sizeof(relative), "media/%s/%s", ch_id, final_name); + if (length < 0 || length >= (int)sizeof(relative) || media_index_get_base(cc->db, media_base) + || media_index_path(media_base, relative, dest)) { media_index_result_free(&result); return -1; } struct md_done_ctx* ctx = u_calloc(1, sizeof(*ctx)); if (!ctx) { media_index_result_free(&result); return -1; } @@ -936,9 +933,13 @@ static int md_start_download(struct UTUN_INSTANCE* inst, DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: download start ch=%s ct=%s file=%s blocks=%d size=%lld dest=%s", CC_ID, ch_id, content_type ? content_type : "?", final_name, nb, (long long)fsize, dest); uint64_t gid = strtoull(ch_id, NULL, 10); - media_download_start(inst, gid, &result, dest, media_base, author_node_id, md_download_done_cb, ctx, md_download_progress_cb, ctx); + int started = media_download_start(inst, gid, &result, dest, media_base, author_node_id, + md_download_done_cb, ctx, md_download_progress_cb, ctx); media_index_result_free(&result); - return 0; + if (started < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media download rejected ch=%s path=%s", ch_id, dest); u_free(ctx); + } + return started; } static int md_auto_download(struct UTUN_INSTANCE* inst, @@ -1136,10 +1137,8 @@ void chat_media_backfill(struct UTUN_INSTANCE* inst) { struct chat_core_ctx* cc = CC(inst); if (!cc || !cc->initialized || !cc->db) return; - char media_base[512]; - const char* last_slash = strrchr(cc->db_path, '/'); - if (last_slash) snprintf(media_base, sizeof(media_base), "%.*s", (int)(last_slash - cc->db_path), cc->db_path); - else snprintf(media_base, sizeof(media_base), "%s", cc->db_path); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; int total = 0; sqlite3_stmt* cs = NULL; @@ -1198,37 +1197,9 @@ void chat_media_backfill(struct UTUN_INSTANCE* inst) { continue; } - int registered = 0; - for (int n = 0; n < result.num_blocks; n++) { - int exists = 0; - sqlite3_stmt* ex = NULL; - if (sqlite3_prepare_v2(cc->db, "SELECT 1 FROM media_files WHERE block_id=? LIMIT 1", -1, &ex, NULL) == SQLITE_OK) { - sqlite3_bind_blob(ex, 1, result.block_ids + n * 16, 16, SQLITE_STATIC); - exists = (sqlite3_step(ex) == SQLITE_ROW); - sqlite3_finalize(ex); - } - if (exists) continue; - - char rel[512]; - size_t bl = strlen(media_base); - if (strncmp(path, media_base, bl) == 0 && path[bl] == '/') snprintf(rel, sizeof(rel), "%s", path + bl + 1); - else snprintf(rel, sizeof(rel), "%s", path); - - int64_t chunk_size = (n == result.num_blocks - 1) ? result.file_size - (int64_t)n * result.block_size : result.block_size; - int64_t offset = (int64_t)n * result.block_size; - - if (media_index_register_downloaded(cc->db, result.media_id, result.block_ids + n * 16, - result.content_hash, ch_id, rel, cc->my_node_id, - result.file_size, chunk_size, n, offset) == 0) { - registered++; - } - } - - if (registered > 0) { - total += registered; - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: backfill registered %d blocks ch=%s file=%s", - CC_ID, registered, ch_id, final_name); - } + int registered = media_index_register_downloaded_file(cc->db, &result, cc->my_node_id, ch_id, path, media_base); + if (!registered) total += result.num_blocks; + else DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media backfill registration failed ch=%s file=%s", ch_id, path); u_free(body); media_index_result_free(&result);