diff --git a/src/config_parser.c b/src/config_parser.c index 68d8a416..555cfc50 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -23,6 +23,16 @@ static int assign_string(char *dest, size_t dest_size, const char *src); static int parse_firewall_rule(const char *rule_str, struct global_config *global); +static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) { + if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1; + for (size_t i = 0; i < binary_len; i++) { + unsigned int byte; + if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1; + binary[i] = (uint8_t)byte; + } + return 0; +} + typedef enum { SECTION_UNKNOWN, SECTION_GLOBAL, @@ -31,7 +41,8 @@ typedef enum { SECTION_ROUTING, SECTION_DEBUG, SECTION_FIREWALL, - SECTION_CONTROL + SECTION_CONTROL, + SECTION_ALLOWED_KEYS } section_type_t; static char* trim(char *str) { @@ -507,6 +518,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t if (strcasecmp(section, "debug") == 0) return SECTION_DEBUG; if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL; if (strcasecmp(section, "control") == 0) return SECTION_CONTROL; + if (strcasecmp(section, "allowed_keys") == 0) return SECTION_ALLOWED_KEYS; char *colon = strchr(section, ':'); if (!colon) return SECTION_UNKNOWN; @@ -642,6 +654,26 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename) DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid control key '%s'", filename, line_num, key); } break; + case SECTION_ALLOWED_KEYS: + if (strcmp(key, "allow_all") == 0) { + cfg->global.allowed_keys_allow_all = atoi(value) ? 1 : 0; + } else if (strcmp(key, "key") == 0) { + struct CFG_ALLOWED_KEY *new_key = u_calloc(1, sizeof(struct CFG_ALLOWED_KEY)); + if (!new_key) { + DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate allowed key"); + } else if (strlen(value) != SC_PUBKEY_SIZE * 2) { + DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid key length %zu, expected %d", filename, line_num, strlen(value), SC_PUBKEY_SIZE * 2); + u_free(new_key); + } else if (hex_to_binary(value, new_key->key_bin, SC_PUBKEY_SIZE) != 0) { + DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid hex key format", filename, line_num); + u_free(new_key); + } else { + new_key->next = cfg->global.allowed_keys; + cfg->global.allowed_keys = new_key; + cfg->global.allowed_keys_count++; + } + } + break; default: DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key); break; @@ -718,6 +750,13 @@ void free_config(struct utun_config *config) { u_free(config->global.firewall_rules); // Free control allow rules (array allocated with realloc) u_free(config->global.control_allows); + // Free allowed keys + struct CFG_ALLOWED_KEY *ak = config->global.allowed_keys; + while (ak) { + struct CFG_ALLOWED_KEY *next = ak->next; + u_free(ak); + ak = next; + } u_free(config); } @@ -804,6 +843,19 @@ void print_config(const struct utun_config *cfg) { } DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Control allows: %d rules (default deny all if 0)", g->control_allow_count); + + DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Allowed keys: allow_all=%d, count=%d", g->allowed_keys_allow_all, g->allowed_keys_count); + if (!g->allowed_keys_allow_all && g->allowed_keys_count > 0) { + struct CFG_ALLOWED_KEY *ak = g->allowed_keys; + while (ak) { + char key_hex[SC_PUBKEY_SIZE * 2 + 1]; + for (int i = 0; i < SC_PUBKEY_SIZE; i++) { + snprintf(key_hex + i * 2, sizeof(key_hex) - i * 2, "%02x", ak->key_bin[i]); + } + DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " key=%s", key_hex); + ak = ak->next; + } + } } int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) { diff --git a/src/config_parser.h b/src/config_parser.h index 7c700142..57f60f3f 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -5,6 +5,7 @@ #include #include #include "../lib/platform_compat.h" +#include "secure_channel.h" #ifdef __cplusplus extern "C" { @@ -72,6 +73,11 @@ struct CFG_CONTROL_ALLOW { uint32_t netmask; // netmask in host byte order (e.g. 0xffffff00 for /24) }; +struct CFG_ALLOWED_KEY { + uint8_t key_bin[SC_PUBKEY_SIZE]; // public key in binary (64 bytes) + struct CFG_ALLOWED_KEY *next; +}; + struct global_config { char name[16]; // Instance name char my_private_key_hex[MAX_KEY_LEN]; @@ -110,6 +116,11 @@ struct global_config { // Control server configuration ([control] section) struct CFG_CONTROL_ALLOW *control_allows; int control_allow_count; + + // Allowed keys configuration ([allowed_keys] section) + struct CFG_ALLOWED_KEY *allowed_keys; + int allowed_keys_count; + int allowed_keys_allow_all; }; struct utun_config { diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 4bbe95f4..9cc86779 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -1126,6 +1126,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { // 5 - коллизия peer ID и ключей // 6 - не удалось расшифровать обычный пакет // 7 - слишком короткий пакет +// 8 - ключ не в списке allowed_keys // 13 - переполнение при парсинге пакета // 46 - расшифрованный пакет слишком маленький (< 3 байта) // 55 - не удалось создать подключение @@ -1197,6 +1198,30 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { errorcode=3; goto ec_fr; } + + // Check allowed keys for incoming connections + struct global_config *global = &e_sock->instance->config->global; + if (!global->allowed_keys_allow_all) { + if (global->allowed_keys_count == 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Connection rejected: no allowed_keys configured, allow_all=0"); + errorcode=8; + goto ec_fr; + } + int found = 0; + struct CFG_ALLOWED_KEY *ak = global->allowed_keys; + while (ak) { + if (memcmp(ak->key_bin, sc.peer_public_key, SC_PUBKEY_SIZE) == 0) { + found = 1; + break; + } + ak = ak->next; + } + if (!found) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Connection rejected: peer public key not in allowed_keys list"); + errorcode=8; + goto ec_fr; + } + } // INIT decryption succeeded - process as new incoming connection if (pkt_len<3) { diff --git a/tests/test_bgp_route_exchange.c b/tests/test_bgp_route_exchange.c index a4811df0..b3a8244a 100644 --- a/tests/test_bgp_route_exchange.c +++ b/tests/test_bgp_route_exchange.c @@ -95,7 +95,10 @@ static const char* server_config_content = "\n" "[server: test]\n" "addr=127.0.0.1:39011\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; // Client config: 2 subnets - 192.168.20.0/24 and 192.168.21.0/24 static const char* client_config_content = diff --git a/tests/test_etcp_100_packets.c b/tests/test_etcp_100_packets.c index b73080c2..bbe9c44c 100644 --- a/tests/test_etcp_100_packets.c +++ b/tests/test_etcp_100_packets.c @@ -48,7 +48,10 @@ static const char* server_config_content = "\n" "[server: test]\n" "addr=127.0.0.1:9021\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; // Client config content static const char* client_config_content = diff --git a/tests/test_etcp_api.c b/tests/test_etcp_api.c index 48c1fde6..6bb6fd08 100644 --- a/tests/test_etcp_api.c +++ b/tests/test_etcp_api.c @@ -55,7 +55,10 @@ static const char* server_config_content = "\n" "[server: test]\n" "addr=127.0.0.1:9031\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; // Client config content (uses ports 9032) static const char* client_config_content = diff --git a/tests/test_etcp_ping.c b/tests/test_etcp_ping.c index 60b4dfdd..f05a765f 100644 --- a/tests/test_etcp_ping.c +++ b/tests/test_etcp_ping.c @@ -39,7 +39,10 @@ static const char* server_config_content = "\n" "[server:test]\n" "addr=127.0.0.1:9011\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; static const char* client_config_content = "[global]\n" diff --git a/tests/test_etcp_simple_traffic.c b/tests/test_etcp_simple_traffic.c index 64865af3..6c74d487 100644 --- a/tests/test_etcp_simple_traffic.c +++ b/tests/test_etcp_simple_traffic.c @@ -67,7 +67,10 @@ static int create_temp_configs(void) { "\n" "[server: test]\n" "addr=127.0.0.1:%d\n" - "type=public\n", + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n", server_port); fclose(f); diff --git a/tests/test_etcp_two_instances.c b/tests/test_etcp_two_instances.c index 1fc61cee..1aa7aa18 100644 --- a/tests/test_etcp_two_instances.c +++ b/tests/test_etcp_two_instances.c @@ -48,7 +48,10 @@ static const char* server_config_content = "\n" "[server: test]\n" "addr=127.0.0.1:9011\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; // Client config content static const char* client_config_content = diff --git a/tests/test_nat_detection.c b/tests/test_nat_detection.c index 6d83fc7f..7dabc8cb 100644 --- a/tests/test_nat_detection.c +++ b/tests/test_nat_detection.c @@ -87,7 +87,10 @@ static int create_temp_configs(void) { "\n" "[server:test_s]\n" "addr=127.0.0.1:39011\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; if (write_config(config_s, tpl_s) != 0) return -1; if (config_ensure_keys_and_node_id(config_s) != 0) return -1; char* pub_s = get_pubkey_from_config(config_s); diff --git a/tests/test_pkt_normalizer_etcp.c b/tests/test_pkt_normalizer_etcp.c index 7eca42d8..dcaf6acd 100644 --- a/tests/test_pkt_normalizer_etcp.c +++ b/tests/test_pkt_normalizer_etcp.c @@ -52,7 +52,10 @@ static const char* server_config_content = "\n" "[server: test]\n" "addr=127.0.0.1:9041\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; // Client config content (uses ports 9042 - unique to avoid conflicts) static const char* client_config_content = diff --git a/tests/test_route_ping.c b/tests/test_route_ping.c index 615da393..6b1fc599 100644 --- a/tests/test_route_ping.c +++ b/tests/test_route_ping.c @@ -87,7 +87,10 @@ static int create_temp_configs(void) { "\n" "[server:test_c]\n" "addr=127.0.0.1:39013\n" - "type=public\n"; + "type=public\n" + "\n" + "[allowed_keys]\n" + "allow_all=1\n"; if (write_config(config_c, tpl_c) != 0) return -1; if (config_ensure_keys_and_node_id(config_c) != 0) return -1; char* pub_c = get_pubkey_from_config(config_c); @@ -105,6 +108,9 @@ static int create_temp_configs(void) { "addr=127.0.0.1:39012\n" "type=public\n" "\n" + "[allowed_keys]\n" + "allow_all=1\n" + "\n" "[client:to_c]\n" "keepalive=1\n" "peer_public_key=%s\n" @@ -128,6 +134,9 @@ static int create_temp_configs(void) { "addr=127.0.0.1:39011\n" "type=public\n" "\n" + "[allowed_keys]\n" + "allow_all=1\n" + "\n" "[client:to_b]\n" "keepalive=1\n" "peer_public_key=%s\n"