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:
1 parent
3625a4e5d5
commit
e39979bfac
12 files changed
+131
-10
No files matched your search
+53
-1
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user