From 31c8f35b6b89e8933f4bd801a29cf0f1441e1ba6 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 3 Nov 2026 19:49:58 +0100 Subject: [PATCH] Scope router access policies to CHAT without changing UTUN --- src/routing_layer/etcp_router.c | 33 ++++++++++++++++++++++++++++--- src/routing_layer/etcp_router.h | 7 +++++++ tests/test_chat_group_isolation.c | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/routing_layer/etcp_router.c b/src/routing_layer/etcp_router.c index 01cfe9a9a..1ec0812ba 100644 --- a/src/routing_layer/etcp_router.c +++ b/src/routing_layer/etcp_router.c @@ -70,8 +70,16 @@ static uint32_t router_effective_max_inflight(struct ETCP_ROUTER_CONN* rconn); static void router_update_inflight_limit(struct ETCP_ROUTER_CONN* rconn); static void router_update_minrtt(struct ETCP_ROUTER_CONN* rconn); +/* Общий service ID может ограничивать только CHAT; неизвестная группа не получает обход проверки. */ +static int router_policy_applies(struct UTUN_INSTANCE* inst, uint64_t group_id, uint8_t svc) { + if (!inst->router_bindings.chat_policy[svc]) return 1; + struct TOPO_GROUP* group = inst->topo_groups ? topo_groups_find(inst->topo_groups, group_id) : NULL; + return !group || group->group_type != TOPO_GROUP_TYPE_UTUN; +} + /* Политика не изменяет router; отказ не закрывает канал из чужого стека отправки. */ static int router_allowed(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, uint8_t svc) { + if (!router_policy_applies(inst, group, svc)) return 1; int (*authorize)(struct UTUN_INSTANCE*, uint64_t, uint64_t) = inst->router_bindings.authorize[svc]; if (!authorize || authorize(inst, group, peer) == 1) return 1; DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router access denied: group=%016llx peer=%016llx svc=%u", @@ -98,7 +106,8 @@ static int router_source_send(struct ETCP_CONN* conn, struct ll_entry* e) { struct UTUN_INSTANCE* inst = conn->instance; const struct SVC_ROUTE_HDR* header = (const struct SVC_ROUTE_HDR*)e->dgram; if (!router_allowed(inst, header->group_id, header->dst_node_id, header->svc_id)) return -1; - if (e->len == SVC_ROUTE_HDR_SIZE && (inst->router_bindings.required_crypto[header->svc_id] & ROUTER_FLAG_SIGNED)) { + if (e->len == SVC_ROUTE_HDR_SIZE && router_policy_applies(inst, header->group_id, header->svc_id) + && (inst->router_bindings.required_crypto[header->svc_id] & ROUTER_FLAG_SIGNED)) { uint8_t* signed_wire = NULL; size_t size = 0; if (route_crypto_encode(inst, header->dst_node_id, e->dgram, e->len, ROUTE_CRYPTO_SIGN, &signed_wire, &size)) return -1; queue_dgram_free(e); e->dgram = signed_wire; e->len = size; @@ -913,7 +922,8 @@ static void router_retrans_timer_cb(void* arg) { // Скопировать plaintext и mode в send_q, выделить seq; кодирование выполняется при отправке после handshake. static int router_enqueue_send(struct ETCP_ROUTER_CONN* rconn, const uint8_t* payload, size_t len, int force, int mode) { if (!router_allowed(rconn->inst, rconn->group_id, rconn->remote_node_id, rconn->svc_id)) return -1; - uint8_t required = rconn->inst->router_bindings.required_crypto[rconn->svc_id]; + uint8_t required = router_policy_applies(rconn->inst, rconn->group_id, rconn->svc_id) + ? rconn->inst->router_bindings.required_crypto[rconn->svc_id] : 0; if ((mode & required) != required) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router send: insufficient crypto svc=%u peer=%016llx flags=%02x required=%02x", rconn->svc_id, (unsigned long long)rconn->remote_node_id, mode, required); @@ -1501,7 +1511,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) router_forward_transit(inst, entry, (struct SVC_ROUTE_HDR*)entry->dgram); return; } entry->len = body; - uint8_t required = inst->router_bindings.required_crypto[h->svc_id]; + uint8_t required = router_policy_applies(inst, h->group_id, h->svc_id) ? inst->router_bindings.required_crypto[h->svc_id] : 0; if ((h->flags & (required & ROUTER_FLAG_SIGNED)) != (required & ROUTER_FLAG_SIGNED)) { DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router receive: insufficient crypto svc=%u peer=%016llx flags=%02x required=%02x", h->svc_id, (unsigned long long)h->src_node_id, h->flags, required); @@ -1619,6 +1629,7 @@ int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn ca inst->router_bindings.authorize[svc_id] = NULL; inst->router_bindings.receive_ready[svc_id] = NULL; inst->router_bindings.required_crypto[svc_id] = 0; + inst->router_bindings.chat_policy[svc_id] = 0; return 0; } if (inst->router_bindings.callbacks[svc_id]) @@ -1639,6 +1650,7 @@ int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id) { inst->router_bindings.authorize[svc_id] = NULL; inst->router_bindings.receive_ready[svc_id] = NULL; inst->router_bindings.required_crypto[svc_id] = 0; + inst->router_bindings.chat_policy[svc_id] = 0; DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u", svc_id); return 0; } @@ -1665,6 +1677,21 @@ int etcp_router_set_authorize(struct UTUN_INSTANCE* inst, uint8_t svc_id, return 0; } +int etcp_router_set_chat_policy(struct UTUN_INSTANCE* inst, uint8_t svc_id, uint8_t flags, + int (*authorize)(struct UTUN_INSTANCE*, uint64_t, uint64_t)) { + if (!inst || !inst->router_bindings.callbacks[svc_id] || !authorize || !(flags & ROUTER_FLAG_SIGNED) + || (flags & ~(ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED)) + || inst->router_bindings.authorize[svc_id] || inst->router_bindings.required_crypto[svc_id]) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router CHAT policy invalid or already configured svc=%u flags=%02x", svc_id, flags); + return -1; + } + inst->router_bindings.required_crypto[svc_id] = flags; + inst->router_bindings.authorize[svc_id] = authorize; + inst->router_bindings.chat_policy[svc_id] = 1; + DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "router CHAT policy svc=%u required=%02x; UTUN unchanged", svc_id, flags); + return 0; +} + void etcp_router_revalidate_group(struct UTUN_INSTANCE* inst, uint64_t group_id) { if (!inst) return; for (;;) { diff --git a/src/routing_layer/etcp_router.h b/src/routing_layer/etcp_router.h index f580a6ae7..9d0f5ecf8 100644 --- a/src/routing_layer/etcp_router.h +++ b/src/routing_layer/etcp_router.h @@ -261,6 +261,7 @@ struct ETCP_ROUTER_BINDINGS { int (*authorize[SVC_ROUTE_MAX_BINDINGS])(struct UTUN_INSTANCE*, uint64_t, uint64_t); // Допуск group/peer; 1 — разрешено. int (*receive_ready[SVC_ROUTE_MAX_BINDINGS])(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t); uint8_t required_crypto[SVC_ROUTE_MAX_BINDINGS]; // Обязательные ENCRYPTED/SIGNED для сервисных данных; 0 — без требования. + uint8_t chat_policy[SVC_ROUTE_MAX_BINDINGS]; // 1 — crypto/authorize не применяются к существующей UTUN-группе. }; // ==================================================================== @@ -298,6 +299,12 @@ int etcp_router_require_crypto(struct UTUN_INSTANCE* inst, uint8_t svc_id, uint8 * Транзит не проверяется. Unbind очищает callback. 0/-1 с диагностикой. */ int etcp_router_set_authorize(struct UTUN_INSTANCE* inst, uint8_t svc_id, int (*authorize)(struct UTUN_INSTANCE*, uint64_t, uint64_t)); +/* Установить crypto и authorize общему сервису только для CHAT, сохранив обмен существующих UTUN-групп. + * До первого использования сервиса; требует bound callback, SIGNED, authorize и отсутствие прежней политики. + * Неизвестная группа проходит authorize (обычно получает отказ); сам scope не даёт ей разрешения. + * Ограничения чистоты authorize те же. Bind сохраняет scope, unbind очищает. 0/-1 с диагностикой. */ +int etcp_router_set_chat_policy(struct UTUN_INSTANCE* inst, uint8_t svc_id, uint8_t flags, + int (*authorize)(struct UTUN_INSTANCE*, uint64_t, uint64_t)); /* После commit изменения допуска: закрыть запрещённые каналы группы, уведомить сервисы, очистить очереди. * Не затрагивает общий ETCP и сервисы без authorize; callbacks могут менять список каналов. */ void etcp_router_revalidate_group(struct UTUN_INSTANCE* inst, uint64_t group_id); diff --git a/tests/test_chat_group_isolation.c b/tests/test_chat_group_isolation.c index 79532c051..4cf39c250 100644 --- a/tests/test_chat_group_isolation.c +++ b/tests/test_chat_group_isolation.c @@ -5,6 +5,7 @@ #include #include #include "utun_instance.h" +#include "config_updater.h" #include "broadcast.h" #include "chat/member_sync.h" #include "radio/radio.h"