]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dict-ldap: dict_ldap_map_settings - Var expand pattern variables in filter setting
authorMarco Bettini <marco.bettini@open-xchange.com>
Fri, 18 Oct 2024 14:04:41 +0000 (14:04 +0000)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Jan 2025 08:40:01 +0000 (10:40 +0200)
Also remove setting_define.username_attribute as it is no longer used.
Now the username must be included explicitly in the filter.

src/lib-dict-backend/dict-ldap-settings.c
src/lib-dict-backend/dict-ldap-settings.h
src/lib-dict-backend/dict-ldap.c

index 94fdb094677c822ea79d3c204abc074a96c71448..6f03e1043615873866f297a1164cd1d7cc04ec7a 100644 (file)
@@ -9,6 +9,7 @@
 #include "settings.h"
 #include "settings-parser.h"
 #include "dict-ldap-settings.h"
+#include "dict.h"
 
 #include <ctype.h>
 
 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);
        }
 
index 82d0865aba8a87eeae9b51fe82cec1c1b65e1638..fbdf4803cc1bbd2c317e7abaf4b48be123e252b8 100644 (file)
@@ -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,
index 3954a0386d325e43614e10daa43e2d4c5f2a0c96..196807e58e7bbb3bcbdb9ad7ed38f9c2291299bb 100644 (file)
@@ -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, &params, &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);