From 99608de811eff754a9ce0ebfe7d4fe3fa478fd2b Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 14:01:37 +0200 Subject: [PATCH] Reject failed topology writes and malformed stored signatures --- src/routing_layer/topo_node_sqlite.c | 38 +++++++++++++++++++--------- tests/test_node_snapshot.c | 16 ++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 6839b270..2a726472 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -778,7 +778,10 @@ int topo_node_sqlite_member_get_join(sqlite3* db, const char* channel_id, uint64 int rc = -1; if (sqlite3_step(stmt) == SQLITE_ROW) { const uint8_t* sig = (const uint8_t*)sqlite3_column_blob(stmt, 0); - if (sig) { memcpy(join_sig_out, sig, PEERS_JOIN_SIG_SIZE); *join_ts_out = (uint64_t)sqlite3_column_int64(stmt, 1); rc = 0; } + if (sig && sqlite3_column_bytes(stmt, 0) == PEERS_JOIN_SIG_SIZE) { + memcpy(join_sig_out, sig, PEERS_JOIN_SIG_SIZE); *join_ts_out = (uint64_t)sqlite3_column_int64(stmt, 1); rc = 0; + } else DEBUG_WARN(DEBUG_CATEGORY_BGP, "invalid stored join signature channel=%s node=%016llx bytes=%d", + channel_id, (unsigned long long)node_id, sqlite3_column_bytes(stmt, 0)); } sqlite3_finalize(stmt); return rc; @@ -817,15 +820,18 @@ int topo_node_sqlite_member_in_channel(sqlite3* db, const char* channel_id, uint } int topo_node_sqlite_member_del(sqlite3* db, const char* channel_id, uint64_t node_id) { - if (!db || !channel_id) return -1; + if (!db || !channel_id) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member delete: invalid arguments"); return -1; } char peers_tbl[80]; peers_table_name(channel_id, peers_tbl, sizeof(peers_tbl)); char sql[256]; snprintf(sql, sizeof(sql), "DELETE FROM \"%s\" WHERE node_id=?", peers_tbl); sqlite3_stmt* stmt = NULL; - if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1; - sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); - sqlite3_step(stmt); sqlite3_finalize(stmt); - return 0; + int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(stmt, 1, (sqlite3_int64)node_id); + if (rc == SQLITE_OK) rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member delete: channel=%s node=%016llx rc=%d: %s", + channel_id, (unsigned long long)node_id, rc, sqlite3_errmsg(db)); + sqlite3_finalize(stmt); + return rc == SQLITE_DONE ? 0 : -1; } int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id, @@ -838,6 +844,12 @@ int topo_node_sqlite_channel_get(sqlite3* db, const char* channel_id, " FROM channels WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1; sqlite3_bind_text(stmt, 1, channel_id, -1, SQLITE_STATIC); if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_finalize(stmt); return -1; } + if ((x25519_pub && sqlite3_column_bytes(stmt, 2) != 32) || + (ed25519_pub && sqlite3_column_bytes(stmt, 3) != 32) || (signature && sqlite3_column_bytes(stmt, 4) != 64)) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid stored channel keys/signature channel=%s sizes=%d/%d/%d", channel_id, + sqlite3_column_bytes(stmt, 2), sqlite3_column_bytes(stmt, 3), sqlite3_column_bytes(stmt, 4)); + sqlite3_finalize(stmt); return -1; + } if (name_out && name_sz > 0) { const unsigned char* txt = sqlite3_column_text(stmt, 0); if (txt) snprintf(name_out, name_sz, "%s", txt); else name_out[0] = '\0'; @@ -899,14 +911,16 @@ int topo_node_sqlite_channel_set_privs(sqlite3* db, const char* channel_id, } int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online) { - if (!db) return -1; + if (!db) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node online: database is NULL"); return -1; } sqlite3_stmt* stmt = NULL; - if (sqlite3_prepare_v2(db, "UPDATE nodes SET online=? WHERE node_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1; - sqlite3_bind_int(stmt, 1, online ? 1 : 0); - sqlite3_bind_int64(stmt, 2, (sqlite3_int64)node_id); - sqlite3_step(stmt); + int rc = sqlite3_prepare_v2(db, "UPDATE nodes SET online=? WHERE node_id=?", -1, &stmt, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int(stmt, 1, online ? 1 : 0); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(stmt, 2, (sqlite3_int64)node_id); + if (rc == SQLITE_OK) rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node online: node=%016llx online=%d rc=%d: %s", + (unsigned long long)node_id, online, rc, sqlite3_errmsg(db)); sqlite3_finalize(stmt); - return 0; + return rc == SQLITE_DONE ? 0 : -1; } int topo_node_sqlite_node_get_online(sqlite3* db, uint64_t node_id) { diff --git a/tests/test_node_snapshot.c b/tests/test_node_snapshot.c index f54878b3..654428d2 100644 --- a/tests/test_node_snapshot.c +++ b/tests/test_node_snapshot.c @@ -74,6 +74,7 @@ static void test_verified_write(void) { assert(sqlite3_exec(db, "DROP TRIGGER reject_node; CREATE TRIGGER reject_node BEFORE UPDATE ON nodes " "BEGIN SELECT RAISE(ABORT,'test update rejection'); END", NULL, NULL, NULL) == SQLITE_OK); assert(topo_node_sqlite_node_update_verified(db, 7, "rejected", key, key, 101, 1) < 0); + assert(topo_node_sqlite_node_set_online(db, 7, 0) < 0 && topo_node_sqlite_node_get_online(db, 7) == 1); sqlite3_stmt* st = NULL; assert(sqlite3_prepare_v2(db, "SELECT name,update_ts FROM nodes ORDER BY node_id", -1, &st, NULL) == SQLITE_OK); assert(sqlite3_step(st) == SQLITE_ROW && !strcmp((const char*)sqlite3_column_text(st, 0), "before")); @@ -83,6 +84,21 @@ static void test_verified_write(void) { assert(sqlite3_prepare_v2(db, "SELECT name,update_ts FROM nodes WHERE node_id=7", -1, &st, NULL) == SQLITE_OK); assert(sqlite3_step(st) == SQLITE_ROW && !strcmp((const char*)sqlite3_column_text(st, 0), "after")); assert(sqlite3_column_int64(st, 1) == 101); sqlite3_finalize(st); + uint8_t signature[64] = {0}; + assert(!topo_node_sqlite_channel_put(db, "123", "test", 7, key, NULL, key, NULL, signature)); + assert(!topo_node_sqlite_member_placeholder_put(db, "123", 7, key, key)); + assert(sqlite3_exec(db, "CREATE TRIGGER reject_member BEFORE DELETE ON peers_123 " + "BEGIN SELECT RAISE(ABORT,'test member deletion rejection'); END", NULL, NULL, NULL) == SQLITE_OK); + assert(topo_node_sqlite_member_del(db, "123", 7) < 0 && topo_node_sqlite_member_in_channel(db, "123", 7)); + assert(sqlite3_exec(db, "DROP TRIGGER reject_member; UPDATE peers_123 SET join_sig=X'01'; " + "UPDATE channels SET x25519_pubkey=X'01'", NULL, NULL, NULL) == SQLITE_OK); + uint64_t timestamp = 77; memset(signature, 0xa5, sizeof(signature)); + assert(topo_node_sqlite_member_get_join(db, "123", 7, signature, ×tamp) < 0); + assert(timestamp == 77 && signature[0] == 0xa5); + memset(key, 0xa5, sizeof(key)); + assert(topo_node_sqlite_channel_get(db, "123", NULL, 0, NULL, key, NULL, NULL) < 0 && key[0] == 0xa5); + assert(!topo_node_sqlite_member_del(db, "123", 7) && !topo_node_sqlite_member_in_channel(db, "123", 7)); + assert(!topo_node_sqlite_node_set_online(db, 7, 0) && !topo_node_sqlite_node_get_online(db, 7)); assert(sqlite3_exec(db, "DROP TABLE nodes", NULL, NULL, NULL) == SQLITE_OK); assert(topo_node_sqlite_node_update_verified(db, 7, "no table", key, key, 102, 1) < 0); sqlite3_close(db);