Mercurial > dovecot > core-2.2
view src/plugins/dict-ldap/dict-ldap.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 | b9a3058184f1 |
children | 85958212120e |
line wrap: on
line source
/* Copyright (c) 2016 Dovecot authors, see the included COPYING memcached */ #include "lib.h" #include "array.h" #include "module-dir.h" #include "str.h" #include "istream.h" #include "ostream.h" #include "var-expand.h" #include "connection.h" #include "llist.h" #include "ldap-client.h" #include "dict.h" #include "dict-private.h" #include "dict-ldap-settings.h" struct ldap_dict; struct dict_ldap_op { struct ldap_dict *dict; const struct dict_ldap_map *map; pool_t pool; unsigned long txid; struct dict_lookup_result res; dict_lookup_callback_t *callback; void *callback_ctx; }; struct ldap_dict { struct dict dict; struct dict_ldap_settings *set; const char *uri; const char *username; const char *base_dn; enum ldap_scope scope; pool_t pool; struct ldap_client *client; struct ioloop *ioloop, *prev_ioloop; unsigned long last_txid; unsigned int pending; struct ldap_dict *prev,*next; }; static void ldap_dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context); static bool dict_ldap_map_match(const struct dict_ldap_map *map, const char *path, ARRAY_TYPE(const_string) *values, size_t *pat_len_r, size_t *path_len_r, bool partial_ok, bool recurse) { const char *path_start = path; const char *pat, *attribute, *p; size_t len; array_clear(values); pat = map->pattern; while (*pat != '\0' && *path != '\0') { if (*pat == '$') { /* variable */ pat++; if (*pat == '\0') { /* pattern ended with this variable, it'll match the rest of the path */ len = strlen(path); if (partial_ok) { /* iterating - the last field never matches fully. if there's a trailing '/', drop it. */ pat--; if (path[len-1] == '/') { attribute = t_strndup(path, len-1); array_append(values, &attribute, 1); } else { array_append(values, &path, 1); } } else { array_append(values, &path, 1); path += len; } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; return TRUE; } /* pattern matches until the next '/' in path */ p = strchr(path, '/'); if (p != NULL) { attribute = t_strdup_until(path, p); array_append(values, &attribute, 1); path = p; } else { /* no '/' anymore, but it'll still match a partial */ array_append(values, &path, 1); path += strlen(path); pat++; } } else if (*pat == *path) { pat++; path++; } else { return FALSE; } } *path_len_r = path - path_start; *pat_len_r = pat - map->pattern; if (*pat == '\0') return *path == '\0'; else if (!partial_ok) return FALSE; else { /* partial matches must end with '/'. */ if (pat != map->pattern && pat[-1] != '/') return FALSE; /* if we're not recursing, there should be only one $variable left. */ if (recurse) return TRUE; return pat[0] == '$' && strchr(pat, '/') == NULL; } } static const struct dict_ldap_map * ldap_dict_find_map(struct ldap_dict *dict, const char *path, ARRAY_TYPE(const_string) *values) { const struct dict_ldap_map *maps; unsigned int i, count; size_t len; t_array_init(values, dict->set->max_attribute_count); maps = array_get(&dict->set->maps, &count); for (i = 0; i < count; i++) { if (dict_ldap_map_match(&maps[i], path, values, &len, &len, FALSE, FALSE)) return &maps[i]; } return NULL; } static int dict_ldap_connect(struct ldap_dict *dict, const char **error_r) { struct ldap_client_settings set; memset(&set, 0, sizeof(set)); set.uri = dict->set->uri; set.bind_dn = dict->set->bind_dn; set.password = dict->set->password; set.timeout_secs = dict->set->timeout; set.max_idle_time_secs = dict->set->max_idle_time; set.debug = dict->set->debug; set.require_ssl = dict->set->require_ssl; set.start_tls = dict->set->start_tls; return ldap_client_init(&set, &dict->client, error_r); } static void ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv, string_t *query_r) { const char *template; ARRAY(struct var_expand_table) exp; struct var_expand_table entry; t_array_init(&exp, 8); entry.key = '\0'; entry.value = dict->username; entry.long_key = "username"; array_append(&exp, &entry, 1); if (priv) { template = t_strdup_printf("(&(%s=%s)%s)", map->username_attribute, "%{username}", map->filter); } else { template = map->filter; } for(size_t i = 0; i < array_count(values) && i < array_count(&(map->ldap_attributes)); i++) { struct var_expand_table entry; entry.value = *array_idx(values, i); entry.long_key = *array_idx(&(map->ldap_attributes), i); array_append(&exp, &entry, 1); } array_append_zero(&exp); var_expand(query_r, template, array_idx(&exp, 0)); } static int ldap_dict_init(struct dict *dict_driver, const char *uri, const struct dict_settings *set, struct dict **dict_r, const char **error_r) { pool_t pool = pool_alloconly_create("ldap dict", 2048); struct ldap_dict *dict = p_new(pool, struct ldap_dict, 1); dict->pool = pool; dict->dict = *dict_driver; dict->username = p_strdup(pool, set->username); dict->uri = p_strdup(pool, uri); dict->set = dict_ldap_settings_read(pool, uri, error_r); if (dict->set == NULL) { pool_unref(&pool); return -1; } if (dict_ldap_connect(dict, error_r) < 0) { pool_unref(&pool); return -1; } *dict_r = (struct dict*)dict; *error_r = NULL; return 0; } static void ldap_dict_deinit(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; ldap_client_deinit(&ctx->client); pool_unref(&ctx->pool); } static int ldap_dict_wait(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; i_assert(ctx->ioloop == NULL); ctx->prev_ioloop = current_ioloop; ctx->ioloop = io_loop_create(); dict_switch_ioloop(dict); do { io_loop_run(current_ioloop); } while (ctx->pending > 0); io_loop_set_current(ctx->prev_ioloop); dict_switch_ioloop(dict); io_loop_set_current(ctx->ioloop); io_loop_destroy(&ctx->ioloop); ctx->prev_ioloop = NULL; return 0; } static bool ldap_dict_switch_ioloop(struct dict *dict) { struct ldap_dict *ctx = (struct ldap_dict *)dict; ldap_client_switch_ioloop(ctx->client); return ctx->pending > 0; } static void ldap_dict_lookup_done(const struct dict_lookup_result *result, void *ctx) { struct dict_lookup_result *res = ctx; res->ret = result->ret; res->value = t_strdup(result->value); res->error = t_strdup(result->error); } static void ldap_dict_lookup_callback(struct ldap_result *result, struct dict_ldap_op *op) { pool_t pool = op->pool; struct ldap_search_iterator *iter; const struct ldap_entry *entry; op->dict->pending--; if (ldap_result_has_failed(result)) { op->res.ret = -1; op->res.error = ldap_result_get_error(result); } else { iter = ldap_search_iterator_init(result); entry = ldap_search_iterator_next(iter); if (entry != NULL) { if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback got dn %s", ldap_entry_dn(entry)); /* try extract value */ const char *const *values = ldap_entry_get_attribute(entry, op->map->value_attribute); if (values != NULL) { if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback got attribute %s", op->map->value_attribute); op->res.ret = 1; op->res.value = p_strdup(op->pool, values[0]); } else { if (op->dict->set->debug > 0) i_debug("ldap_dict_lookup_callback dit not get attribute %s", op->map->value_attribute); op->res.value = NULL; } } ldap_search_iterator_deinit(&iter); } op->callback(&(op->res), op->callback_ctx); pool_unref(&pool); } static int ldap_dict_lookup(struct dict *dict, pool_t pool, const char *key, const char **value_r) { struct dict_lookup_result res; pool_t orig_pool = pool; int ret; T_BEGIN { ldap_dict_lookup_async(dict, key, ldap_dict_lookup_done, &res); if ((ret = ldap_dict_wait(dict)) == 0) { if (res.ret == 0) { *value_r = p_strdup(orig_pool, res.value); } else ret = res.ret; } } T_END; return ret; } /* static struct dict_iterate_context *ldap_dict_iterate_init(struct dict *dict, const char *const *paths, enum dict_iterate_flags flags) { return NULL; } static bool ldap_dict_iterate(struct dict_iterate_context *ctx, const char **key_r, const char **value_r) { return FALSE; } static int ldap_dict_iterate_deinit(struct dict_iterate_context *ctx) { return -1; } static struct dict_transaction_context ldap_dict_transaction_init(struct dict *dict); static int ldap_dict_transaction_commit(struct dict_transaction_context *ctx, bool async, dict_transaction_commit_callback_t *callback, void *context); static void ldap_dict_transaction_rollback(struct dict_transaction_context *ctx); static void ldap_dict_set(struct dict_transaction_context *ctx, const char *key, const char *value); static void ldap_dict_unset(struct dict_transaction_context *ctx, const char *key); static void ldap_dict_append(struct dict_transaction_context *ctx, const char *key, const char *value); static void ldap_dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); */ static void ldap_dict_lookup_async(struct dict *dict, const char *key, dict_lookup_callback_t *callback, void *context) { struct ldap_search_input input; struct ldap_dict *ctx = (struct ldap_dict*)dict; struct dict_ldap_op *op; pool_t oppool = pool_alloconly_create("ldap dict lookup", 64); string_t *query = str_new(oppool, 64); op = p_new(oppool, struct dict_ldap_op, 1); op->pool = oppool; op->dict = ctx; op->callback = callback; op->callback_ctx = context; op->txid = ctx->last_txid++; /* key needs to be transformed into something else */ ARRAY_TYPE(const_string) values; T_BEGIN { const char *attributes[2] = {0, 0}; t_array_init(&values, 8); const struct dict_ldap_map *map = ldap_dict_find_map(ctx, key, &values); if (map != NULL) { op->map = map; attributes[0] = map->value_attribute; /* build lookup */ memset(&input, 0, sizeof(input)); input.base_dn = map->base_dn; input.scope = map->scope_val; ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0, query); input.filter = str_c(query); input.attributes = attributes; input.timeout_secs = ctx->set->timeout; ctx->pending++; ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op); } else { op->res.error = "no such key"; callback(&(op->res), context); pool_unref(&oppool); } } T_END; } struct dict dict_driver_ldap = { .name = "ldap", { ldap_dict_init, ldap_dict_deinit, ldap_dict_wait, ldap_dict_lookup, NULL, /*ldap_dict_iterate_init,*/ NULL, /*ldap_dict_iterate,*/ NULL, /*ldap_dict_iterate_deinit,*/ NULL, /*ldap_transaction_init,*/ NULL, /*ldap_transaction_commit,*/ NULL, /*ldap_transaction_rollback,*/ NULL, /*ldap_set,*/ NULL, /*ldap_unset,*/ NULL, /*ldap_append,*/ NULL, /*ldap_atomic_inc,*/ ldap_dict_lookup_async, ldap_dict_switch_ioloop } }; void dict_ldap_init(struct module *module ATTR_UNUSED); void dict_ldap_deinit(void); void dict_ldap_init(struct module *module ATTR_UNUSED) { dict_driver_register(&dict_driver_ldap); } void dict_ldap_deinit(void) { ldap_clients_cleanup(); dict_driver_unregister(&dict_driver_ldap); } const char *dict_ldap_plugin_dependencies[] = { NULL };