From: Marco Bettini Date: Fri, 18 Oct 2024 14:04:41 +0000 (+0000) Subject: dict-ldap: dict_ldap_map_settings - Var expand pattern variables in filter setting X-Git-Tag: 2.4.0~118 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=91cb1d4891005dacdf69becce558d50cbbe68229;p=thirdparty%2Fdovecot%2Fcore.git dict-ldap: dict_ldap_map_settings - Var expand pattern variables in filter setting Also remove setting_define.username_attribute as it is no longer used. Now the username must be included explicitly in the filter. --- diff --git a/src/lib-dict-backend/dict-ldap-settings.c b/src/lib-dict-backend/dict-ldap-settings.c index 94fdb09467..6f03e10436 100644 --- a/src/lib-dict-backend/dict-ldap-settings.c +++ b/src/lib-dict-backend/dict-ldap-settings.c @@ -9,6 +9,7 @@ #include "settings.h" #include "settings-parser.h" #include "dict-ldap-settings.h" +#include "dict.h" #include @@ -25,17 +26,13 @@ static const struct setting_define dict_ldap_map_setting_defines[] = { DEF(STR, pattern), DEFN(STR, base, ldap_base), - DEFN(STR, filter, ldap_filter), DEFN(ENUM, scope, ldap_scope), - DEF(STR, username_attribute), DEF(BOOLLIST, values), SETTING_DEFINE_LIST_END }; static const struct dict_ldap_map_settings dict_ldap_map_default_settings = { .pattern = "", - .filter = "", - .username_attribute = "cn", .values = ARRAY_INIT, .base = "", .scope = "subtree:onelevel:base", @@ -51,6 +48,29 @@ const struct setting_parser_info dict_ldap_map_setting_parser_info = { .pool_offset1 = 1 + offsetof(struct dict_ldap_map_settings, pool), }; +#undef DEFN +#define DEFN(type, field, name) \ + SETTING_DEFINE_STRUCT_##type(#name, field, struct dict_ldap_map_pre_settings) + +static const struct setting_define dict_ldap_map_pre_setting_defines[] = { + DEFN(STR, filter, ldap_filter), + SETTING_DEFINE_LIST_END +}; + +static const struct dict_ldap_map_pre_settings dict_ldap_map_pre_default_settings = { + .filter = "", +}; + +const struct setting_parser_info dict_ldap_map_pre_setting_parser_info = { + .name = "dict_ldap_map_pre", + + .defines = dict_ldap_map_pre_setting_defines, + .defaults = &dict_ldap_map_pre_default_settings, + + .struct_size = sizeof(struct dict_ldap_map_pre_settings), + .pool_offset1 = 1 + offsetof(struct dict_ldap_map_pre_settings, pool), +}; + #undef DEF #define DEF(type, name) \ SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, struct dict_ldap_settings) @@ -77,13 +97,14 @@ const struct setting_parser_info dict_ldap_setting_parser_info = { static int dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set, - const char **error_r) + struct dict_ldap_map_pre_settings *pre, + const char **error_r) { if (!str_begins_with(pre->filter, "(")) { *error_r = "ldap_filter must start with '('"; return -1; } - if (set->filter[strlen(set->filter) - 1]!= ')') { + if (!str_ends_with(pre->filter, ")")) { *error_r = "ldap_filter must end with ')'"; return -1; } @@ -93,11 +114,6 @@ dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set, return -1; } - if (*set->username_attribute == '\0') { - *error_r = "username_attribute not set"; - return -1; - } - if (array_is_empty(&set->values)) { *error_r = "ldap_map_value not set"; return -1; @@ -166,6 +182,13 @@ static void dict_ldap_settings_parse_pattern(struct dict_ldap_map_settings *map) map->parsed_pattern = p_strdup(map->pool, str_c(pattern)); } +#define chain_ref(dst, src) \ +STMT_START { \ + pool_add_external_ref(dst, src); \ + pool_t tmp = (src); \ + pool_unref(&tmp); \ +} STMT_END + static int dict_ldap_settings_parse_maps(struct event *event, struct dict_ldap_settings *set, const char **error_r) @@ -179,26 +202,31 @@ dict_ldap_settings_parse_maps(struct event *event, struct dict_ldap_settings *se const char *name; array_foreach_elem(&set->maps, name) { - struct dict_ldap_map_settings *map; + struct dict_ldap_map_settings *map = NULL; + struct dict_ldap_map_pre_settings *pre = NULL; if (settings_get_filter(event, "dict_map", name, &dict_ldap_map_setting_parser_info, - SETTINGS_GET_FLAG_NO_EXPAND, &map, - error_r) < 0) { + 0, &map, error_r) < 0 || + settings_get_filter(event, "dict_map", name, + &dict_ldap_map_pre_setting_parser_info, + SETTINGS_GET_FLAG_NO_EXPAND, + &pre, error_r) < 0) { *error_r = t_strdup_printf("Failed to get dict_map %s: %s", name, *error_r); + settings_free(map); + settings_free(pre); return -1; } - if (dict_ldap_map_settings_postcheck(map, error_r) < 0) { + if (dict_ldap_map_settings_postcheck(map, pre, error_r) < 0) { settings_free(map); + settings_free(pre); return -1; } + settings_free(pre); dict_ldap_settings_parse_pattern(map); - pool_add_external_ref(set->pool, map->pool); - pool_t pool_copy = map->pool; - pool_unref(&pool_copy); - + chain_ref(set->pool, map->pool); array_push_back(&set->parsed_maps, map); } diff --git a/src/lib-dict-backend/dict-ldap-settings.h b/src/lib-dict-backend/dict-ldap-settings.h index 82d0865aba..fbdf4803cc 100644 --- a/src/lib-dict-backend/dict-ldap-settings.h +++ b/src/lib-dict-backend/dict-ldap-settings.h @@ -5,8 +5,6 @@ struct dict_ldap_map_settings { pool_t pool; const char *pattern; - const char *filter; - const char *username_attribute; ARRAY_TYPE(const_string) values; const char *base; const char *scope; @@ -21,6 +19,11 @@ struct dict_ldap_map_settings { const char *parsed_pattern; }; +struct dict_ldap_map_pre_settings { + pool_t pool; + const char *filter; +}; + struct dict_ldap_settings { pool_t pool; ARRAY_TYPE(const_string) maps; @@ -30,6 +33,7 @@ struct dict_ldap_settings { }; extern const struct setting_parser_info dict_ldap_map_setting_parser_info; +extern const struct setting_parser_info dict_ldap_map_pre_setting_parser_info; extern const struct setting_parser_info dict_ldap_setting_parser_info; int dict_ldap_settings_get(struct event *event, diff --git a/src/lib-dict-backend/dict-ldap.c b/src/lib-dict-backend/dict-ldap.c index 3954a0386d..196807e58e 100644 --- a/src/lib-dict-backend/dict-ldap.c +++ b/src/lib-dict-backend/dict-ldap.c @@ -22,6 +22,13 @@ static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= "; struct ldap_dict; +struct dict_ldap_vars_context { + const struct dict_ldap_map_settings *set; + ARRAY_TYPE(const_string) values; + const char *username; + bool private; +}; + struct dict_ldap_op { struct ldap_dict *dict; struct event *event; @@ -128,7 +135,7 @@ int dict_ldap_connect(struct ldap_dict *dict, const char **error_r) #define IS_LDAP_ESCAPED_CHAR(c) \ ((((unsigned char)(c)) & 0x80) != 0 || strchr(LDAP_ESCAPE_CHARS, (c)) != NULL) -static const char *ldap_escape(const char *str) +static const char *ldap_escape(const char *str, void *context ATTR_UNUSED) { string_t *ret = NULL; @@ -146,50 +153,6 @@ static const char *ldap_escape(const char *str) return ret == NULL ? str : str_c(ret); } -static bool -ldap_dict_build_query(const struct dict_op_settings *set, - const struct dict_ldap_map_settings *map, - ARRAY_TYPE(const_string) *values, bool priv, - string_t *query_r, const char **error_r) -{ - const char *template, *error; - ARRAY(struct var_expand_table) exp; - struct var_expand_table entry; - - t_array_init(&exp, 8); - if (priv) { - i_assert(set->username != NULL); - i_zero(&entry); - entry.value = ldap_escape(set->username); - entry.key = "username"; - array_push_back(&exp, &entry); - 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->parsed_pattern_keys); i++) { - struct var_expand_table entry; - const char *value = array_idx_elem(values, i); - const char *long_key = array_idx_elem(&map->parsed_pattern_keys, i); - i_zero(&entry); - entry.value = ldap_escape(value); - entry.key = long_key; - array_push_back(&exp, &entry); - } - - array_append_zero(&exp); - const struct var_expand_params params = { - .table = array_front(&exp), - }; - - if (var_expand(query_r, template, ¶ms, &error) < 0) { - *error_r = t_strdup_printf("Failed to expand %s: %s", template, error); - return FALSE; - } - return TRUE; -} - static int ldap_dict_init(const struct dict *dict_driver, struct event *event, struct dict **dict_r, const char **error_r) @@ -275,11 +238,8 @@ ldap_dict_lookup_cb_values(const struct ldap_entry *entry, struct dict_ldap_op * e_debug(op->event, "got dn %s", ldap_entry_dn(entry)); - unsigned int count = array_count(&op->map->values); - i_assert(count > 0); - ARRAY_TYPE(const_string) resp_values; - p_array_init(&resp_values, op->pool, count + 1); + p_array_init(&resp_values, op->pool, array_count(&op->map->values) + 1); const char *attribute; array_foreach_elem(&op->map->values, attribute) { @@ -296,7 +256,7 @@ ldap_dict_lookup_cb_values(const struct ldap_entry *entry, struct dict_ldap_op * array_append_zero(&resp_values); array_pop_back(&resp_values); bool got_values = array_not_empty(&resp_values); - op->res.values = array_front(&resp_values); + op->res.values = got_values ? array_front(&resp_values) : NULL; op->res.value = got_values ? op->res.values[0] : NULL; op->res.ret = got_values ? 1 : 0; } @@ -396,6 +356,25 @@ void ldap_dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); */ +static int +ldap_dict_pattern_expand(const char *key, const char **value_r, void *_ctx, + const char **error_r) +{ + struct dict_ldap_op *op = _ctx; + *value_r = ""; + + const ARRAY_TYPE(const_string) *keys = &op->map->parsed_pattern_keys; + const char *const *value = array_lsearch(keys, &key, i_strcmp_p); + if (value != NULL) { + unsigned int index = array_ptr_to_idx(keys, value); + *value_r = array_idx_elem(&op->pattern_values, index); + return 0; + } + + *error_r = t_strdup_printf("pattern %s not found", key); + return -1; +} + static void ldap_dict_lookup_async(struct dict *dict, const struct dict_op_settings *set, @@ -405,10 +384,8 @@ void ldap_dict_lookup_async(struct dict *dict, struct ldap_search_input input; struct ldap_dict *ctx = (struct ldap_dict*)dict; struct dict_ldap_op *op; - const char *error; 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; @@ -417,30 +394,62 @@ void ldap_dict_lookup_async(struct dict *dict, op->txid = ctx->last_txid++; op->event = event_create(op->dict->dict.event); - /* key needs to be transformed into something else */ - ARRAY_TYPE(const_string) values; - t_array_init(&values, 8); - const struct dict_ldap_map_settings *map = ldap_dict_find_map(ctx, key, &values); + struct dict_ldap_vars_context *pattern_ctx = + p_new(op->pool, struct dict_ldap_vars_context, 1); + t_array_init(&pattern_ctx->values, 2); + pattern_ctx->private = str_begins_with(key, DICT_PATH_PRIVATE); + if (pattern_ctx->private) + pattern_ctx->username = set->username; + + const struct dict_ldap_map_settings *map = pattern_ctx->set = + ldap_dict_find_map(ctx, key, &pattern_ctx->values); if (map != NULL) { op->map = map; - /* build lookup */ - i_zero(&input); - input.base_dn = map->base; - input.scope = map->parsed_scope; - if (!ldap_dict_build_query(set, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0, query, &error)) { - op->res.error = error; + + static const struct var_expand_provider providers[] = { + { "pattern", ldap_dict_pattern_expand }, + { NULL, NULL } + }; + + struct var_expand_table *table = + p_new(op->pool, struct var_expand_table, 2); + table[0].key = "user"; + table[0].value = p_strdup(op->pool, set->username); + + struct var_expand_params *params = + p_new(op->pool, struct var_expand_params, 1); + params->escape_func = ldap_escape; + params->providers = providers; + params->context = op; + params->table = table; + event_set_ptr(op->event, SETTINGS_EVENT_VAR_EXPAND_PARAMS, params); + if (str_begins_with(key, DICT_PATH_PRIVATE)) + event_add_str(op->event, "user", set->username); + + struct dict_ldap_map_pre_settings *pre; + if (settings_get_filter(op->event, "dict_map", map->pattern, + &dict_ldap_map_pre_setting_parser_info, + 0, &pre, &op->res.error) < 0) { + + op->res.ret = -1; callback(&op->res, context); event_unref(&op->event); - pool_unref(&oppool); + pool_unref(&op->pool); return; } - input.filter = str_c(query); + + /* build lookup */ + i_zero(&input); + input.base_dn = map->base; + input.scope = map->parsed_scope; + input.filter = pre->filter; /* Guaranteed to be NULL-terminated by dict_ldap_map_settings_postcheck() */ input.attributes = array_front(&map->values); ctx->pending++; ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op); + settings_free(pre); } else { op->res.error = "no such key"; callback(&op->res, context);