Report precise INI read failures and identity repair reasons

This commit is contained in:
evgeny committed 2026-10-21 19:09:03 +02:00
1 parent 2df21efc22
commit 4f5651906c
2 files changed
+53 -18

No files matched your search

+34 -14
View File
@@ -10,6 +10,7 @@
#include "../lib/mem.h"
#include "../lib/strbuf.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
@@ -54,10 +55,16 @@ static inline int config_ini_key_value(const char *line, char *key, size_t key_c
/* Проверить физические строки до разбора, чтобы не принять хвост длинной строки за новую опцию. */
static inline int config_ini_validate(const char *text, size_t length, const char *filename) {
size_t column = 0, line = 1;
if (!text && length) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: NULL configuration buffer", filename); return -1; }
if (!text && length) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: NULL configuration buffer", filename); errno = EINVAL; return -1; }
for (size_t i = 0; i < length; i++) {
if (!text[i] || (text[i] != '\n' && ++column >= CONFIG_INI_LINE_SIZE - 1)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: INI contains NUL or a line longer than 1022 bytes", filename, line);
if (!text[i]) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: embedded NUL at byte %zu", filename, line, i);
errno = EINVAL;
return -1;
}
if (text[i] != '\n' && ++column >= CONFIG_INI_LINE_SIZE - 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%zu: INI line exceeds 1022 bytes", filename, line);
errno = EOVERFLOW;
return -1;
}
if (text[i] == '\n') { column = 0; line++; }
@@ -73,26 +80,39 @@ static inline int config_ini_read(FILE *file, const char *filename, char **text,
*text = NULL;
*length = 0;
while ((count = fread(chunk, 1, sizeof(chunk), file)) != 0) {
if (memchr(chunk, '\0', count) || output.len > UINT32_MAX / 2 - count - 1 ||
strbuf_addf(&output, "%.*s", (int)count, chunk) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: cannot read INI text (NUL, size overflow or allocation failure)", filename);
strbuf_free(&output);
return -1;
const char *nul = memchr(chunk, '\0', count);
if (nul) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: embedded NUL at byte %zu", filename, output.len + (size_t)(nul - chunk));
errno = EINVAL;
goto failed;
}
if (output.len > UINT32_MAX / 2 - count - 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: INI text exceeds allocator size limit", filename);
errno = EFBIG;
goto failed;
}
if (strbuf_addf(&output, "%.*s", (int)count, chunk) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: allocation failed while reading INI at byte %zu", filename, output.len);
errno = ENOMEM;
goto failed;
}
}
if (ferror(file) || config_ini_validate(strbuf_str(&output), output.len, filename)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: configuration read/validation failed", filename);
strbuf_free(&output);
return -1;
if (ferror(file)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: read failed at byte %zu: %s", filename, output.len, strerror(errno));
goto failed;
}
if (config_ini_validate(strbuf_str(&output), output.len, filename)) goto failed;
if (!output.buf && strbuf_addf(&output, "%s", "") < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: cannot allocate empty configuration buffer", filename);
strbuf_free(&output);
return -1;
errno = ENOMEM;
goto failed;
}
*length = output.len;
*text = strbuf_detach(&output);
return 0;
failed:
strbuf_free(&output);
return -1;
}
static inline int config_ini_nibble(unsigned char ch) {
+19 -4
View File
@@ -107,7 +107,10 @@ static int config_edit(const char *text, size_t length, const struct config_fiel
int section = CFG_OTHER_SECTION;
for (size_t pos = 0;;) {
/* Запас покрывает вставки полей и строки: strbuf не должен вырасти до 2^32 байтов. */
if (output->len > UINT32_MAX / 2 - 16384) return -1;
if (output->len > UINT32_MAX / 2 - 16384) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "edited INI exceeds allocator size limit");
return -1;
}
for (int s = CFG_GLOBAL_SECTION; s < CFG_SECTION_COUNT; s++) {
if (ends[s] == pos && config_insert_missing(output, fields, s, 1, eol)) return -1;
}
@@ -130,7 +133,10 @@ static int config_edit(const char *text, size_t length, const struct config_fiel
} else if (strbuf_addf(output, "%.*s", (int)(end - pos), text + pos) < 0) return -1;
pos = end;
}
if (output->len > UINT32_MAX / 2 - 16384) return -1;
if (output->len > UINT32_MAX / 2 - 16384) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "edited INI exceeds allocator size limit");
return -1;
}
for (int s = CFG_GLOBAL_SECTION; s < CFG_SECTION_COUNT; s++) {
if (ends[s] == SIZE_MAX && config_insert_missing(output, fields, s, 0, eol)) return -1;
}
@@ -192,7 +198,12 @@ static int config_publish(const char *filename, const char *text, size_t length,
struct stat current;
if (!lstat(filename, &current)) {
if (!exists || !S_ISREG(current.st_mode) || current.st_nlink != 1 ||
current.st_dev != original->st_dev || current.st_ino != original->st_ino) { errno = EINVAL; goto done; }
current.st_dev != original->st_dev || current.st_ino != original->st_ino) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s: destination changed or unsupported: existed=%d mode=%o links=%llu",
filename, exists, (unsigned)current.st_mode, (unsigned long long)current.st_nlink);
errno = EINVAL;
goto done;
}
} else if (exists || errno != ENOENT) goto done;
parent = u_strdup(filename);
if (!parent) goto done;
@@ -327,7 +338,8 @@ int config_ensure_keys_and_node_id(const char *filename) {
struct strbuf output = strbuf_new();
if (config_ini_hex(fields[CFG_PRIVATE].value, private_key, sizeof(private_key))) {
struct SC_MYKEYS keys;
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating identity: effective private key missing or invalid", filename);
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating identity: effective private key %s (length=%zu)", filename,
fields[CFG_PRIVATE].present ? "invalid" : "missing", strlen(fields[CFG_PRIVATE].value));
if (sc_generate_keypair(&keys) != SC_OK) goto done;
memcpy(private_key, keys.private_key, sizeof(private_key));
bytes_to_hex(private_key, sizeof(private_key), fields[CFG_PRIVATE].value, sizeof(fields[CFG_PRIVATE].value));
@@ -348,11 +360,14 @@ int config_ensure_keys_and_node_id(const char *filename) {
if (reality_enabled) {
if (config_ini_hex(fields[CFG_REALITY_PRIVATE].value, reality_private, sizeof(reality_private))) {
uint8_t public_part[REALITY_AUTH_KEY_SIZE];
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating REALITY private key: %s", filename,
fields[CFG_REALITY_PRIVATE].present ? "invalid" : "missing");
if (reality_generate_keypair(reality_private, public_part) != REALITY_OK) goto done;
bytes_to_hex(reality_private, sizeof(reality_private), fields[CFG_REALITY_PRIVATE].value, sizeof(fields[CFG_REALITY_PRIVATE].value));
fields[CFG_REALITY_PRIVATE].update = 1;
}
if (config_ini_hex_list(fields[CFG_REALITY_IDS].value, &ids[0][0], REALITY_SHORT_ID_SIZE, REALITY_MAX_SHORT_IDS, &id_count) || !id_count) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s: generating 4 REALITY short IDs: effective list missing, empty or invalid", filename);
id_count = 4;
if (random_bytes(&ids[0][0], (size_t)id_count * REALITY_SHORT_ID_SIZE)) goto done;
char *cursor = fields[CFG_REALITY_IDS].value;