Store admin keys atomically and reject invalid transfer context

This commit is contained in:
evgeny committed 2026-10-26 20:44:42 +01:00
1 parent e65ceeafdc
commit 76527de44f
2 files changed
+62 -14

No files matched your search

+20 -5
View File
@@ -45,12 +45,23 @@ static int ed25519_pub_from_priv(const uint8_t priv[32], uint8_t pub[32]) {
static void admin_key_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!entry) return;
if (!conn || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF + 8 + 32 + 32) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — invalid delivery len=%u", CA_ID, entry->len);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
struct UTUN_INSTANCE* inst = conn->instance;
if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return; }
if (!inst) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — no instance", CA_ID);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized || !cc->db) { queue_dgram_free(entry); queue_entry_free(entry); return; }
if (!cc || !cc->initialized || !cc->db) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — not initialized", CA_ID);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
if (!sqlite3_get_autocommit(cc->db)) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — import rejected inside external transaction", CA_ID);
queue_dgram_free(entry); queue_entry_free(entry); return;
}
const uint8_t* d = entry->dgram;
uint64_t from_node;
@@ -84,6 +95,10 @@ static void admin_key_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (topo_node_sqlite_channel_set_privs(cc->db, ch_id, x_priv, ed_priv) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — set_privs FAILED ch=%s from 0x%016llx",
CA_ID, ch_id, (unsigned long long)from_node);
if (!sqlite3_get_autocommit(cc->db)) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: recv — rollback left transaction open ch=%s; restart required", CA_ID, ch_id);
inst->running = 0;
}
queue_dgram_free(entry); queue_entry_free(entry); return;
}
@@ -120,8 +135,8 @@ void chat_admin_destroy(struct UTUN_INSTANCE* inst) {
/* ─── передача прав (вызывается из uasync-потока) ─── */
void chat_core_transfer_admin(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id) {
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized || !inst || !cc->db) {
struct chat_core_ctx* cc = inst ? CC(inst) : NULL;
if (!cc || !cc->initialized || !cc->db) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — not initialized", CA_ID);
return;
}
@@ -151,7 +166,7 @@ void chat_core_transfer_admin(struct UTUN_INSTANCE* inst, const char* ch_id, uin
struct ll_entry* entry = queue_entry_new(0);
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — queue_entry_new failed", CA_ID); return; }
entry->dgram = u_malloc(1 + sizeof(payload));
if (!entry->dgram) { queue_entry_free(entry); return; }
if (!entry->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: transfer — payload allocation failed", CA_ID); queue_entry_free(entry); return; }
entry->dgram[0] = ETCP_RT_ID_ADMIN_KEY;
memcpy(entry->dgram + 1, payload, sizeof(payload));
entry->len = 1 + (uint16_t)sizeof(payload);
+42 -9
View File
@@ -953,18 +953,51 @@ int topo_node_sqlite_channel_get_privs(sqlite3* db, const char* channel_id,
return 0;
}
/* Импорт пары: savepoint охватывает UPDATE и его завершение, внешний commit остаётся вызывающему. */
int topo_node_sqlite_channel_set_privs(sqlite3* db, const char* channel_id,
const uint8_t* x25519_priv, const uint8_t* ed25519_priv) {
if (!db || !channel_id || !x25519_priv || !ed25519_priv) return -1;
if (!db || !channel_id || !channel_id[0] || !x25519_priv || !ed25519_priv) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: invalid arguments"); return -1;
}
int own = sqlite3_get_autocommit(db);
int rc = sqlite3_exec(db, "SAVEPOINT channel_private_keys", NULL, NULL, NULL);
if (rc != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: channel=%s stage=savepoint rc=%d: %s",
channel_id, rc, sqlite3_errmsg(db)); return -1;
}
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db,
"UPDATE channels SET x25519_privkey=?, ed25519_privkey=? WHERE channel_id=?", -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_blob(stmt, 1, x25519_priv, 32, SQLITE_STATIC);
sqlite3_bind_blob(stmt, 2, ed25519_priv, 32, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, channel_id, -1, SQLITE_STATIC);
int rc = sqlite3_step(stmt) == SQLITE_DONE ? 0 : -1;
sqlite3_finalize(stmt);
return rc;
const char* stage = "prepare";
rc = sqlite3_prepare_v2(db, "UPDATE channels SET x25519_privkey=?, ed25519_privkey=? WHERE channel_id=?", -1, &stmt, NULL);
if (rc != SQLITE_OK) goto fail;
stage = "bind x25519"; rc = sqlite3_bind_blob(stmt, 1, x25519_priv, 32, SQLITE_STATIC);
if (rc != SQLITE_OK) goto fail;
stage = "bind ed25519"; rc = sqlite3_bind_blob(stmt, 2, ed25519_priv, 32, SQLITE_STATIC);
if (rc != SQLITE_OK) goto fail;
stage = "bind channel"; rc = sqlite3_bind_text(stmt, 3, channel_id, -1, SQLITE_STATIC);
if (rc != SQLITE_OK) goto fail;
stage = "update"; rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) goto fail;
int changed = sqlite3_changes(db);
stage = "finalize"; rc = sqlite3_finalize(stmt); stmt = NULL;
if (rc != SQLITE_OK) goto fail;
if (changed != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: channel=%s changed=%d expected=1", channel_id, changed);
stage = "row count"; rc = SQLITE_NOTFOUND; goto fail;
}
stage = "release"; rc = sqlite3_exec(db, "RELEASE channel_private_keys", NULL, NULL, NULL);
if (rc != SQLITE_OK) goto fail;
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "channel private keys stored: channel=%s committed=%d", channel_id, own);
return 0;
fail:
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: channel=%s stage=%s rc=%d: %s",
channel_id, stage, rc, sqlite3_errmsg(db));
int final_rc = sqlite3_finalize(stmt);
if (final_rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_BGP,
"channel private keys: cleanup finalize channel=%s rc=%d: %s", channel_id, final_rc, sqlite3_errmsg(db));
if (!sqlite3_get_autocommit(db) &&
sqlite3_exec(db, "ROLLBACK TO channel_private_keys; RELEASE channel_private_keys", NULL, NULL, NULL) != SQLITE_OK)
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "channel private keys: rollback failed channel=%s: %s", channel_id, sqlite3_errmsg(db));
return -1;
}
int topo_node_sqlite_node_set_online(sqlite3* db, uint64_t node_id, int online) {