Store admin keys atomically and reject invalid transfer context
This commit is contained in:
1 parent
e65ceeafdc
commit
76527de44f
2 files changed
+62
-14
No files matched your search
+20
-5
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user