You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

413 lines
12 KiB

// config_updater.c - Configuration file updater implementation
#define _POSIX_C_SOURCE 200809L
#include "config_updater.h"
#include "config_parser.h"
#include "secure_channel.h"
#include "debug_config.h"
#include "../lib/platform_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PRIV_HEXKEY_LEN 65 // 32 bytes * 2 hex chars + null
#define PUB_HEXKEY_LEN 129 // 64 bytes * 2 hex chars + null
#define HEXNODEID_LEN 17 // 8 bytes * 2 hex chars + null
#define MAX_LINE_LEN 1024
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) {
if (!bytes || !hex_str || hex_len < len * 2 + 1) {
if (hex_str && hex_len > 0) hex_str[0] = '\0';
return;
}
for (size_t i = 0; i < len; i++) {
snprintf(hex_str + i * 2, hex_len - i * 2, "%02x", bytes[i]);
}
hex_str[len * 2] = '\0';
}
static int is_valid_priv_key(const char *key) {
if (!key || strlen(key) != 64) return 0;
for (int i = 0; i < 64; i++) {
if (!isxdigit((unsigned char)key[i])) return 0;
}
return 1;
}
static int is_valid_pub_key(const char *key) {
if (!key || strlen(key) != 128) return 0;
for (int i = 0; i < 128; i++) {
if (!isxdigit((unsigned char)key[i])) return 0;
}
return 1;
}
static int is_valid_node_id(uint64_t node_id) {
return node_id != 0;
}
static int read_file_to_mem(const char *filename, char **buffer, size_t *size) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
if (file_size < 0) {
fclose(fp);
return -1;
}
fseek(fp, 0, SEEK_SET);
*buffer = malloc(file_size + 1);
if (!*buffer) {
fclose(fp);
return -1;
}
size_t read_size = fread(*buffer, 1, file_size, fp);
if (read_size != (size_t)file_size) {
free(*buffer);
fclose(fp);
return -1;
}
(*buffer)[file_size] = '\0';
*size = file_size;
fclose(fp);
return 0;
}
static int write_mem_to_file(const char *filename, const char *buffer, size_t size) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
size_t written = fwrite(buffer, 1, size, fp);
if (written != size) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
static char* find_section_global(char *buf, size_t buf_len) {
char *p = buf;
while (p < buf + buf_len) {
if (p[0] == '[' && strncmp(p + 1, "global", 6) == 0) {
char *end = strchr(p, ']');
if (end) return end + 1;
}
p = strchr(p, '\n');
if (!p) break;
p++; // skip '\n'
}
return NULL;
}
static char* find_option(char *buf, size_t buf_len, const char *option) {
char *p = buf;
while (p < buf + buf_len) {
if (strncmp(p, option, strlen(option)) == 0) {
char *after_key = p + strlen(option);
if (after_key[0] == '=') return p;
}
p = strchr(p, '\n');
if (!p) break;
p++;
}
return NULL;
}
static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_capacity, const char *option, const char *value) {
if (!buf || !*buf || !buf_len || !buf_capacity || !option || !value) return -1;
// Check if option already exists
char *opt_pos = find_option(*buf, *buf_len, option);
if (opt_pos) {
// Find option line end
char *line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Calculate length difference
size_t old_line_len = line_end - opt_pos + 1;
long len_diff = new_len - old_line_len;
// Ensure buffer capacity
if (*buf_len + len_diff + 1 > *buf_capacity) {
*buf_capacity = *buf_len + len_diff + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate positions after realloc
opt_pos = find_option(*buf, *buf_len, option);
if (!opt_pos) return -1;
line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
}
// Move content and insert new line
memmove(opt_pos + new_len, line_end, *buf_len - (line_end - *buf) + 1);
memcpy(opt_pos, new_line, new_len);
*buf_len += len_diff;
} else {
// Insert after [global] section
char *section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
// Find end of global section (next [ or end of buffer)
char *insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
// Move to beginning of next line
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Ensure buffer capacity
if (*buf_len + new_len + 1 > *buf_capacity) {
*buf_capacity = *buf_len + new_len + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate insert position after realloc
section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
}
// Move content and insert new line
memmove(insert_pos + new_len, insert_pos, *buf_len - (insert_pos - *buf) + 1);
memcpy(insert_pos, new_line, new_len);
*buf_len += new_len;
}
return 0;
}
int config_ensure_keys_and_node_id(const char *filename) {
struct utun_config *config = parse_config(filename);
if (!config) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse config: %s", filename);
return -1;
}
struct global_config *global = &config->global;
// Debug: print what we found
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Checking config - priv_key='%s' (len=%zu), pub_key='%s' (len=%zu), node_id=%llu",
global->my_private_key_hex ? global->my_private_key_hex : "NULL",
global->my_private_key_hex ? strlen(global->my_private_key_hex) : 0,
global->my_public_key_hex ? global->my_public_key_hex : "NULL",
global->my_public_key_hex ? strlen(global->my_public_key_hex) : 0,
(unsigned long long)global->my_node_id);
// Check if we need to generate anything
int need_priv_key = !is_valid_priv_key(global->my_private_key_hex);
int need_pub_key = !is_valid_pub_key(global->my_public_key_hex);
int need_node_id = !is_valid_node_id(global->my_node_id);
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d",
need_priv_key, need_pub_key, need_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
// Generate keys if needed
char new_priv_key[PRIV_HEXKEY_LEN] = {0};
char new_pub_key[PUB_HEXKEY_LEN] = {0};
uint64_t new_node_id = 0;
if (need_priv_key) {
// Generate new keypair if private key is invalid
struct SC_MYKEYS mykeys;
if (sc_generate_keypair(&mykeys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair");
free_config(config);
return -1;
}
bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(mykeys.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
} else if (need_pub_key) {
// Compute public key from existing private key
uint8_t priv_bin[SC_PRIVKEY_SIZE];
uint8_t pub_bin[SC_PUBKEY_SIZE];
// Convert private key from hex to binary
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int byte;
if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid private key hex format");
free_config(config);
return -1;
}
priv_bin[i] = (uint8_t)byte;
}
// Compute public key
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to compute public key from private key");
free_config(config);
return -1;
}
// Convert to hex
bytes_to_hex(priv_bin, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(pub_bin, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
}
if (need_node_id) {
if (random_bytes((uint8_t*)&new_node_id, sizeof(new_node_id)) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate random node_id");
free_config(config);
return -1;
}
new_node_id &= 0x7FFFFFFFFFFFFFFF;
}
free_config(config);
// Read entire config file to memory
char *file_buf = NULL;
size_t file_size = 0;
if (read_file_to_mem(filename, &file_buf, &file_size) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read config file: %s", filename);
return -1;
}
// Update config file
size_t buf_capacity = file_size + 1024;
char *work_buf = malloc(buf_capacity);
if (!work_buf) {
free(file_buf);
return -1;
}
memcpy(work_buf, file_buf, file_size);
size_t work_len = file_size;
int ret = 0;
if (need_node_id) {
char node_id_hex[HEXNODEID_LEN + 1];
snprintf(node_id_hex, sizeof(node_id_hex), "%016llx", (unsigned long long)new_node_id);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) {
ret = -1;
}
}
if (need_priv_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) {
ret = -1;
}
}
if (need_pub_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) {
ret = -1;
}
}
if (ret == 0) {
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Writing updated config file, work_len=%zu", work_len);
if (write_mem_to_file(filename, work_buf, work_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to write updated config file: %s", filename);
ret = -1;
} else {
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Successfully updated config file: %s", filename);
}
}
free(file_buf);
free(work_buf);
return ret;
}