// 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 #include #include #include #include #include #include #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) { printf("[CONFIG ERROR] Failed to parse config: %s\n", filename); return -1; } struct global_config *global = &config->global; // Debug: print what we found printf("[CONFIG DEBUG] Checking config - priv_key='%s' (len=%zu), pub_key='%s' (len=%zu), node_id=%llu\n", 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); printf("[CONFIG DEBUG] Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d\n", 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) { printf( "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) { printf( "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) { printf( "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) { int fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { printf( "Failed to open /dev/urandom"); free_config(config); return -1; } if (read(fd, &new_node_id, sizeof(new_node_id)) != sizeof(new_node_id)) { close(fd); printf( "Failed to read random bytes for node_id"); free_config(config); return -1; } close(fd); 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) { printf( "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), "%llx", (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) { printf("[CONFIG DEBUG] Writing updated config file, work_len=%zu\n", work_len); if (write_mem_to_file(filename, work_buf, work_len) < 0) { printf( "Failed to write updated config file: %s", filename); ret = -1; } else { printf("[CONFIG DEBUG] Successfully updated config file: %s\n", filename); } } free(file_buf); free(work_buf); return ret; }