Browse Source

chat: push-invite join key + запрет флагов на плейсхолдере

- CHANNEL_INVITE теперь несёт join_key (регистрируется инвайтером локально),
  джойнер шлёт JOIN_INFO_REQ с валидным ключом — чинит автоjoin chatserver-узлов
- ask-путь сохраняет join_key для chat_sync_accept_invite
- chat_member_tags_commit отвергает установку флагов на source!=0 (плейсхолдер)
- member_sync_apply_record: defensive ERROR при adm_tags на плейсхолдере
proxy
evgeny 1 week ago
parent
commit
b46fd599fa
  1. 8
      src/chat/chat_core.c
  2. 56
      src/chat/chat_sync.c
  3. 7
      src/chat/member_sync.c

8
src/chat/chat_core.c

@ -668,7 +668,7 @@ int chat_member_tags_commit(struct UTUN_INSTANCE* inst, struct chat_member_tags*
char peers_tbl[80]; peers_table_name(t->ch_id, peers_tbl, sizeof(peers_tbl));
sqlite3_stmt* st = NULL;
char sql[384]; snprintf(sql, sizeof(sql),
"SELECT x25519_pubkey, ed25519_pubkey, join_sig, join_ts, update_sig, update_ts, userinfo, storage"
"SELECT x25519_pubkey, ed25519_pubkey, join_sig, join_ts, update_sig, update_ts, userinfo, storage, source"
" FROM \"%s\" WHERE node_id=?", peers_tbl);
if (sqlite3_prepare_v2(cc->db, sql, -1, &st, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit — member query failed", CC_ID); return -1;
@ -685,9 +685,15 @@ int chat_member_tags_commit(struct UTUN_INSTANCE* inst, struct chat_member_tags*
uint64_t uts = (uint64_t)sqlite3_column_int64(st, 5);
const char* uinfo = (const char*)sqlite3_column_text(st, 6);
int stg = sqlite3_column_int(st, 7);
int src = sqlite3_column_int(st, 8);
if (!x25 || !ed) { sqlite3_finalize(st);
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit — member has no keys ch=%s", CC_ID, t->ch_id); return -1;
}
if (src != 0) { sqlite3_finalize(st);
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: tags_commit — cannot set flags on placeholder member (source=%d) ch=%s nid=0x%016llx",
CC_ID, src, t->ch_id, (unsigned long long)t->node_id);
return -1;
}
const char* stg_val = chat_member_tags_get(t, "storage");
if (stg_val && strcmp(stg_val, "yes") == 0) stg = 1;

56
src/chat/chat_sync.c

@ -31,6 +31,7 @@
#include <string.h>
#include <sqlite3.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
@ -76,6 +77,7 @@ struct chat_sync {
/* Pending incoming invites (для ask/deny) */
uint64_t pending_inv_in_ch_id[4];
uint64_t pending_inv_in_node_id[4];
uint64_t pending_inv_in_key[4];
int pending_inv_in_count;
};
@ -1403,6 +1405,10 @@ static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer,
/* skip inviter_update_sig/ts/userinfo for now — verify channel sig only */
p += 64; uint64_t inviter_update_ts; memcpy(&inviter_update_ts, p, 8); p += 8;
uint8_t userinfo_len = *p++; p += userinfo_len;
/* join-ключ (зарегистрирован инвайтером) — хвостовые 8 байт */
uint64_t join_key = 0;
if (p + 8 > pl + len) { DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "%s: CHANNEL_INVITE truncated (no join_key) from=%016llx", CS_ID, (unsigned long long)peer); return; }
memcpy(&join_key, p, 8); p += 8;
/* Verify channel signature */
uint8_t vmsg[1024]; size_t vlen = 0;
@ -1440,13 +1446,26 @@ static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer,
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: CHANNEL_INVITE channel_put rc=%d ch=%s ch_x25519=%016llx",
CS_ID, cprc, ch_id, *(const uint64_t*)x25519);
chat_core_ensure_channel_ready(cs->inst, ch_id);
/* Initiate join: connect back and send CHANNEL_INFO_REQ */
/* Initiate join: connect back and send JOIN_INFO_REQ с зарегистрированным ключом */
cs->pending_join_key = join_key;
chat_sync_join_channel(cs->inst, ch_id, peer);
return;
}
/* policy == ask — post event to GUI */
{
/* сохранить join-ключ, чтобы принять инвайт позже через chat_sync_accept_invite */
uint64_t ch_num = strtoull(ch_id, NULL, 10);
int slot = -1;
for (int i = 0; i < cs->pending_inv_in_count; i++)
if (cs->pending_inv_in_ch_id[i] == ch_num && cs->pending_inv_in_node_id[i] == peer) { slot = i; break; }
if (slot < 0 && cs->pending_inv_in_count < 4) slot = cs->pending_inv_in_count++;
if (slot >= 0) {
cs->pending_inv_in_ch_id[slot] = ch_num;
cs->pending_inv_in_node_id[slot] = peer;
cs->pending_inv_in_key[slot] = join_key;
}
size_t inv_name_len = 0; char inv_name[128] = "";
{ sqlite3* vdb = cs->inst->topo_sqlite_db;
sqlite3_stmt* st = NULL;
@ -1489,6 +1508,15 @@ static int cs_send_channel_invite(struct chat_sync* cs, struct UTUN_INSTANCE* in
int ulen = member_sync_build_update_msg(join_sig, upd_ts, juser, umsg, (int)sizeof(umsg));
if (ulen >= 0) cs_ed25519_sign(inst->my_ed25519_privkey, umsg, (size_t)ulen, upd_sig);
}
/* join-ключ: генерируем и регистрируем локально (инвайтер == connection-узел),
чтобы джойнер смог пройти JOIN_INFO_REQ с валидным ключом. */
uint64_t join_key = 0;
if (RAND_bytes((uint8_t*)&join_key, sizeof(join_key)) != 1 || join_key == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: join_key generation failed ch=%s", CS_ID, ch_id);
return -1;
}
chat_join_register_key(inst, strtoull(ch_id, NULL, 10), 0, join_key);
uint8_t buf[1024]; size_t boff = 0;
buf[boff++] = CS_MSG_CHANNEL_INVITE;
uint8_t cnl = (uint8_t)strlen(ch_name);
@ -1508,9 +1536,10 @@ static int cs_send_channel_invite(struct chat_sync* cs, struct UTUN_INSTANCE* in
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : "");
uint8_t il = (uint8_t)strlen(juser);
buf[boff++] = il; memcpy(buf + boff, juser, il); boff += il;
memcpy(buf + boff, &join_key, 8); boff += 8;
int rc = cs_send(cs, ch_id, target_node_id, buf, boff);
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: sent CHANNEL_INVITE to 0x%016llx ch=%s rc=%d",
CS_ID, (unsigned long long)target_node_id, ch_id, rc);
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: sent CHANNEL_INVITE to 0x%016llx ch=%s key=%016llx rc=%d",
CS_ID, (unsigned long long)target_node_id, ch_id, (unsigned long long)join_key, rc);
return rc;
}
@ -1716,16 +1745,31 @@ void chat_sync_invite_to_channel_with_addrs(
/* ─── accept/deny incoming invite ─── */
void chat_sync_accept_invite(struct UTUN_INSTANCE* inst, uint64_t channel_id, uint64_t inviter_node_id) {
if (!cs_of(inst) || !inst) return;
struct chat_sync* cs = cs_of(inst);
if (!cs || !inst) return;
char ch_id[64]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)channel_id);
if (channel_id == 0 || inviter_node_id == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: accept_invite invalid params ch=%llu node=%016llx",
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id);
return;
}
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: accept_invite ch=%llu inviter=0x%016llx",
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id);
/* достать join-ключ, сохранённый при приёме CHANNEL_INVITE (ask) */
uint64_t join_key = 0;
for (int i = 0; i < cs->pending_inv_in_count; i++) {
if (cs->pending_inv_in_ch_id[i] == channel_id && cs->pending_inv_in_node_id[i] == inviter_node_id) {
join_key = cs->pending_inv_in_key[i];
break;
}
}
if (join_key == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: accept_invite no join_key for ch=%llu inviter=0x%016llx",
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id);
return;
}
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: accept_invite ch=%llu inviter=0x%016llx key=%016llx",
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id, (unsigned long long)join_key);
chat_core_ensure_channel_ready(inst, ch_id);
cs->pending_join_key = join_key;
chat_sync_join_channel(inst, ch_id, inviter_node_id);
}

