From a1653d648b150659b3eeb2b54923512964765ecc Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 16:19:12 +0100 Subject: [PATCH] Keep unresolved invitation chains inactive and test interrupted sync and cycles --- src/chat/group_policy.c | 2 +- src/chat/member_sync.c | 13 ++++++++----- tests/test_group_policy.c | 11 +++++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/chat/group_policy.c b/src/chat/group_policy.c index bc4185024..838ff8e13 100644 --- a/src/chat/group_policy.c +++ b/src/chat/group_policy.c @@ -158,7 +158,7 @@ static int gp_load(struct UTUN_INSTANCE* inst, const char* ch, struct gp_member* int tree = member_sync_verify_signature(db, ch, &m); if (tree == MS_VERIFY_ERROR) goto done; p->valid = verified == 1 && tree == 1 && !sqlite3_column_int(st, 12) && memcmp(m.join_sig, (uint8_t[64]){0}, 64); - if (p->valid && m.adm_tags && m.adm_tags_sig) { + if (p->valid && m.adm_tags && m.adm_tags_sig && memcmp(m.adm_tags_sig, (uint8_t[64]){0}, 64)) { char role[MS_ADM_TAGS_MAX + 1]; if (!json_flat_get(m.adm_tags, "admin", role, sizeof(role))) { const char* last = role; diff --git a/src/chat/member_sync.c b/src/chat/member_sync.c index d0e4fb364..e808fdaee 100644 --- a/src/chat/member_sync.c +++ b/src/chat/member_sync.c @@ -1277,7 +1277,7 @@ static int member_finish(void* ctx, const char* ns) { if (sqlite3_column_bytes(st, 1) != 32 || sqlite3_column_bytes(st, 3) != 64) { rc = SQLITE_CORRUPT; break; } if (_sig_is_zero64(m.signature)) continue; int verified = member_sync_verify_signature(db, ns, &m); - if (verified != 1) { + if (verified != 1 && verified != MS_VERIFY_DEFERRED) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: cannot confirm ns=%s node=%016llx signer=%016llx verification=%d", MS_ID, ns, (unsigned long long)m.node_id, (unsigned long long)m.signed_by, verified); rc = SQLITE_CONSTRAINT; break; @@ -1304,15 +1304,18 @@ static void member_policy_result(void* ctx, const char* ns, uint64_t peer, int r } if (group_policy_refresh(inst, ns)) return; struct ready_member { uint64_t node, signer; uint8_t x[32], signature[64]; }; - struct ready_member* rows = NULL; size_t count = 0; + struct ready_member* rows = NULL; size_t count = 0, capacity = 0; char sql[192]; sqlite3_stmt* st = NULL; snprintf(sql, sizeof(sql), "SELECT node_id,signed_by,x25519_pubkey,signature FROM peers_%s WHERE admitted=1 AND deleted=0", ns); int rc = sqlite3_prepare_v2(_db(inst), sql, -1, &st, NULL); if (rc == SQLITE_OK) while ((rc = sqlite3_step(st)) == SQLITE_ROW) { if (sqlite3_column_bytes(st, 2) != 32 || sqlite3_column_bytes(st, 3) != 64) { rc = SQLITE_CORRUPT; break; } - if (count >= UINT32_MAX / sizeof(*rows) - 1) { rc = SQLITE_TOOBIG; break; } - struct ready_member* p = u_realloc(rows, (count + 1) * sizeof(*rows)); - if (!p) { rc = SQLITE_NOMEM; break; } rows = p; + if (count == capacity) { + size_t next = capacity ? capacity * 2 : 16; + if (next > UINT32_MAX / sizeof(*rows)) { rc = SQLITE_TOOBIG; break; } + struct ready_member* p = u_realloc(rows, next * sizeof(*rows)); + if (!p) { rc = SQLITE_NOMEM; break; } rows = p; capacity = next; + } rows[count].node = (uint64_t)sqlite3_column_int64(st, 0); rows[count].signer = (uint64_t)sqlite3_column_int64(st, 1); memcpy(rows[count].x, sqlite3_column_blob(st, 2), 32); memcpy(rows[count].signature, sqlite3_column_blob(st, 3), 64); count++; } diff --git a/tests/test_group_policy.c b/tests/test_group_policy.c index 83bdc39f2..71a022344 100644 --- a/tests/test_group_policy.c +++ b/tests/test_group_policy.c @@ -4,7 +4,7 @@ static uint8_t group_x[32] = {81}, group_private[32] = {82}, group_public[32]; struct person { uint64_t id; uint8_t x[32], ed[32], private[32], join[64], tree[64], role[64]; }; -static struct person people[6]; +static struct person people[8]; static void public_key(const uint8_t private[32], uint8_t public[32]) { EVP_PKEY* key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, private, 32); assert(key); @@ -51,7 +51,7 @@ static void page(struct UTUN_INSTANCE* src, struct UTUN_INSTANCE* dst, uint64_t int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); struct UASYNC* ua = uasync_create(); assert(ua); public_key(group_private, group_public); - for (unsigned i = 0; i < 6; i++) { + for (unsigned i = 0; i < 8; i++) { struct person* p = &people[i]; p->x[0] = i + 1; p->private[0] = i + 11; public_key(p->private, p->ed); p->id = sc_derive_node_id_from_pubkey(p->x); uint8_t body[112]; assert(member_sync_build_join_msg(group_x, group_public, p->id, p->x, 1, body, sizeof(body)) == 112); @@ -90,6 +90,8 @@ int main(void) { assert(!topo_node_sqlite_member_get_sign(source.topo_sqlite_db, "1001", people[3].id, &signer, signature) && !signer); /* Политика раньше переподписи: текущий допуск сохраняется до полной контрольной точки. */ page(&source, &target, 0); assert(admitted(&target, 3) && admitted(&target, 4)); + member_sync_destroy(&target); assert(!member_sync_init(&target)); + assert(admitted(&target, 3) && admitted(&target, 4) && group_policy_pending(target.topo_sqlite_db, "1001") == 1); const uint64_t mask = ~UINT64_C(0) << (64 - MT_MAX_LEVEL * 5); for (unsigned i = 0; i < 6; i++) page(&source, &target, people[i].id & mask); assert(admitted(&target, 3) && admitted(&target, 4)); @@ -104,6 +106,11 @@ int main(void) { struct ms_member_rec forged = {.node_id = people[3].id, .x25519 = people[3].x, .join_sig = people[4].join, .signature = signature, .signed_by = 0}; assert(member_sync_verify_signature(source.topo_sqlite_db, "1001", &forged) == 0); + put_person(&source, 6, 7); assert(!admitted(&source, 6) && !member_finish(&source, "1001")); + put_person(&source, 7, 6); assert(!admitted(&source, 6) && !admitted(&source, 7)); + token(&source, baseline); + assert(!group_policy_command(&source, "1001", 1, baseline, NULL, &json)); u_free(json); + assert(!admitted(&source, 6) && !admitted(&source, 7)); member_sync_destroy(&source); member_sync_destroy(&target); assert(sqlite3_close(source.topo_sqlite_db) == SQLITE_OK && sqlite3_close(target.topo_sqlite_db) == SQLITE_OK); uasync_destroy(ua, 0); puts("PASS: default, roles, cascade, selection, rollback, stale preview, page order, signature precedence, restart");