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 на плейсхолдере
This commit is contained in:
evgeny committed 2026-09-22 17:58:50 +03:00
1 parent 0e625da109
commit b46fd599fa
3 files changed
+63 -8

No files matched your search

+50 -6
View File
@@ -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);
}