fix(chat): validate owner versions and complete member reads before mutation
This commit is contained in:
1 parent
91fb996a09
commit
16d7fc4fb5
2 files changed
+34
-7
No files matched your search
+30
-7
@@ -52,6 +52,28 @@ static uint64_t member_read64(const uint8_t* p) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Версия owner-блока — каноническая десятичная строка1..INT_MAX,без atoi overflow. */
|
||||
static int owner_version_pair(const char* key, const char* text, void* arg) {
|
||||
if (strcmp(key, "ver")) return 0;
|
||||
int value = 0;
|
||||
if (*text < '1' || *text > '9') goto invalid;
|
||||
for (const char* p = text; *p; p++) {
|
||||
if (*p < '0' || *p > '9' || value > (INT_MAX - (*p - '0')) / 10) goto invalid;
|
||||
value = value * 10 + (*p - '0');
|
||||
}
|
||||
*(int*)arg = value; return 0;
|
||||
invalid:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member owner: invalid version format/range"); return -1;
|
||||
}
|
||||
|
||||
int member_sync_owner_version(sqlite3* db, const char* tags) {
|
||||
if (!tags || !tags[0]) return 0;
|
||||
int version = 0;
|
||||
if (chat_json_strings(db, tags, MS_ADM_TAGS_MAX, owner_version_pair, &version)) return -1;
|
||||
if (!version) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member owner: missing version"); return -1; }
|
||||
return version;
|
||||
}
|
||||
|
||||
int member_sync_build_owner_msg(uint64_t node_id, const char* tags, uint8_t* out, size_t capacity) {
|
||||
size_t len = tags ? strlen(tags) : 0;
|
||||
if (!tags || !out || len > MS_ADM_TAGS_MAX || capacity < len + 8) {
|
||||
@@ -577,10 +599,10 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
|
||||
if (sqlite3_errcode(db) == SQLITE_NOMEM) rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: local record read failed ns=%s node=%016llx rc=%d: %s",
|
||||
MS_ID, ch_id, (unsigned long long)m->node_id, rc, sqlite3_errmsg(db)); return MT_ERR_IO;
|
||||
int final = sqlite3_finalize(st);
|
||||
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: local record read failed ns=%s node=%016llx rc=%d finalize=%d: %s",
|
||||
MS_ID, ch_id, (unsigned long long)m->node_id, rc, final, sqlite3_errmsg(db)); return MT_ERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,7 +688,8 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
|
||||
int adm_ver = 0, adm_storage = 0;
|
||||
if (m->adm_tags && m->adm_tags[0] && m->adm_tags_sig && !_sig_is_zero64(m->adm_tags_sig)) {
|
||||
char ver_str[32];
|
||||
adm_ver = json_flat_get(m->adm_tags, "ver", ver_str, sizeof(ver_str)) == 0 ? atoi(ver_str) : 0;
|
||||
adm_ver = member_sync_owner_version(db, m->adm_tags);
|
||||
if (adm_ver < 0) return MT_ERR_IO;
|
||||
adm_storage = (json_flat_get(m->adm_tags, "storage", ver_str, sizeof(ver_str)) == 0 && strcmp(ver_str, "yes") == 0) ? 1 : 0;
|
||||
uint8_t ch_ed_pub[32] = {0};
|
||||
int lookup = topo_node_sqlite_channel_get(db, ch_id, NULL, 0, NULL, NULL, ch_ed_pub, NULL);
|
||||
@@ -689,8 +712,8 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
|
||||
if (block_b_ok) {
|
||||
int local_ver = 0;
|
||||
if (has_local && local_tags[0]) {
|
||||
char lv[32];
|
||||
if (json_flat_get(local_tags, "ver", lv, sizeof(lv)) == 0) local_ver = atoi(lv);
|
||||
local_ver = member_sync_owner_version(db, local_tags);
|
||||
if (local_ver < 0) return MT_ERR_IO;
|
||||
}
|
||||
if (adm_ver > local_ver) block_b_changed = 1;
|
||||
else if (adm_ver < local_ver) block_b_stale = 1;
|
||||
|
||||
@@ -56,6 +56,10 @@ int member_sync_verified_ed25519(struct UTUN_INSTANCE* inst, const char* channel
|
||||
/* UTF-8 JSON bytes, excluding NUL. A complete record fits in one Merkle page. */
|
||||
#define MS_ADM_TAGS_MAX 8192
|
||||
#define MS_USERINFO_MAX 1024
|
||||
/* Проверить весь строковый JSON owner-блока,уникальность ключей и ver1..INT_MAX без ведущих нулей.
|
||||
* Возвращает версию,0 для NULL/пустого блока,-1 при ошибке формата/SQL. Подпись здесь не проверяется. */
|
||||
int member_sync_owner_version(struct sqlite3* db, const char* tags);
|
||||
|
||||
/* Байты для подписи владельца: tags без NUL, затем node_id[8] в порядке байтов хоста.
|
||||
* Возвращает длину или -1, если данные/буфер не подходят. */
|
||||
int member_sync_build_owner_msg(uint64_t node_id, const char* tags, uint8_t* out, size_t capacity);
|
||||
|
||||
Reference in new issue
Block a user