diff --git a/src/config.c b/src/config.c index 5fcbc9e..8a9551a 100644 --- a/src/config.c +++ b/src/config.c @@ -67,20 +67,32 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name, return CHROMA_ERROR_MEMORY; } + // Validate string lengths to prevent buffer overflow + size_t output_len = strlen(output_name); + size_t path_len = strlen(image_path); + + if (output_len >= sizeof(config->mappings[0].output_name)) { + chroma_log("ERROR", "Output name too long: %s (max %zu)", output_name, + sizeof(config->mappings[0].output_name) - 1); + return CHROMA_ERROR_CONFIG; + } + + if (path_len >= sizeof(config->mappings[0].image_path)) { + chroma_log("ERROR", "Image path too long: %s (max %zu)", image_path, + sizeof(config->mappings[0].image_path) - 1); + return CHROMA_ERROR_CONFIG; + } + chroma_config_mapping_t *mapping = &config->mappings[config->mapping_count]; - strncpy(mapping->output_name, output_name, sizeof(mapping->output_name) - 1); - mapping->output_name[sizeof(mapping->output_name) - 1] = '\0'; - - strncpy(mapping->image_path, image_path, sizeof(mapping->image_path) - 1); - mapping->image_path[sizeof(mapping->image_path) - 1] = '\0'; + strcpy(mapping->output_name, output_name); + strcpy(mapping->image_path, image_path); config->mapping_count++; chroma_log("DEBUG", "Added mapping: %s -> %s", output_name, image_path); chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)", - config->mapping_count, output_name, image_path, - strlen(image_path)); + config->mapping_count, output_name, image_path, path_len); return CHROMA_OK; } @@ -138,11 +150,29 @@ static int parse_config_line(chroma_config_t *config, char *line, // Parse configuration options if (strcasecmp(key, "default_image") == 0) { - strncpy(config->default_image, value, sizeof(config->default_image) - 1); - config->default_image[sizeof(config->default_image) - 1] = '\0'; - chroma_log("DEBUG", "Set default image: %s", value); - chroma_log("TRACE", "Default image path set: length=%zu, expanded='%s'", - strlen(value), value); + char *expanded_path = chroma_expand_path(value); + const char *path_to_use = expanded_path ? expanded_path : value; + size_t path_len = strlen(path_to_use); + + if (path_len >= sizeof(config->default_image)) { + chroma_log("ERROR", "Default image path too long: %s (max %zu)", + path_to_use, sizeof(config->default_image) - 1); + if (expanded_path) { + free(expanded_path); + } + return CHROMA_ERROR_CONFIG; + } + + strcpy(config->default_image, path_to_use); + + if (expanded_path) { + chroma_log("DEBUG", "Set default image: %s -> %s", value, expanded_path); + chroma_log("TRACE", "Default image path set: length=%zu, expanded='%s'", + path_len, expanded_path); + free(expanded_path); + } else { + chroma_log("WARN", "Failed to expand path, using original: %s", value); + } } else if (strcasecmp(key, "daemon") == 0 || strcasecmp(key, "daemon_mode") == 0) { config->daemon_mode = parse_bool(value); @@ -160,18 +190,33 @@ static int parse_config_line(chroma_config_t *config, char *line, return CHROMA_OK; } + // Expand path before validation and storage + char *expanded_path = chroma_expand_path(value); + const char *path_to_validate = expanded_path ? expanded_path : value; + // Validate image path - if (chroma_image_validate(value) != CHROMA_OK) { - chroma_log("WARN", "Invalid image path for output %s: %s", output_name, - value); + if (chroma_image_validate(path_to_validate) != CHROMA_OK) { + chroma_log("WARN", "Invalid image path for output %s: %s (expanded: %s)", + output_name, value, path_to_validate); + if (expanded_path) { + free(expanded_path); + } return CHROMA_OK; } - if (add_output_mapping(config, output_name, value) != CHROMA_OK) { + if (add_output_mapping(config, output_name, path_to_validate) != + CHROMA_OK) { chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name, - value); + path_to_validate); + if (expanded_path) { + free(expanded_path); + } return CHROMA_ERROR_CONFIG; } + + if (expanded_path) { + free(expanded_path); + } } else { chroma_log("WARN", "Unknown configuration key line %d: %s", line_number, key); diff --git a/src/image.c b/src/image.c index 53e03d8..6a22991 100644 --- a/src/image.c +++ b/src/image.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "../include/chroma.h" diff --git a/src/utils.c b/src/utils.c index 8b66fd3..5341c5b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -94,18 +94,138 @@ void chroma_cleanup_signals(void) { g_config_file = NULL; } -// Expand tilde in path +// Expand environment variables in a string +static char *expand_env_vars(const char *str) { + if (!str || strchr(str, '$') == NULL) { + return strdup(str); + } + + char *result = strdup(""); + if (!result) { + return NULL; + } + + const char *p = str; + while (*p) { + if (*p == '$') { + p++; + if (*p == '{') { + // ${VAR} format + p++; + const char *end = strchr(p, '}'); + if (!end) { + // No closing brace, treat as literal + char *tmp = realloc(result, strlen(result) + 2); + if (!tmp) { + free(result); + return NULL; + } + result = tmp; + strcat(result, "${"); + break; + } + + size_t var_len = end - p; + char *var_name = malloc(var_len + 1); + if (!var_name) { + free(result); + return NULL; + } + strncpy(var_name, p, var_len); + var_name[var_len] = '\0'; + + const char *var_value = getenv(var_name); + if (var_value) { + char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1); + if (!tmp) { + free(var_name); + free(result); + return NULL; + } + result = tmp; + strcat(result, var_value); + } + + free(var_name); + p = end + 1; + } else { + // $VAR format + const char *start = p; + while (*p && (isalnum((unsigned char)*p) || *p == '_')) { + p++; + } + + if (p == start) { + // Not a valid variable name, treat $ as literal + char *tmp = realloc(result, strlen(result) + 2); + if (!tmp) { + free(result); + return NULL; + } + result = tmp; + strcat(result, "$"); + } else { + size_t var_len = p - start; + char *var_name = malloc(var_len + 1); + if (!var_name) { + free(result); + return NULL; + } + strncpy(var_name, start, var_len); + var_name[var_len] = '\0'; + + const char *var_value = getenv(var_name); + if (var_value) { + char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1); + if (!tmp) { + free(var_name); + free(result); + return NULL; + } + result = tmp; + strcat(result, var_value); + } + + free(var_name); + } + } + } else { + // Regular character + size_t len = strlen(result); + char *tmp = realloc(result, len + 2); + if (!tmp) { + free(result); + return NULL; + } + result = tmp; + result[len] = *p; + result[len + 1] = '\0'; + p++; + } + } + + return result; +} + +// Expand tilde and environment variables in path char *chroma_expand_path(const char *path) { if (!path) { return NULL; } - if (path[0] != '~') { - return strdup(path); + // First expand environment variables + char *env_expanded = expand_env_vars(path); + if (!env_expanded) { + return NULL; + } + + // Then expand tilde if present + if (env_expanded[0] != '~') { + return env_expanded; } const char *home; - if (path[1] == '/' || path[1] == '\0') { + if (env_expanded[1] == '/' || env_expanded[1] == '\0') { // ~/... or just ~ home = getenv("HOME"); if (!home) { @@ -116,34 +236,39 @@ char *chroma_expand_path(const char *path) { } if (!home) { chroma_log("ERROR", "Could not determine home directory"); + free(env_expanded); return strdup(path); // Return original path as fallback } size_t home_len = strlen(home); - size_t path_len = strlen(path); + size_t path_len = strlen(env_expanded); char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0 if (!expanded) { chroma_log("ERROR", "Failed to allocate memory for path expansion"); + free(env_expanded); return strdup(path); } strcpy(expanded, home); - if (path[1] == '/') { - strcat(expanded, path + 1); + if (env_expanded[1] == '/') { + strcat(expanded, env_expanded + 1); } + free(env_expanded); return expanded; } else { // ~user/... - const char *slash = strchr(path, '/'); - size_t user_len = slash ? (size_t)(slash - path - 1) : strlen(path) - 1; + const char *slash = strchr(env_expanded, '/'); + size_t user_len = + slash ? (size_t)(slash - env_expanded - 1) : strlen(env_expanded) - 1; char *username = malloc(user_len + 1); if (!username) { + free(env_expanded); return strdup(path); } - strncpy(username, path + 1, user_len); + strncpy(username, env_expanded + 1, user_len); username[user_len] = '\0'; struct passwd *pw = getpwnam(username); @@ -151,6 +276,7 @@ char *chroma_expand_path(const char *path) { if (!pw) { chroma_log("ERROR", "User not found: %s", username); free(username); + free(env_expanded); return strdup(path); } @@ -160,6 +286,7 @@ char *chroma_expand_path(const char *path) { size_t remaining_len = slash ? strlen(slash) : 0; char *expanded = malloc(home_len + remaining_len + 1); if (!expanded) { + free(env_expanded); return strdup(path); } @@ -168,6 +295,7 @@ char *chroma_expand_path(const char *path) { strcat(expanded, slash); } + free(env_expanded); return expanded; } } @@ -413,4 +541,4 @@ void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size) { } // Cleanup utility functions -void chroma_utils_cleanup(void) { chroma_cleanup_signals(); } \ No newline at end of file +void chroma_utils_cleanup(void) { chroma_cleanup_signals(); }