Mercurial > dovecot > core-2.2
view src/lib-dict/dict-sql-settings.c @ 21322:5ab8dc1a4a6f
global: Change string position/length from unsigned int to size_t
Mainly to avoid truncating >4GB strings, which might potentially cause
some security holes. Normally there are other limits, which prevent such
excessive strings from being created in the first place.
I'm sure this didn't find everything. Maybe everything could be found with
compiler warnings. -Wconversion kind of does it, but it gives way too many
unnecessary warnings.
These were mainly found with:
grep " = strlen"
egrep "unsigned int.*(size|len)"
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Mon, 12 Dec 2016 07:19:55 +0200 |
parents | f0d35d358f1b |
children | 59437f8764c6 |
line wrap: on
line source
/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "str.h" #include "hash.h" #include "settings.h" #include "dict-sql-settings.h" #include <ctype.h> enum section_type { SECTION_ROOT = 0, SECTION_MAP, SECTION_FIELDS }; struct dict_sql_map_field { struct dict_sql_field sql_field; const char *variable; }; struct setting_parser_ctx { pool_t pool; struct dict_sql_settings *set; enum section_type type; struct dict_sql_map cur_map; ARRAY(struct dict_sql_map_field) cur_fields; }; #define DEF_STR(name) DEF_STRUCT_STR(name, dict_sql_map) #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_sql_map) static const struct setting_def dict_sql_map_setting_defs[] = { DEF_STR(pattern), DEF_STR(table), DEF_STR(username_field), DEF_STR(value_field), DEF_STR(value_type), DEF_BOOL(value_hexblob), { 0, NULL, 0 } }; struct dict_sql_settings_cache { pool_t pool; const char *path; struct dict_sql_settings *set; }; static HASH_TABLE(const char *, struct dict_sql_settings_cache *) dict_sql_settings_cache; static const char *pattern_read_name(const char **pattern) { const char *p = *pattern, *name; if (*p == '{') { /* ${name} */ name = ++p; p = strchr(p, '}'); if (p == NULL) { /* error, but allow anyway */ *pattern += strlen(*pattern); return ""; } *pattern = p + 1; } else { /* $name - ends at the first non-alnum_ character */ name = p; for (; *p != '\0'; p++) { if (!i_isalnum(*p) && *p != '_') break; } *pattern = p; } name = t_strdup_until(name, p); return name; } static const char *dict_sql_fields_map(struct setting_parser_ctx *ctx) { struct dict_sql_map_field *fields; string_t *pattern; const char *p, *name; unsigned int i, count; /* go through the variables in the pattern, replace them with plain '$' character and add its sql field */ pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1); fields = array_get_modifiable(&ctx->cur_fields, &count); p_array_init(&ctx->cur_map.sql_fields, ctx->pool, count); for (p = ctx->cur_map.pattern; *p != '\0';) { if (*p != '$') { str_append_c(pattern, *p); p++; continue; } p++; str_append_c(pattern, '$'); name = pattern_read_name(&p); for (i = 0; i < count; i++) { if (fields[i].variable != NULL && strcmp(fields[i].variable, name) == 0) break; } if (i == count) { return t_strconcat("Missing SQL field for variable: ", name, NULL); } /* mark this field as used */ fields[i].variable = NULL; array_append(&ctx->cur_map.sql_fields, &fields[i].sql_field, 1); } /* make sure there aren't any unused fields */ for (i = 0; i < count; i++) { if (fields[i].variable != NULL) { return t_strconcat("Unused variable: ", fields[i].variable, NULL); } } if (ctx->set->max_field_count < count) ctx->set->max_field_count = count; ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern)); return NULL; } static const char *dict_sql_map_finish(struct setting_parser_ctx *ctx) { if (ctx->cur_map.pattern == NULL) return "Missing setting: pattern"; if (ctx->cur_map.table == NULL) return "Missing setting: table"; if (ctx->cur_map.value_field == NULL) return "Missing setting: value_field"; if (ctx->cur_map.value_type != NULL) { if (strcmp(ctx->cur_map.value_type, "string") != 0 && strcmp(ctx->cur_map.value_type, "hexblob") != 0 && strcmp(ctx->cur_map.value_type, "uint") != 0) return "Invalid value in value_type"; } if (ctx->cur_map.username_field == NULL) { /* not all queries require this */ ctx->cur_map.username_field = "'username_field not set'"; } if (!array_is_created(&ctx->cur_map.sql_fields)) { /* no fields besides value. allocate the array anyway. */ p_array_init(&ctx->cur_map.sql_fields, ctx->pool, 1); if (strchr(ctx->cur_map.pattern, '$') != NULL) return "Missing fields for pattern variables"; } array_append(&ctx->set->maps, &ctx->cur_map, 1); memset(&ctx->cur_map, 0, sizeof(ctx->cur_map)); return NULL; } static const char * parse_setting(const char *key, const char *value, struct setting_parser_ctx *ctx) { struct dict_sql_map_field *field; size_t value_len; switch (ctx->type) { case SECTION_ROOT: if (strcmp(key, "connect") == 0) { ctx->set->connect = p_strdup(ctx->pool, value); return NULL; } break; case SECTION_MAP: return parse_setting_from_defs(ctx->pool, dict_sql_map_setting_defs, &ctx->cur_map, key, value); case SECTION_FIELDS: if (*value != '$') { return t_strconcat("Value is missing '$' for field: ", key, NULL); } field = array_append_space(&ctx->cur_fields); field->sql_field.name = p_strdup(ctx->pool, key); value_len = strlen(value); if (strncmp(value, "${hexblob:", 10) == 0 && value[value_len-1] == '}') { field->variable = p_strndup(ctx->pool, value + 10, value_len-10-1); field->sql_field.value_type = DICT_SQL_TYPE_HEXBLOB; } else if (strncmp(value, "${uint:", 7) == 0 && value[value_len-1] == '}') { field->variable = p_strndup(ctx->pool, value + 7, value_len-7-1); field->sql_field.value_type = DICT_SQL_TYPE_UINT; } else { field->variable = p_strdup(ctx->pool, value + 1); } return NULL; } return t_strconcat("Unknown setting: ", key, NULL); } static bool parse_section(const char *type, const char *name ATTR_UNUSED, struct setting_parser_ctx *ctx, const char **error_r) { switch (ctx->type) { case SECTION_ROOT: if (type == NULL) return FALSE; if (strcmp(type, "map") == 0) { array_clear(&ctx->cur_fields); ctx->type = SECTION_MAP; return TRUE; } break; case SECTION_MAP: if (type == NULL) { ctx->type = SECTION_ROOT; *error_r = dict_sql_map_finish(ctx); return FALSE; } if (strcmp(type, "fields") == 0) { ctx->type = SECTION_FIELDS; return TRUE; } break; case SECTION_FIELDS: if (type == NULL) { ctx->type = SECTION_MAP; *error_r = dict_sql_fields_map(ctx); return FALSE; } break; } *error_r = t_strconcat("Unknown section: ", type, NULL); return FALSE; } struct dict_sql_settings * dict_sql_settings_read(const char *path, const char **error_r) { struct setting_parser_ctx ctx; struct dict_sql_settings_cache *cache; pool_t pool; if (!hash_table_is_created(dict_sql_settings_cache)) { hash_table_create(&dict_sql_settings_cache, default_pool, 0, str_hash, strcmp); } cache = hash_table_lookup(dict_sql_settings_cache, path); if (cache != NULL) return cache->set; memset(&ctx, 0, sizeof(ctx)); pool = pool_alloconly_create("dict sql settings", 1024); ctx.pool = pool; ctx.set = p_new(pool, struct dict_sql_settings, 1); t_array_init(&ctx.cur_fields, 16); p_array_init(&ctx.set->maps, pool, 8); if (!settings_read(path, NULL, parse_setting, parse_section, &ctx, error_r)) { pool_unref(&pool); return NULL; } if (ctx.set->connect == NULL) { *error_r = t_strdup_printf("Error in configuration file %s: " "Missing connect setting", path); pool_unref(&pool); return NULL; } cache = p_new(pool, struct dict_sql_settings_cache, 1); cache->pool = pool; cache->path = p_strdup(pool, path); cache->set = ctx.set; hash_table_insert(dict_sql_settings_cache, cache->path, cache); return ctx.set; } void dict_sql_settings_deinit(void) { struct hash_iterate_context *iter; struct dict_sql_settings_cache *cache; const char *key; if (!hash_table_is_created(dict_sql_settings_cache)) return; iter = hash_table_iterate_init(dict_sql_settings_cache); while (hash_table_iterate(iter, dict_sql_settings_cache, &key, &cache)) pool_unref(&cache->pool); hash_table_iterate_deinit(&iter); hash_table_destroy(&dict_sql_settings_cache); }