7
src/chat/member_sync.c

@ -438,10 +438,11 @@ int member_sync_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
char local_tags[256] = "";
uint64_t local_signed_by = 0;
uint8_t local_signature[64] = {0};
int local_src = 0;
{
sqlite3_stmt* st = NULL;
char sql[512]; snprintf(sql, sizeof(sql),
"SELECT join_sig, join_ts, update_ts, adm_tags, signed_by, signature FROM \"%s\" WHERE node_id=?", peers_tbl);
"SELECT join_sig, join_ts, update_ts, adm_tags, signed_by, signature, source FROM \"%s\" WHERE node_id=?", peers_tbl);
if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) == SQLITE_OK) {
sqlite3_bind_int64(st, 1, (sqlite3_int64)m->node_id);
if (sqlite3_step(st) == SQLITE_ROW) {
@ -455,6 +456,7 @@ int member_sync_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
local_signed_by = (uint64_t)sqlite3_column_int64(st, 4);
const uint8_t* ls = (const uint8_t*)sqlite3_column_blob(st, 5);
if (ls && sqlite3_column_bytes(st, 5) >= 64) memcpy(local_signature, ls, 64);
local_src = sqlite3_column_int(st, 6);
}
sqlite3_finalize(st);
}
@ -572,6 +574,9 @@ int member_sync_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
if (adm_ver > local_ver) block_b_changed = 1;
else if (adm_ver < local_ver) block_b_stale = 1;
}
if (block_b_changed && local_src != 0)
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: adm_tags change on placeholder member (source=%d) — ignored by merkle, won't propagate nid=0x%016llx ns=%s",
MS_ID, local_src, (unsigned long long)m->node_id, ch_id);
/* ── запись изменённых блоков ── */
if (block_a_changed) {

Loading…
Cancel
Save