Config: Added [allowed_keys] section for client public key authorization on server

- Added CFG_ALLOWED_KEY struct with binary key storage (64 bytes) for fast memcmp comparison
- Implemented parsing of [allowed_keys] section with allow_all flag and per-key entries
- Added key validation in etcp_connections.c server handler (errorcode=8 for rejected keys)
- Default behavior: deny all if [allowed_keys] section is missing or allow_all=0 with no keys
- Added proper memory cleanup and debug logging for allowed_keys configuration
- Updated all test configs to include [allowed_keys] allow_all=1 for backward compatibility
This commit is contained in:
Evgeny committed 2026-04-24 00:16:30 +03:00
1 parent 3625a4e5d5
commit e39979bfac
12 files changed
+131 -10

No files matched your search

+53 -1
View File
@@ -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) {
+11
View File
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <stddef.h>
#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 {
+25
View File
@@ -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) {