From: Marco Bettini Date: Fri, 25 Oct 2024 16:26:19 +0000 (+0000) Subject: dict-ldap: dict_ldap_map_settings - Var expand values X-Git-Tag: 2.4.0~117 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=29cb0f0b6a65e731337d4436738fdb2e57938abf;p=thirdparty%2Fdovecot%2Fcore.git dict-ldap: dict_ldap_map_settings - Var expand values --- diff --git a/src/lib-dict-backend/dict-ldap-settings.c b/src/lib-dict-backend/dict-ldap-settings.c index 6f03e10436..2c9e5f4275 100644 --- a/src/lib-dict-backend/dict-ldap-settings.c +++ b/src/lib-dict-backend/dict-ldap-settings.c @@ -15,6 +15,10 @@ /* */ #include "ldap-settings-parse.h" + +static bool +dict_ldap_map_settings_post_check(void *set, pool_t pool, const char **error_r); + /* */ #undef DEF @@ -27,13 +31,11 @@ static const struct setting_define dict_ldap_map_setting_defines[] = { DEF(STR, pattern), DEFN(STR, base, ldap_base), DEFN(ENUM, scope, ldap_scope), - DEF(BOOLLIST, values), SETTING_DEFINE_LIST_END }; static const struct dict_ldap_map_settings dict_ldap_map_default_settings = { .pattern = "", - .values = ARRAY_INIT, .base = "", .scope = "subtree:onelevel:base", }; @@ -71,6 +73,30 @@ const struct setting_parser_info dict_ldap_map_pre_setting_parser_info = { .pool_offset1 = 1 + offsetof(struct dict_ldap_map_pre_settings, pool), }; +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type("dict_map_"#name, name, struct dict_ldap_map_post_settings) + +static const struct setting_define dict_ldap_map_post_setting_defines[] = { + DEF(STR, value), + SETTING_DEFINE_LIST_END +}; + +static const struct dict_ldap_map_post_settings dict_ldap_map_post_default_settings = { + .value = "", +}; + +const struct setting_parser_info dict_ldap_map_post_setting_parser_info = { + .name = "dict_ldap_map_post", + + .defines = dict_ldap_map_post_setting_defines, + .defaults = &dict_ldap_map_post_default_settings, + .check_func = dict_ldap_map_settings_post_check, + + .struct_size = sizeof(struct dict_ldap_map_post_settings), + .pool_offset1 = 1 + offsetof(struct dict_ldap_map_post_settings, pool), +}; + #undef DEF #define DEF(type, name) \ SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, struct dict_ldap_settings) @@ -95,9 +121,57 @@ const struct setting_parser_info dict_ldap_setting_parser_info = { .pool_offset1 = 1 + offsetof(struct dict_ldap_settings, pool), }; +/* */ + +static bool +dict_ldap_map_settings_post_check(void *_set, pool_t pool, + const char **error_r ATTR_UNUSED) +{ + struct dict_ldap_map_post_settings *set = _set; + p_array_init(&set->values, pool, 1); + if (*set->value != '\0') + array_push_back(&set->values, &set->value); + return TRUE; +} + +/* */ + +static int ldap_parse_attributes(struct dict_ldap_map_settings *set, + struct dict_ldap_map_post_settings *post, + const char **error_r) +{ + const char *value; + p_array_init(&set->parsed_attributes, set->pool, 2); + array_foreach_elem(&post->values, value) { + struct var_expand_program *prog; + + if (var_expand_program_create(value, &prog, error_r) < 0) { + *error_r = t_strdup_printf("Invalid ldap_map_value %s: %s", + value, *error_r); + return -1; + } + + const char *const *vars = var_expand_program_variables(prog); + for (; *vars != NULL; vars++) { + const char *ldap_attr; + if (!str_begins(*vars, "ldap:", &ldap_attr) && + !str_begins(*vars, "ldap_multi:", &ldap_attr)) + continue; + + /* When we free program, this name would be invalid, + so dup it here. */ + ldap_attr = p_strdup(set->pool, ldap_attr); + array_push_back(&set->parsed_attributes, &ldap_attr); + } + var_expand_program_free(&prog); + } + return 0; +} + static int dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set, struct dict_ldap_map_pre_settings *pre, + struct dict_ldap_map_post_settings *post, const char **error_r) { if (!str_begins_with(pre->filter, "(")) { @@ -114,12 +188,9 @@ dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set, return -1; } - if (array_is_empty(&set->values)) { + if (array_is_empty(&post->values)) { *error_r = "ldap_map_value not set"; return -1; - } else { - array_append_zero(&set->values); - array_pop_back(&set->values); } if (ldap_parse_scope(set->scope, &set->parsed_scope) < 0) { @@ -128,7 +199,7 @@ dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set, return -1; } - return 0; + return ldap_parse_attributes(set, post, error_r); } static const char *pattern_read_name(const char **pattern) @@ -204,26 +275,34 @@ dict_ldap_settings_parse_maps(struct event *event, struct dict_ldap_settings *se array_foreach_elem(&set->maps, name) { struct dict_ldap_map_settings *map = NULL; struct dict_ldap_map_pre_settings *pre = NULL; + struct dict_ldap_map_post_settings *post = NULL; if (settings_get_filter(event, "dict_map", name, &dict_ldap_map_setting_parser_info, 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) { + &pre, error_r) < 0 || + settings_get_filter(event, "dict_map", name, + &dict_ldap_map_post_setting_parser_info, + SETTINGS_GET_FLAG_NO_EXPAND, + &post, error_r) < 0) { *error_r = t_strdup_printf("Failed to get dict_map %s: %s", name, *error_r); settings_free(map); settings_free(pre); + settings_free(post); return -1; } - if (dict_ldap_map_settings_postcheck(map, pre, error_r) < 0) { + if (dict_ldap_map_settings_postcheck(map, pre, post, error_r) < 0) { settings_free(map); settings_free(pre); + settings_free(post); return -1; } settings_free(pre); + settings_free(post); dict_ldap_settings_parse_pattern(map); chain_ref(set->pool, map->pool); diff --git a/src/lib-dict-backend/dict-ldap-settings.h b/src/lib-dict-backend/dict-ldap-settings.h index fbdf4803cc..ffae6e4f16 100644 --- a/src/lib-dict-backend/dict-ldap-settings.h +++ b/src/lib-dict-backend/dict-ldap-settings.h @@ -5,12 +5,13 @@ struct dict_ldap_map_settings { pool_t pool; const char *pattern; - ARRAY_TYPE(const_string) values; const char *base; const char *scope; /* parsed */ + ARRAY_TYPE(const_string) parsed_attributes; + /* attributes sorted by the position in parsed_pattern. */ ARRAY_TYPE(const_string) parsed_pattern_keys; int parsed_scope; @@ -24,6 +25,18 @@ struct dict_ldap_map_pre_settings { const char *filter; }; +struct dict_ldap_map_post_settings { + pool_t pool; + const char *value; + + /* parsed */ + + /* This is preliminary support for supporting multiple values. + For now the array contains only the single value coming + from 'value' above. */ + ARRAY_TYPE(const_string) values; +}; + struct dict_ldap_settings { pool_t pool; ARRAY_TYPE(const_string) maps; @@ -34,6 +47,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_map_post_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 196807e58e..660ca47e0b 100644 --- a/src/lib-dict-backend/dict-ldap.c +++ b/src/lib-dict-backend/dict-ldap.c @@ -21,18 +21,22 @@ static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= "; struct ldap_dict; +struct key_value { + /* pre lower-cased */ + const char *key_lcase; -struct dict_ldap_vars_context { - const struct dict_ldap_map_settings *set; - ARRAY_TYPE(const_string) values; - const char *username; - bool private; + const char *value; }; struct dict_ldap_op { struct ldap_dict *dict; struct event *event; const struct dict_ldap_map_settings *map; + ARRAY_TYPE(const_string) pattern_values; + ARRAY(struct key_value) attribute_values; + + const char *username; + bool private; pool_t pool; unsigned long txid; struct dict_lookup_result res; @@ -238,27 +242,43 @@ 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)); - ARRAY_TYPE(const_string) resp_values; - p_array_init(&resp_values, op->pool, array_count(&op->map->values) + 1); - const char *attribute; - array_foreach_elem(&op->map->values, attribute) { + array_foreach_elem(&op->map->parsed_attributes, attribute) { const char *const *values = ldap_entry_get_attribute(entry, attribute); bool no_attribute = values == NULL; e_debug(op->event, "%s attribute %s", no_attribute ? "dit not get" : "got", attribute); - if (no_attribute && array_is_empty(&resp_values)) - break; - const char *value = no_attribute ? "" : p_strdup(op->pool, values[0]); + if (no_attribute) + continue; + struct key_value *kv = array_append_space(&op->attribute_values); + kv->key_lcase = p_strdup(op->pool, t_str_lcase(attribute)); + kv->value = p_strdup(op->pool, values[0]); + } + + struct dict_ldap_map_post_settings *post; + if (settings_get_filter(op->event, "dict_map", op->map->pattern, + &dict_ldap_map_post_setting_parser_info, + 0, &post, &op->res.error) < 0) { + op->res.ret = -1; + return; + } + + ARRAY_TYPE(const_string) resp_values; + p_array_init(&resp_values, op->pool, array_count(&post->values) + 1); + + const char *value; + array_foreach_elem(&post->values, value) { + value = p_strdup(op->pool, value); array_push_back(&resp_values, &value); } + settings_free(post); array_append_zero(&resp_values); array_pop_back(&resp_values); bool got_values = array_not_empty(&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; + op->res.ret = 1; } static void @@ -356,6 +376,29 @@ void ldap_dict_atomic_inc(struct dict_transaction_context *ctx, const char *key, long long diff); */ +static int +ldap_dict_ldap_expand(const char *key, const char **value_r, void *_ctx, + const char **error_r ATTR_UNUSED) +{ + struct dict_ldap_op *op = _ctx; + *value_r = ""; + + if (array_not_empty(&op->attribute_values)) { + key = t_str_lcase(key); + + const struct key_value *attr; + array_foreach(&op->attribute_values, attr) { + if (strcmp(key, attr->key_lcase) == 0) { + *value_r = attr->value; + return 0; + } + } + } + + *error_r = t_strdup_printf("ldap attribute %s not found", key); + return -1; +} + static int ldap_dict_pattern_expand(const char *key, const char **value_r, void *_ctx, const char **error_r) @@ -393,22 +436,15 @@ void ldap_dict_lookup_async(struct dict *dict, op->callback_ctx = context; op->txid = ctx->last_txid++; op->event = event_create(op->dict->dict.event); + op->private = str_begins_with(key, DICT_PATH_PRIVATE); + op->username = set->username; + op->map = ldap_dict_find_map(ctx, key, &op->pattern_values); + p_array_init(&op->attribute_values, op->pool, 2); - 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; - + if (op->map != NULL) { static const struct var_expand_provider providers[] = { { "pattern", ldap_dict_pattern_expand }, + { "ldap", ldap_dict_ldap_expand}, { NULL, NULL } }; @@ -424,11 +460,8 @@ void ldap_dict_lookup_async(struct dict *dict, 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, + if (settings_get_filter(op->event, "dict_map", op->map->pattern, &dict_ldap_map_pre_setting_parser_info, 0, &pre, &op->res.error) < 0) { @@ -441,12 +474,14 @@ void ldap_dict_lookup_async(struct dict *dict, /* build lookup */ i_zero(&input); - input.base_dn = map->base; - input.scope = map->parsed_scope; input.filter = pre->filter; + input.base_dn = op->map->base; + input.scope = op->map->parsed_scope; /* Guaranteed to be NULL-terminated by dict_ldap_map_settings_postcheck() */ - input.attributes = array_front(&map->values); + input.attributes = + array_is_empty(&op->map->parsed_attributes) ? NULL : + array_front(&op->map->parsed_attributes); ctx->pending++; ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op); settings_free(pre);