fix: NCD close UAF + conn name + init_connections per-link ni_tmp

- ncd_down_cb: defer callback removal+close via uasync_call_soon to avoid
  UAF in etcp_cbk_fire iteration (removing ncd_up_cb corrupts saved next ptr)
- ncd_recv_control_handler: use deferred close for CLOSE handlers instead of
  direct etcp_connection_close to avoid nested etcp_cbk_fire UAF
- node_conn_direct_open/open_node: use ni->node_name if available for
  conn name, fallback to 'n_<node_id>' instead of NULL
- init_connections: build ni_tmp in link loop with only current link's addr,
  preventing NCD from creating links for ALL addrs on first socket
- utun_instance_reload: same per-link ni_tmp fix
- init_connections/reload: set ni_tmp.node_name = client->name for
  test find_client_link by name
This commit is contained in:
evgeny committed 2026-08-01 12:30:31 +03:00
1 parent 4ba4ab038c
commit 8237d92be9
3 files changed
+207 -230

No files matched your search

+52 -55
View File
@@ -18,6 +18,7 @@
#include "chat/chat_sync.h"
#include "stcp_server.h"
#include "control_server.h"
#include "transport_layer/node_conn_direct.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
@@ -418,6 +419,17 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->control_srv = NULL;
}
// Close config-based connection handles (sends CLOSE before sockets die)
struct CONFIG_CONN_HANDLE* ch = instance->config_conn_handles;
while (ch) {
struct CONFIG_CONN_HANDLE* next = ch->next;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[INSTANCE_DESTROY] closing config handle for node=0x%016llx", (unsigned long long)ch->node_id);
node_conn_direct_close(ch->handle);
u_free(ch);
ch = next;
}
instance->config_conn_handles = NULL;
// Cleanup ETCP sockets and connections FIRST (before destroying uasync)
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Cleaning up ETCP sockets and connections");
struct ETCP_SOCKET* sock = instance->etcp_sockets;
@@ -857,67 +869,52 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
}
os = next;
}
// Clients [client:] and links (link=)
// Clients [client:] — close old config handles, rebuild from new config
struct CONFIG_CONN_HANDLE* ch = instance->config_conn_handles;
while (ch) { struct CONFIG_CONN_HANDLE* next = ch->next; node_conn_direct_close(ch->handle); u_free(ch); ch = next; }
instance->config_conn_handles = NULL;
for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) {
struct ETCP_CONN *conn = NULL;
{
struct ll_entry* entry = instance->connections->head;
while (entry) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (strcmp(nc->name, ce->conn->name) == 0) { conn = ce->conn; break; }
entry = entry->next;
}
}
if (!conn) {
conn = etcp_connection_create(instance, nc->name);
if (conn) {
sc_set_peer_public_key(&conn->crypto_ctx, (const uint8_t*)nc->peer_public_key_hex, 1);
if (random_bytes((uint8_t*)&conn->session_id, sizeof(conn->session_id)) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to generate session_id for client %s", nc->name);
conn->session_id = 0;
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new client %s session_id=%08x", nc->name, conn->session_id);
}
}
// links
if (strlen(nc->peer_public_key_hex) == 0) { DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "client %s no peer pubkey", nc->name); continue; }
uint8_t pubkey_bin[SC_PUBKEY_SIZE];
if (sc_hex_to_binary(nc->peer_public_key_hex, pubkey_bin, SC_PUBKEY_SIZE) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid pubkey hex client %s", nc->name); continue; }
uint64_t node_id = sc_derive_node_id_from_pubkey(pubkey_bin);
struct NODE_CONN_DIRECT* handle = NULL;
struct ETCP_CONN* conn = NULL;
for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {
struct ETCP_LINK *link = NULL;
for (struct ETCP_LINK *l = conn->links; l; l = l->next) {
if (local_sockaddr_equal(&nl->remote_addr, &l->remote_addr)) {
link = l; break;
struct ETCP_SOCKET* sock = NULL;
for (struct ETCP_SOCKET* s = instance->etcp_sockets; s; s = s->next)
if (nl->local_srv && strcmp(nl->local_srv->name, s->name) == 0) { sock = s; break; }
if (!sock) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "no socket for server '%s' client %s", nl->local_srv ? nl->local_srv->name : "?", nc->name); continue; }
if (!handle) {
struct TOPO_ADDR4 v4_addr; struct TOPO_ADDR6 v6_addr;
struct TOPO_NODE ni_tmp; memset(&ni_tmp, 0, sizeof(ni_tmp));
ni_tmp.node_id = node_id; ni_tmp.node_name = nc->name;
memcpy(ni_tmp.public_key, pubkey_bin, SC_PUBKEY_SIZE);
if (nl->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&nl->remote_addr;
v4_addr = (struct TOPO_ADDR4){0}; memcpy(v4_addr.addr, &sin->sin_addr.s_addr, 4);
v4_addr.port = ntohs(sin->sin_port); v4_addr.type = TOPO_ADDR_INTERFACE; v4_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v4_addrs = &v4_addr;
} else if (nl->remote_addr.ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&nl->remote_addr;
v6_addr = (struct TOPO_ADDR6){0}; memcpy(v6_addr.addr, &sin6->sin6_addr, 16);
v6_addr.port = ntohs(sin6->sin6_port); v6_addr.type = TOPO_ADDR_INTERFACE; v6_addr.protocol = TOPO_PROTO_UDP;
ni_tmp.v6_addrs = &v6_addr;
}
}
if (link) {
// in-place for unchanged link (no tear)
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Link for %s unchanged - untouched", nc->name);
continue;
}
struct ETCP_SOCKET *sock = NULL;
for (struct ETCP_SOCKET *s = instance->etcp_sockets; s; s = s->next) {
if (strcmp(nl->server_name, s->name) == 0) {
sock = s; break;
}
}
if (sock) {
int rc = node_conn_direct_open_node(instance, node_id, NULL, NULL, &handle, &ni_tmp, sock);
if (rc == NCD_ERR || !handle) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ncd reload open failed client %s", nc->name); break; }
conn = node_conn_direct_get_conn(handle);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "reload: client %s ncd %s node=0x%016llx", nc->name, rc == NCD_NEW ? "NEW" : "REUSED", (unsigned long long)node_id);
} else {
etcp_link_new(conn, sock, &nl->remote_addr, 0);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new link for %s", nc->name);
}
}
// remove deleted links (safe with next)
struct ETCP_LINK *l = conn->links;
while (l) {
struct ETCP_LINK *next_l = l->next;
int found = 0;
for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {
if (local_sockaddr_equal(&nl->remote_addr, &l->remote_addr)) {
found = 1; break;
}
}
if (!found) {
etcp_link_close(l);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Removed deleted link for %s", nc->name);
}
l = next_l;
if (handle) {
ch = u_calloc(1, sizeof(*ch));
if (ch) { ch->node_id = node_id; strncpy(ch->name, nc->name, MAX_CONN_NAME_LEN - 1);
ch->handle = handle; ch->next = instance->config_conn_handles; instance->config_conn_handles = ch; }
}
}
// Reload networks: clear and repopulate