]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: ldap - Split pre/post settings and finish the conversion
authorMarco Bettini <marco.bettini@open-xchange.com>
Mon, 8 Jul 2024 14:40:35 +0000 (14:40 +0000)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Jan 2025 08:39:59 +0000 (10:39 +0200)
src/auth/db-ldap-settings.c
src/auth/db-ldap-settings.h
src/auth/db-ldap.c
src/auth/db-ldap.h
src/auth/passdb-ldap.c
src/auth/userdb-ldap.c
src/lib-ldap/ldap-connection.c

index c9bc857ebd37efa514ec31b038b802408ef557e2..5f07218e3d9e90722c67f2aa68e3892f92f8b8da 100644 (file)
@@ -14,8 +14,6 @@ static bool ldap_setting_check(void *_set, pool_t pool, const char **error_r);
 #undef DEF
 #define DEF(type, name) \
        SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, struct ldap_settings)
-#define DEFN(type, field, name) \
-       SETTING_DEFINE_STRUCT_##type(#name, field, struct ldap_settings)
 
 static const struct setting_define ldap_setting_defines[] = {
        { .type = SET_FILTER_NAME, .key = "passdb_ldap", },
@@ -24,20 +22,14 @@ static const struct setting_define ldap_setting_defines[] = {
        DEF(STR, uris),
        DEF(STR, auth_dn),
        DEF(STR, auth_dn_password),
-       DEFN(BOOL, passdb_ldap_bind, passdb_ldap_bind),
-       DEFN(STR, passdb_ldap_bind_userdn, passdb_ldap_bind_userdn),
        DEF(STR, auth_sasl_mechanism),
        DEF(STR, auth_sasl_realm),
        DEF(STR, auth_sasl_authz_id),
        DEF(BOOL, starttls),
        DEF(STR, deref),
        DEF(STR, scope),
-       DEF(STR, base),
        DEF(UINT, version),
        DEF(STR, debug_level),
-       DEF(STR, filter),
-       DEF(STRLIST, iterate_attrs),
-       DEF(STR, iterate_filter),
        SETTING_DEFINE_LIST_END
 };
 
@@ -46,20 +38,14 @@ static const struct ldap_settings ldap_default_settings = {
        .uris = "",
        .auth_dn = "",
        .auth_dn_password = "",
-       .passdb_ldap_bind = FALSE,
-       .passdb_ldap_bind_userdn = "",
        .auth_sasl_mechanism = "",
        .auth_sasl_realm = "",
        .auth_sasl_authz_id = "",
        .starttls = FALSE,
        .deref = "never",
        .scope = "subtree",
-       .base = "",
        .version = 3,
        .debug_level = "0",
-       .filter = "",
-       .iterate_attrs = ARRAY_INIT,
-       .iterate_filter = "",
 };
 
 static const struct setting_keyvalue ldap_default_settings_keyvalue[] = {
@@ -81,6 +67,67 @@ const struct setting_parser_info ldap_setting_parser_info = {
        .pool_offset1 = 1 + offsetof(struct ldap_settings, pool),
 };
 
+#undef DEF
+#undef DEFN
+#define DEF(type, field) \
+       SETTING_DEFINE_STRUCT_##type("ldap_"#field, field, struct ldap_pre_settings)
+#define DEFN(type, field, name) \
+       SETTING_DEFINE_STRUCT_##type(#name, field, struct ldap_pre_settings)
+
+static const struct setting_define ldap_pre_setting_defines[] = {
+       { .type = SET_FILTER_NAME, .key = "passdb_ldap", },
+       { .type = SET_FILTER_NAME, .key = "userdb_ldap", },
+       DEF(STR, base),
+       DEFN(BOOL, passdb_ldap_bind, passdb_ldap_bind),
+       DEFN(STR, passdb_ldap_bind_userdn, passdb_ldap_bind_userdn),
+       DEF(STR, filter),
+       DEF(STR, iterate_filter),
+       SETTING_DEFINE_LIST_END
+};
+
+static const struct ldap_pre_settings ldap_pre_default_settings = {
+       .base = "",
+       .passdb_ldap_bind = FALSE,
+       .passdb_ldap_bind_userdn = "",
+       .filter = "",
+       .iterate_filter = "",
+};
+
+const struct setting_parser_info ldap_pre_setting_parser_info = {
+       .name = "auth_ldap_pre",
+
+       .defines = ldap_pre_setting_defines,
+       .defaults = &ldap_pre_default_settings,
+
+       .struct_size = sizeof(struct ldap_pre_settings),
+       .pool_offset1 = 1 + offsetof(struct ldap_pre_settings, pool),
+};
+
+#undef DEF
+#define DEF(type, field) \
+       SETTING_DEFINE_STRUCT_##type("ldap_"#field, field, struct ldap_post_settings)
+
+static const struct setting_define ldap_post_setting_defines[] = {
+       { .type = SET_FILTER_NAME, .key = "passdb_ldap", },
+       { .type = SET_FILTER_NAME, .key = "userdb_ldap", },
+       DEF(STRLIST, iterate_fields),
+       SETTING_DEFINE_LIST_END
+};
+
+static const struct ldap_post_settings ldap_post_default_settings = {
+       .iterate_fields = ARRAY_INIT,
+};
+
+const struct setting_parser_info ldap_post_setting_parser_info = {
+       .name = "auth_ldap_post",
+
+       .defines = ldap_post_setting_defines,
+       .defaults = &ldap_post_default_settings,
+
+       .struct_size = sizeof(struct ldap_post_settings),
+       .pool_offset1 = 1 + offsetof(struct ldap_post_settings, pool),
+};
+
 /* <settings checks> */
 
 static int ldap_parse_deref(const char *str, int *ref_r)
@@ -157,11 +204,6 @@ static bool ldap_setting_check(void *_set, pool_t pool ATTR_UNUSED,
 
 int ldap_setting_post_check(const struct ldap_settings *set, const char **error_r)
 {
-       if (*set->base == '\0') {
-               *error_r = "No ldap_base given";
-               return -1;
-       }
-
        if (*set->uris == '\0' && *set->hosts == '\0') {
                *error_r = "Neither ldap_uris nor ldap_hosts set";
                return -1;
@@ -180,3 +222,13 @@ int ldap_setting_post_check(const struct ldap_settings *set, const char **error_
 
        return 0;
 }
+
+int ldap_pre_settings_pre_check(const struct ldap_pre_settings *set, const char **error_r)
+{
+       if (*set->base == '\0') {
+               *error_r = "No ldap_base given";
+               return -1;
+       }
+
+       return 0;
+}
index f6973db45d3aaf294ff459948d72d35b55f0463a..9cba46e615bda8e986d0038f9ee297b74efd9f08 100644 (file)
@@ -8,7 +8,6 @@ struct ldap_settings {
        const char *uris;
        const char *auth_dn;
        const char *auth_dn_password;
-       const char *passdb_ldap_bind_userdn;
 
        const char *auth_sasl_mechanism;
        const char *auth_sasl_realm;
@@ -16,21 +15,14 @@ struct ldap_settings {
 
        const char *deref;
        const char *scope;
-       const char *base;
 
        const char *debug_level;
 
-       const char *filter;
-       const char *iterate_filter;
-
-       ARRAY_TYPE(const_string) iterate_attrs;
-
        unsigned int version;
 
        uid_t uid;
        gid_t gid;
 
-       bool passdb_ldap_bind;
        bool starttls;
 
        /* parsed */
@@ -38,7 +30,31 @@ struct ldap_settings {
        int parsed_scope;
 };
 
+struct ldap_pre_settings {
+       pool_t pool;
+
+       /* shared: */
+       const char *base;
+       const char *filter;
+
+       /* passdb: */
+       bool passdb_ldap_bind;
+       const char *passdb_ldap_bind_userdn;
+
+       /* userdb: */
+       const char *iterate_filter;
+};
+
+struct ldap_post_settings {
+       pool_t pool;
+       ARRAY_TYPE(const_string) iterate_fields;
+};
+
 extern const struct setting_parser_info ldap_setting_parser_info;
+extern const struct setting_parser_info ldap_pre_setting_parser_info;
+extern const struct setting_parser_info ldap_post_setting_parser_info;
+
 int ldap_setting_post_check(const struct ldap_settings *set, const char **error_r);
+int ldap_pre_settings_pre_check(const struct ldap_pre_settings *set, const char **error_r);
 
 #endif
index b2e60677f8b2fea678ae3bd1cf56eb074b8f6691..4201956226ff641dee9f86694998cd49f8eb978f 100644 (file)
@@ -50,6 +50,7 @@
 #endif
 
 #define DB_LDAP_REQUEST_MAX_ATTEMPT_COUNT 3
+#define DB_LDAP_ATTR_DN "~dn"
 
 static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= ";
 
@@ -67,8 +68,7 @@ struct db_ldap_result_iterate_context {
        pool_t pool;
 
        struct ldap_request *ldap_request;
-       const ARRAY_TYPE(ldap_field) *attr_map;
-       unsigned int attr_idx;
+       char **attr_next;
 
        /* attribute name => value */
        HASH_TABLE(char *, struct db_ldap_value *) ldap_attrs;
@@ -355,13 +355,13 @@ static int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
 {
        if (ret == LDAP_SERVER_DOWN) {
                e_error(conn->event, "Can't connect to server: %s",
-                       conn->set->uris != NULL ?
+                       *conn->set->uris != '\0' ?
                        conn->set->uris : conn->set->hosts);
                return -1;
        }
        if (ret != LDAP_SUCCESS) {
                e_error(conn->event, "binding failed (dn %s): %s",
-                       conn->set->auth_dn == NULL ? "(none)" : conn->set->auth_dn,
+                       *conn->set->auth_dn == '\0' ? "(none)" : conn->set->auth_dn,
                        ldap_get_error(conn));
                return -1;
        }
@@ -905,7 +905,7 @@ static void db_ldap_init_ld(struct ldap_connection *conn)
 {
        int ret;
 
-       if (conn->set->uris != NULL) {
+       if (*conn->set->uris != '\0') {
 #ifdef LDAP_HAVE_INITIALIZE
                ret = ldap_initialize(&conn->ld, conn->set->uris);
                if (ret != LDAP_SUCCESS) {
@@ -948,7 +948,7 @@ int db_ldap_connect(struct ldap_connection *conn)
                ret = ldap_start_tls_s(conn->ld, NULL, NULL);
                if (ret != LDAP_SUCCESS) {
                        if (ret == LDAP_OPERATIONS_ERROR &&
-                           conn->set->uris != NULL &&
+                           *conn->set->uris != '\0' &&
                            str_begins_with(conn->set->uris, "ldaps:")) {
                                i_fatal("LDAP: Don't use both ldap_starttls=yes and ldaps URI");
                        }
@@ -1072,15 +1072,14 @@ db_ldap_field_find(const char *data, void *context,
        return 1;
 }
 
-void db_ldap_set_attrs(struct ldap_connection *conn,
-                      const ARRAY_TYPE(const_string) *attrlist,
-                      char ***attr_names_r, ARRAY_TYPE(ldap_field) *attr_map,
-                      const char *skip_attr)
+void db_ldap_get_attribute_names(struct ldap_connection *conn,
+                                const ARRAY_TYPE(const_string) *attrlist,
+                                char ***attr_names_r,
+                                const char *skip_attr)
 {
        static struct var_expand_func_table var_funcs_table[] = {
                { "ldap", db_ldap_field_find },
                { "ldap_multi", db_ldap_field_find },
-               { "ldap_ptr", db_ldap_field_find },
                { NULL, NULL }
        };
 
@@ -1093,86 +1092,21 @@ void db_ldap_set_attrs(struct ldap_connection *conn,
        string_t *tmp_str = t_str_new(128);
 
        for (unsigned int index = 0; index < count; ) {
-               char *key = p_strdup(conn->pool, array_idx_elem(attrlist, index++));
-               char *value = p_strdup(conn->pool, array_idx_elem(attrlist, index++));
-               char *ldap_attr, *name;
-
-               switch (*value) {
-               case '\0':
-                       ldap_attr = key;
-                       name = key;
-                       break;
-               default:
-                       ldap_attr = key;
-                       name = value;
-               }
+               const char *name = array_idx_elem(attrlist, index++);
+               const char *value = array_idx_elem(attrlist, index++);
 
-               char *templ = strchr(name, '=');
-               if (templ == NULL) {
-                       if (*ldap_attr == '\0') {
-                               /* =foo static value */
-                               templ = "";
-                       }
-               } else {
-                       const char *error ATTR_UNUSED;
-                       *templ++ = '\0';
-                       str_truncate(tmp_str, 0);
-                       if (var_expand_with_funcs(tmp_str, templ, NULL,
-                                                 var_funcs_table, &ctx, &error) <= 0) {
-                               /* This var_expand_with_funcs call fills the
-                                * ldap_field_find_context in ctx, but the
-                                * resulting string_t is not used, and the
-                                * return value or error_r is not checked since
-                                * it gives errors for non-ldap variable
-                                * expansions. */
-                       }
-                       if (strchr(templ, '%') == NULL) {
-                               /* backwards compatibility:
-                                  attr=name=prefix means same as
-                                  attr=name=prefix%$ when %vars are missing */
-                               templ = p_strconcat(conn->pool, templ,
-                                                   "%$", NULL);
-                       }
-               }
+               if (skip_attr != NULL && strcmp(skip_attr, name) == 0)
+                       continue;
 
-               if (*name == '\0')
-                       e_error(conn->event, "Invalid empty attribute name");
-               else if (skip_attr == NULL || strcmp(skip_attr, name) != 0) {
-                       struct ldap_field *field = array_append_space(attr_map);
-                       if (name[0] == '!' && name == ldap_attr) {
-                               /* !ldapAttr */
-                               name = "";
-                               i_assert(ldap_attr[0] == '!');
-                               ldap_attr++;
-                               field->skip = TRUE;
-                       }
-                       field->name = name;
-                       field->value = templ;
-                       field->ldap_attr_name = ldap_attr;
-                       if (*ldap_attr != '\0') {
-                               /* root request's attribute */
-                               array_push_back(&ctx.attr_names, &ldap_attr);
-                       }
-               }
+               const char *error ATTR_UNUSED;
+               str_truncate(tmp_str, 0);
+
+               (void)var_expand_with_funcs(tmp_str, value, NULL, var_funcs_table, &ctx, &error);
        }
        array_append_zero(&ctx.attr_names);
        *attr_names_r = array_front_modifiable(&ctx.attr_names);
 }
 
-static const struct var_expand_table *
-db_ldap_value_get_var_expand_table(struct auth_request *auth_request,
-                                  const char *ldap_value)
-{
-       struct var_expand_table *table;
-       unsigned int count = 1;
-
-       table = auth_request_get_var_expand_table_full(auth_request,
-                       auth_request->fields.user, NULL, &count);
-       table[0].key = '$';
-       table[0].value = ldap_value;
-       return table;
-}
-
 #define IS_LDAP_ESCAPED_CHAR(c) \
        ((((unsigned char)(c)) & 0x80) != 0 || strchr(LDAP_ESCAPE_CHARS, (c)) != NULL)
 
@@ -1265,7 +1199,7 @@ db_ldap_result_iterate_init_full(struct ldap_connection *conn,
        ctx = p_new(pool, struct db_ldap_result_iterate_context, 1);
        ctx->pool = pool;
        ctx->ldap_request = &ldap_request->request;
-       ctx->attr_map = ldap_request->attr_map;
+       ctx->attr_next = ldap_request->attributes;
        ctx->skip_null_values = skip_null_values;
        hash_table_create(&ctx->ldap_attrs, pool, 0, strcase_hash, strcasecmp);
        ctx->var = str_new(ctx->pool, 256);
@@ -1295,50 +1229,6 @@ db_ldap_result_iterate_init(struct ldap_connection *conn,
                                                skip_null_values);
 }
 
-static const char *db_ldap_field_get_default(const char *data)
-{
-       const char *p;
-
-       p = i_strchr_to_next(data, ':');
-       if (p == NULL)
-               return "";
-       else {
-               /* default value given */
-               return p;
-       }
-}
-
-static int
-db_ldap_field_expand(const char *data, void *context,
-                    const char **value_r, const char **error_r ATTR_UNUSED)
-{
-       struct db_ldap_result_iterate_context *ctx = context;
-       struct db_ldap_value *ldap_value;
-       const char *field_name = t_strcut(data, ':');
-
-       ldap_value = hash_table_lookup(ctx->ldap_attrs, field_name);
-       if (ldap_value == NULL) {
-               /* requested ldap attribute wasn't returned at all */
-               str_printfa(ctx->debug, "; %s missing", field_name);
-               *value_r = db_ldap_field_get_default(data);
-               return 1;
-       }
-       ldap_value->used = TRUE;
-
-       if (ldap_value->values[0] == NULL) {
-               /* no value for ldap attribute */
-               *value_r = db_ldap_field_get_default(data);
-               return 1;
-       }
-       if (ldap_value->values[1] != NULL) {
-               e_warning(authdb_event(ctx->ldap_request->auth_request),
-                         "Multiple values found for '%s', using value '%s'",
-                         field_name, ldap_value->values[0]);
-       }
-       *value_r = ldap_value->values[0];
-       return 1;
-}
-
 void db_ldap_field_multi_expand_parse_data(
        const char *data, const char **field_name_r,
        const char **separator_r, const char **default_r)
@@ -1388,12 +1278,17 @@ void db_ldap_field_multi_expand_parse_data(
        *default_r = ptr;
 }
 
+const char *db_ldap_attribute_as_multi(const char *name)
+{
+       return t_strconcat(DB_LDAP_ATTR_MULTI_PREFIX, name, NULL);
+}
+
 static int
 db_ldap_field_multi_expand(const char *data, void *context,
                           const char **value_r, const char **error_r ATTR_UNUSED)
 {
-       struct db_ldap_result_iterate_context *ctx = context;
-       struct db_ldap_value *ldap_value;
+       struct db_ldap_field_expand_context *ctx = context;
+       struct auth_fields *fields = ctx->fields;
 
        const char *field_name;
        const char *field_separator;
@@ -1402,156 +1297,121 @@ db_ldap_field_multi_expand(const char *data, void *context,
        db_ldap_field_multi_expand_parse_data(data, &field_name,
                                              &field_separator,
                                              &field_default);
-       *value_r = field_default;
 
-       ldap_value = hash_table_lookup(ctx->ldap_attrs, field_name);
-       if (ldap_value == NULL) {
-               /* requested ldap attribute wasn't returned at all */
-               str_printfa(ctx->debug, "; %s missing", field_name);
-               return 1;
-       }
-       ldap_value->used = TRUE;
+       const char *value = auth_fields_find(fields,
+                                            db_ldap_attribute_as_multi(field_name));
+       if (value == NULL || *value == '\0')
+               value = auth_fields_find(fields, field_name);
 
-       if (ldap_value->values[0] == NULL) {
-               /* no value for ldap attribute */
-               return 1;
+       if (value == NULL || *value == '\0')
+               value = field_default == NULL ? "" : field_default;
+       else {
+               const char **entries = t_strsplit(value, DB_LDAP_ATTR_SEPARATOR);
+               value = t_strarray_join(entries, field_separator);
        }
-
-       *value_r = t_strarray_join(ldap_value->values, field_separator);
+       *value_r = value;
        return 1;
 }
 
 static int
-db_ldap_field_ptr_expand(const char *data, void *context,
-                        const char **value_r, const char **error_r)
+db_ldap_field_single_expand(const char *data ATTR_UNUSED, void *context,
+                           const char **value_r, const char **error_r ATTR_UNUSED)
 {
-       struct db_ldap_result_iterate_context *ctx = context;
-       const char *field_name, *suffix;
+       struct db_ldap_field_expand_context *ctx = context;
+       struct auth_fields *fields = ctx->fields;
+       const char *field_default = strchr(data, ':');
+       const char *field_name = field_default == NULL ? data : t_strdup_until(data, field_default);
 
-       suffix = strchr(t_strcut(data, ':'), '@');
-       if (db_ldap_field_expand(data, ctx, &field_name, error_r) <= 0)
-               i_unreached();
-       if (field_name[0] == '\0') {
-               *value_r = "";
-               return 1;
+       *value_r = NULL;
+       if (fields != NULL)
+               *value_r = auth_fields_find(fields, field_name);
+
+       if (*value_r == NULL || **value_r == '\0')
+               *value_r = field_default == NULL ? "" : field_default + 1;
+       else if (auth_fields_find(fields,
+                                 db_ldap_attribute_as_multi(field_name)) != NULL) {
+               e_warning(ctx->event, "Multiple values found for '%s': "
+                                     "using value '%s'", field_name, *value_r);
        }
-       field_name = t_strconcat(field_name, suffix, NULL);
-       return db_ldap_field_expand(field_name, ctx, value_r, error_r);
+
+       return 1;
 }
 
 static int
-db_ldap_field_dn_expand(const char *data ATTR_UNUSED, void *context ATTR_UNUSED,
+db_ldap_field_dn_expand(const char *data ATTR_UNUSED, void *context,
                         const char **value_r, const char **error_r ATTR_UNUSED)
 {
-       struct db_ldap_result_iterate_context *ctx = context;
-       char *dn = ldap_get_dn(ctx->ld, ctx->ldap_msg);
-       *value_r = t_strdup(dn);
-       ldap_memfree(dn);
+       struct db_ldap_field_expand_context *ctx = context;
+       struct auth_fields *fields = ctx->fields;
+       *value_r = auth_fields_find(fields, DB_LDAP_ATTR_DN);
        return 1;
 }
 
-static struct var_expand_func_table ldap_var_funcs_table[] = {
-       { "ldap", db_ldap_field_expand },
+const struct var_expand_func_table db_ldap_field_expand_fn_table[] = {
+       { "ldap",       db_ldap_field_single_expand },
        { "ldap_multi", db_ldap_field_multi_expand },
-       { "ldap_ptr", db_ldap_field_ptr_expand },
-       { "ldap_dn", db_ldap_field_dn_expand },
+       { "ldap_dn",    db_ldap_field_dn_expand },
        { NULL, NULL }
 };
 
-static const char *const *
-db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx,
-                           const struct ldap_field *field,
-                           struct db_ldap_value *ldap_value)
+struct auth_fields *
+ldap_query_get_fields(pool_t pool,
+                     struct ldap_connection *conn,
+                     struct ldap_request_search *ldap_request,
+                     LDAPMessage *res, bool skip_null_values)
 {
-       const struct var_expand_table *var_table;
-       const char *const *values, *error;
+       struct auth_fields *fields = auth_fields_init(pool);
+       struct db_ldap_result_iterate_context *ldap_iter;
+       const char *name, *const *values;
 
-       if (ldap_value != NULL)
-               values = ldap_value->values;
-       else {
-               /* LDAP attribute doesn't exist */
-               ctx->val_1_arr[0] = NULL;
-               values = ctx->val_1_arr;
-       }
+       const char *dn = ldap_get_dn(conn->ld, res);
+       auth_fields_add(fields, DB_LDAP_ATTR_DN, dn, 0);
 
-       if (field->value == NULL) {
-               /* use the LDAP attribute's value */
-       } else {
-               /* template */
-               if (values[0] == NULL && *field->ldap_attr_name != '\0') {
-                       /* ldapAttr=key=template%$, but ldapAttr doesn't
-                          exist. */
-                       return values;
-               }
+       ldap_iter = db_ldap_result_iterate_init(conn, ldap_request, res,
+                                               skip_null_values);
+       while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) {
+               auth_fields_add(fields, name, values[0], 0);
                if (values[0] != NULL && values[1] != NULL) {
-                       e_warning(authdb_event(ctx->ldap_request->auth_request),
-                                 "Multiple values found for '%s', "
-                                 "using value '%s'",
-                                 field->name, values[0]);
-               }
-
-               /* do this lookup separately for each expansion, because:
-                  1) the values are allocated from data stack
-                  2) if "user" field is updated, we want %u/%n/%d updated
-                     (and less importantly the same for other variables) */
-               var_table = db_ldap_value_get_var_expand_table(
-                       ctx->ldap_request->auth_request, values[0]);
-               if (var_expand_with_funcs(ctx->var, field->value, var_table,
-                                         ldap_var_funcs_table, ctx, &error) <= 0) {
-                       e_warning(authdb_event(ctx->ldap_request->auth_request),
-                                 "Failed to expand template %s: %s",
-                                 field->value, error);
+                       const char *mname = db_ldap_attribute_as_multi(name);
+                       const char *mvalue = t_strarray_join(values, DB_LDAP_ATTR_SEPARATOR);
+                       auth_fields_add(fields, mname, mvalue, 0);
                }
-               ctx->val_1_arr[0] = str_c(ctx->var);
-               values = ctx->val_1_arr;
        }
-       return values;
+       db_ldap_result_iterate_deinit(&ldap_iter);
+       return fields;
+}
+
+static const char *const *
+db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx,
+                           struct db_ldap_value *ldap_value)
+{
+       if (ldap_value != NULL && ldap_value->values[0] != NULL)
+               return ldap_value->values;
+
+       /* LDAP attribute doesn't exist */
+       ctx->val_1_arr[0] = "";
+       return ctx->val_1_arr;
 }
 
 bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx,
                                 const char **name_r,
                                 const char *const **values_r)
 {
-       const struct var_expand_table *tab;
-       const struct ldap_field *field;
-       struct db_ldap_value *ldap_value;
-       unsigned int pos;
-       const char *error;
+       const char *name = *ctx->attr_next;
+       if (name == NULL)
+               return FALSE;
 
-       do {
-               if (ctx->attr_idx == array_count(ctx->attr_map))
-                       return FALSE;
-               field = array_idx(ctx->attr_map, ctx->attr_idx++);
-       } while (field->skip);
+       ctx->attr_next++;
 
-       ldap_value = *field->ldap_attr_name == '\0' ? NULL :
-               hash_table_lookup(ctx->ldap_attrs, field->ldap_attr_name);
+       struct db_ldap_value *ldap_value = hash_table_lookup(ctx->ldap_attrs, name);
        if (ldap_value != NULL)
                ldap_value->used = TRUE;
-       else if (*field->ldap_attr_name != '\0')
-               str_printfa(ctx->debug, "; %s missing", field->ldap_attr_name);
+       else
+               str_printfa(ctx->debug, "; %s missing", name);
 
        str_truncate(ctx->var, 0);
-       *values_r = db_ldap_result_return_value(ctx, field, ldap_value);
-
-       if (strchr(field->name, '%') == NULL)
-               *name_r = field->name;
-       else {
-               /* expand %variables also for LDAP name fields. we'll use the
-                  same ctx->var, which may already contain the value. */
-               str_append_c(ctx->var, '\0');
-               pos = str_len(ctx->var);
-
-               tab = auth_request_get_var_expand_table(
-                       ctx->ldap_request->auth_request, NULL);
-               if (var_expand_with_funcs(ctx->var, field->name, tab,
-                                         ldap_var_funcs_table, ctx, &error) <= 0) {
-                       e_warning(authdb_event(ctx->ldap_request->auth_request),
-                                 "Failed to expand %s: %s", field->name, error);
-               }
-               *name_r = str_c(ctx->var) + pos;
-       }
-
+       *name_r = name;
+       *values_r = db_ldap_result_return_value(ctx, ldap_value);
        if (ctx->skip_null_values && (*values_r)[0] == NULL) {
                /* no values. don't confuse the caller with this reply. */
                return db_ldap_result_iterate_next(ctx, name_r, values_r);
index 8de5bb74ed454755bb80513b054f424248adbbb2..235dd008699f619d3267d8bb33ff915c67644735 100644 (file)
 #define DB_LDAP_IDLE_RECONNECT_SECS 60
 
 #include <ldap.h>
+#include "var-expand.h"
 #include "db-ldap-settings.h"
 
+#define DB_LDAP_ATTR_MULTI_PREFIX "+"
+#define DB_LDAP_ATTR_SEPARATOR "\001"
+
 struct auth_request;
 struct ldap_connection;
 struct ldap_request;
@@ -81,8 +85,7 @@ struct ldap_request_search {
 
        const char *base;
        const char *filter;
-       char **attributes; /* points to pass_attr_names / user_attr_names */
-       const ARRAY_TYPE(ldap_field) *attr_map;
+       char **attributes; /* points to (pass|user) module attributes */
 
        struct db_ldap_result *result;
        ARRAY(struct ldap_request_named_result) named_results;
@@ -137,18 +140,24 @@ struct ldap_connection {
        time_t last_reply_stamp;
 
        char **pass_attr_names, **user_attr_names, **iterate_attr_names;
-       ARRAY_TYPE(ldap_field) pass_attr_map, user_attr_map, iterate_attr_map;
        bool delayed_connect;
 };
 
+struct db_ldap_field_expand_context {
+       struct event *event;
+       struct auth_fields *fields;
+};
+
+extern const struct var_expand_func_table db_ldap_field_expand_fn_table[];
+
 /* Send/queue request */
 void db_ldap_request(struct ldap_connection *conn,
                     struct ldap_request *request);
 
-void db_ldap_set_attrs(struct ldap_connection *conn,
-                      const ARRAY_TYPE(const_string) *attrlist,
-                      char ***attr_names_r, ARRAY_TYPE(ldap_field) *attr_map,
-                      const char *skip_attr) ATTR_NULL(5);
+void db_ldap_get_attribute_names(struct ldap_connection *conn,
+                                const ARRAY_TYPE(const_string) *attrlist,
+                                char ***attr_names_r,
+                                const char *skip_attr) ATTR_NULL(4);
 
 struct ldap_connection *db_ldap_init(struct event *event);
 void db_ldap_unref(struct ldap_connection **conn);
@@ -171,6 +180,13 @@ bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx,
                                 const char *const **values_r);
 void db_ldap_result_iterate_deinit(struct db_ldap_result_iterate_context **ctx);
 
+struct auth_fields *
+ldap_query_get_fields(pool_t pool,
+                     struct ldap_connection *conn,
+                     struct ldap_request_search *ldap_request,
+                     LDAPMessage *res, bool skip_null_values);
+const char *db_ldap_attribute_as_multi(const char *name);
+
 /* exposed only for unit tests */
 
 const char *const *db_ldap_parse_attrs(const char *cstr);
index d2bf79e201e7dc4339cd1977527379d9c7677616..1e0dd1a7852cfe798841da6b36d7324ee080fc2e 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <ldap.h>
 
+#define RAW_SETTINGS (SETTINGS_GET_FLAG_NO_CHECK | SETTINGS_GET_FLAG_NO_EXPAND)
+
 struct ldap_passdb_module {
        struct passdb_module module;
 
@@ -46,30 +48,18 @@ ldap_query_save_result(struct ldap_connection *conn,
                       struct ldap_request_search *ldap_request,
                       LDAPMessage *res)
 {
-       struct passdb_module *_module = auth_request->passdb->passdb;
-       struct auth_fields *fields = auth_fields_init(auth_request->pool);
-       struct db_ldap_result_iterate_context *ldap_iter;
-       const char *name, *const *values;
-
-       ldap_iter = db_ldap_result_iterate_init(conn, ldap_request, res, FALSE);
-       while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) {
-               auth_fields_add(fields, name, values[0], 0);
-               if (!auth_request->passdb->set->fields_import_all)
-                       continue;
-               if (values[0] == NULL) {
-                       auth_request_set_null_field(auth_request, name);
-                       continue;
-               }
-               if (values[1] != NULL) {
-                       e_warning(authdb_event(auth_request),
-                                 "Multiple values found for '%s', "
-                                 "using value '%s'", name, values[0]);
-               }
-               auth_request_set_field(auth_request, name, values[0],
-                                      _module->default_pass_scheme);
-       }
-       db_ldap_result_iterate_deinit(&ldap_iter);
-       return auth_request_set_passdb_fields(auth_request, fields);
+       struct db_ldap_field_expand_context ctx = {
+               .event = authdb_event(auth_request),
+               .fields = ldap_query_get_fields(auth_request->pool, conn,
+                                               ldap_request, res, FALSE)
+       };
+
+       const char *default_password_scheme =
+               auth_request->passdb->set->default_password_scheme;
+
+       return auth_request_set_passdb_fields_ex(auth_request, &ctx,
+                                                default_password_scheme,
+                                                db_ldap_field_expand_fn_table);
 }
 
 static void
@@ -285,6 +275,7 @@ static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn,
 
 static void ldap_lookup_pass(struct auth_request *auth_request,
                             struct passdb_ldap_request *request,
+                            const struct ldap_pre_settings *ldap_set,
                             bool require_password)
 {
        struct passdb_module *_module = auth_request->passdb->passdb;
@@ -293,33 +284,11 @@ static void ldap_lookup_pass(struct auth_request *auth_request,
        struct ldap_connection *conn = module->conn;
        struct ldap_request_search *srequest = &request->request.search;
        const char **attr_names = (const char **)conn->pass_attr_names;
-       const char *error;
-       string_t *str;
 
        request->require_password = require_password;
        srequest->request.type = LDAP_REQUEST_TYPE_SEARCH;
-
-       str = t_str_new(512);
-       if (auth_request_var_expand(str, conn->set->base, auth_request,
-                                   ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand base=%s: %s", conn->set->base, error);
-               passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE);
-               return;
-       }
-       srequest->base = p_strdup(auth_request->pool, str_c(str));
-
-       str_truncate(str, 0);
-       if (auth_request_var_expand(str, conn->set->filter,
-                                   auth_request, ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand ldap_filter=%s: %s",
-                       conn->set->filter, error);
-               passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE);
-               return;
-       }
-       srequest->filter = p_strdup(auth_request->pool, str_c(str));
-       srequest->attr_map = &conn->pass_attr_map;
+       srequest->base = p_strdup(auth_request->pool, ldap_set->base);
+       srequest->filter = p_strdup(auth_request->pool, ldap_set->filter);
        srequest->attributes = conn->pass_attr_names;
 
        e_debug(authdb_event(auth_request), "pass search: "
@@ -333,43 +302,22 @@ static void ldap_lookup_pass(struct auth_request *auth_request,
 }
 
 static void ldap_bind_lookup_dn(struct auth_request *auth_request,
-                               struct passdb_ldap_request *request)
+                               struct passdb_ldap_request *request,
+                               const struct ldap_pre_settings *ldap_set)
 {
        struct passdb_module *_module = auth_request->passdb->passdb;
        struct ldap_passdb_module *module =
                (struct ldap_passdb_module *)_module;
        struct ldap_connection *conn = module->conn;
        struct ldap_request_search *srequest = &request->request.search;
-       const char *error;
-       string_t *str;
 
        srequest->request.type = LDAP_REQUEST_TYPE_SEARCH;
-
-       str = t_str_new(512);
-       if (auth_request_var_expand(str, conn->set->base, auth_request,
-                                   ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand base=%s: %s", conn->set->base, error);
-               passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE);
-               return;
-       }
-       srequest->base = p_strdup(auth_request->pool, str_c(str));
-
-       str_truncate(str, 0);
-       if (auth_request_var_expand(str, conn->set->filter,
-                                   auth_request, ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand filter=%s: %s",
-                       conn->set->filter, error);
-               passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE);
-               return;
-       }
-       srequest->filter = p_strdup(auth_request->pool, str_c(str));
+       srequest->base = p_strdup(auth_request->pool, ldap_set->base);
+       srequest->filter = p_strdup(auth_request->pool, ldap_set->filter);
 
        /* we don't need the attributes to perform authentication, but they
           may contain some extra parameters. if a password is returned,
           it's just ignored. */
-       srequest->attr_map = &conn->pass_attr_map;
        srequest->attributes = conn->pass_attr_names;
 
        e_debug(authdb_event(auth_request),
@@ -382,29 +330,17 @@ static void ldap_bind_lookup_dn(struct auth_request *auth_request,
 
 static void
 ldap_verify_plain_auth_bind_userdn(struct auth_request *auth_request,
-                                  struct passdb_ldap_request *request)
+                                  struct passdb_ldap_request *request,
+                                  const struct ldap_pre_settings *ldap_set)
 {
        struct passdb_module *_module = auth_request->passdb->passdb;
        struct ldap_passdb_module *module =
                (struct ldap_passdb_module *)_module;
        struct ldap_connection *conn = module->conn;
        struct ldap_request_bind *brequest = &request->request.bind;
-       string_t *dn;
-       const char *error;
 
        brequest->request.type = LDAP_REQUEST_TYPE_BIND;
-
-       dn = t_str_new(512);
-       if (auth_request_var_expand(dn, conn->set->passdb_ldap_bind_userdn,
-                                   auth_request, ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand passdb_ldap_bind_userdn=%s: %s",
-                       conn->set->passdb_ldap_bind_userdn, error);
-               passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE);
-               return;
-       }
-
-       brequest->dn = p_strdup(auth_request->pool, str_c(dn));
+       brequest->dn = p_strdup(auth_request->pool, ldap_set->passdb_ldap_bind_userdn);
         ldap_auth_bind(conn, brequest);
 }
 
@@ -417,7 +353,9 @@ ldap_verify_plain(struct auth_request *request,
        struct ldap_passdb_module *module =
                (struct ldap_passdb_module *)_module;
        struct ldap_connection *conn = module->conn;
+       struct event *event = authdb_event(request);
        struct passdb_ldap_request *ldap_request;
+       const char *error;
 
        /* reconnect if needed. this is also done by db_ldap_search(), but
           with auth binds we'll have to do it ourself */
@@ -426,67 +364,97 @@ ldap_verify_plain(struct auth_request *request,
                return;
        }
 
+       const struct ldap_pre_settings *ldap_pre = NULL;
+       if (settings_get(event, &ldap_pre_setting_parser_info, 0,
+                        &ldap_pre, &error) < 0 ||
+           ldap_pre_settings_pre_check(ldap_pre, &error) < 0) {
+               e_error(event, "%s", error);
+               callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
+               settings_free(ldap_pre);
+               return;
+       }
+
        ldap_request = p_new(request->pool, struct passdb_ldap_request, 1);
        ldap_request->callback.verify_plain = callback;
 
        auth_request_ref(request);
        ldap_request->request.ldap.auth_request = request;
 
-       if (!conn->set->passdb_ldap_bind)
-               ldap_lookup_pass(request, ldap_request, TRUE);
-       else if (conn->set->passdb_ldap_bind_userdn == NULL)
-               ldap_bind_lookup_dn(request, ldap_request);
+       if (!ldap_pre->passdb_ldap_bind)
+               ldap_lookup_pass(request, ldap_request, ldap_pre, TRUE);
+       else if (*ldap_pre->passdb_ldap_bind_userdn == '\0')
+               ldap_bind_lookup_dn(request, ldap_request, ldap_pre);
        else
-               ldap_verify_plain_auth_bind_userdn(request, ldap_request);
+               ldap_verify_plain_auth_bind_userdn(request, ldap_request, ldap_pre);
+
+       settings_free(ldap_pre);
 }
 
 static void ldap_lookup_credentials(struct auth_request *request,
                                    lookup_credentials_callback_t *callback)
 {
-       struct passdb_module *_module = request->passdb->passdb;
-       struct ldap_passdb_module *module =
-               (struct ldap_passdb_module *)_module;
-       struct passdb_ldap_request *ldap_request;
-       bool require_password;
-
-       ldap_request = p_new(request->pool, struct passdb_ldap_request, 1);
+       struct event *event = authdb_event(request);
+       struct passdb_ldap_request *ldap_request =
+               p_new(request->pool, struct passdb_ldap_request, 1);
        ldap_request->callback.lookup_credentials = callback;
 
        auth_request_ref(request);
        ldap_request->request.ldap.auth_request = request;
 
+       const char *error;
+       const struct ldap_pre_settings *ldap_pre = NULL;
+       if (settings_get(event, &ldap_pre_setting_parser_info, 0,
+                        &ldap_pre, &error) < 0 ||
+           ldap_pre_settings_pre_check(ldap_pre, &error) < 0) {
+               e_error(event, "%s", error);
+               passdb_ldap_request_fail(ldap_request, PASSDB_RESULT_INTERNAL_FAILURE);
+               settings_free(ldap_pre);
+               return;
+       }
+
        /* with auth_bind=yes we don't necessarily have a password.
           this will fail actual password credentials lookups, but it's fine
           for passdb lookups done by lmtp/doveadm */
-       require_password = !module->conn->set->passdb_ldap_bind;
-        ldap_lookup_pass(request, ldap_request, require_password);
+       bool require_password = !ldap_pre->passdb_ldap_bind;
+        ldap_lookup_pass(request, ldap_request, ldap_pre, require_password);
+       settings_free(ldap_pre);
 }
 
 static int passdb_ldap_preinit(pool_t pool, struct event *event,
                               struct passdb_module **module_r,
                               const char **error_r)
 {
-       const struct auth_passdb_post_settings *set;
+       const struct auth_passdb_post_settings *auth_post = NULL;
+       const struct ldap_pre_settings *ldap_pre = NULL;
        struct ldap_passdb_module *module;
        struct ldap_connection *conn;
+       int ret = -1;
+
+       if (settings_get(event, &auth_passdb_post_setting_parser_info,
+                        RAW_SETTINGS, &auth_post, error_r) < 0)
+               goto failed;
+       if (settings_get(event, &ldap_pre_setting_parser_info,
+                        RAW_SETTINGS, &ldap_pre, error_r) < 0)
+               goto failed;
 
        module = p_new(pool, struct ldap_passdb_module, 1);
        module->conn = conn = db_ldap_init(event);
-       p_array_init(&conn->pass_attr_map, pool, 16);
-       if (settings_get(event, &auth_passdb_post_setting_parser_info,
-                        SETTINGS_GET_FLAG_NO_CHECK | SETTINGS_GET_FLAG_NO_EXPAND,
-                        &set, error_r) < 0)
-               return -1;
 
-       db_ldap_set_attrs(conn, &set->fields, &conn->pass_attr_names,
-                         &conn->pass_attr_map,
-                         conn->set->passdb_ldap_bind ? "password" : NULL);
+       db_ldap_get_attribute_names(conn, &auth_post->fields,
+                                   &conn->pass_attr_names,
+                                   ldap_pre->passdb_ldap_bind ? "password" : NULL);
+
        module->module.default_cache_key = auth_cache_parse_key_and_fields(
-               pool, conn->set->base, &set->fields, NULL);
+               pool, t_strconcat(ldap_pre->base, ldap_pre->filter, NULL),
+               &auth_post->fields, NULL);
 
-       settings_free(set);
        *module_r = &module->module;
-       return 0;
+       ret = 0;
+
+failed:
+       settings_free(auth_post);
+       settings_free(ldap_pre);
+       return ret;
 }
 
 static void passdb_ldap_init(struct passdb_module *_module)
index 8b18dd236f2bcaa7cd7778563dd0ac192effa771..b2a05dd276183e8a9d86cdcd52d21ce125eba0e0 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <ldap.h>
 
+#define RAW_SETTINGS (SETTINGS_GET_FLAG_NO_CHECK | SETTINGS_GET_FLAG_NO_EXPAND)
+
 struct ldap_userdb_module {
        struct userdb_module module;
 
@@ -48,20 +50,14 @@ ldap_query_get_result(struct ldap_connection *conn,
                      struct ldap_request_search *ldap_request,
                      LDAPMessage *res)
 {
-       struct auth_fields *fields = auth_fields_init(auth_request->pool);
-       struct db_ldap_result_iterate_context *ldap_iter;
-       const char *name, *const *values;
-
-       ldap_iter = db_ldap_result_iterate_init(conn, ldap_request, res, TRUE);
-       while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) {
-               auth_fields_add(fields, name, values[0], 0);
-               if (!auth_request->userdb->set->fields_import_all)
-                       continue;
-               auth_request_set_userdb_field_values(auth_request,
-                                                    name, values);
-       }
-       db_ldap_result_iterate_deinit(&ldap_iter);
-       return auth_request_set_userdb_fields(auth_request, fields);
+       struct db_ldap_field_expand_context ctx = {
+               .event = authdb_event(auth_request),
+               .fields = ldap_query_get_fields(auth_request->pool, conn,
+                                               ldap_request, res, FALSE)
+       };
+
+       return auth_request_set_userdb_fields_ex(auth_request, &ctx,
+                                                db_ldap_field_expand_fn_table);
 }
 
 static void
@@ -117,41 +113,32 @@ static void userdb_ldap_lookup(struct auth_request *auth_request,
        struct ldap_userdb_module *module =
                (struct ldap_userdb_module *)_module;
        struct ldap_connection *conn = module->conn;
+       struct event *event = authdb_event(auth_request);
        const char **attr_names = (const char **)conn->user_attr_names;
+
        struct userdb_ldap_request *request;
        const char *error;
-       string_t *str;
 
-       auth_request_ref(auth_request);
-       request = p_new(auth_request->pool, struct userdb_ldap_request, 1);
-       request->userdb_callback = callback;
-
-       str = t_str_new(512);
-       if (auth_request_var_expand(str, conn->set->base, auth_request,
-                                   ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand base=%s: %s", conn->set->base, error);
+       const struct ldap_pre_settings *ldap_pre = NULL;
+       if (settings_get(event, &ldap_pre_setting_parser_info, 0,
+                        &ldap_pre, &error) < 0 ||
+           ldap_pre_settings_pre_check(ldap_pre, &error) < 0) {
+               e_error(event, "%s", error);
                callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
+               settings_free(ldap_pre);
                return;
        }
-       request->request.base = p_strdup(auth_request->pool, str_c(str));
 
-       str_truncate(str, 0);
-       if (auth_request_var_expand(str, conn->set->filter, auth_request,
-                                   ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand ldap_filter=%s: %s",
-                       conn->set->filter, error);
-               callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
-               return;
-       }
-       request->request.filter = p_strdup(auth_request->pool, str_c(str));
-
-       request->request.attr_map = &conn->user_attr_map;
+       auth_request_ref(auth_request);
+       request = p_new(auth_request->pool, struct userdb_ldap_request, 1);
+       request->userdb_callback = callback;
+       request->request.base = p_strdup(auth_request->pool, ldap_pre->base);
+       request->request.filter = p_strdup(auth_request->pool, ldap_pre->filter);
        request->request.attributes = conn->user_attr_names;
 
-       e_debug(authdb_event(auth_request), "user search: "
-               "base=%s scope=%s filter=%s fields=%s",
+       settings_free(ldap_pre);
+
+       e_debug(event, "user search: base=%s scope=%s filter=%s fields=%s",
                request->request.base, conn->set->scope,
                request->request.filter,
                attr_names == NULL ? "(all)" :
@@ -169,8 +156,6 @@ static void userdb_ldap_iterate_callback(struct ldap_connection *conn,
        struct userdb_iter_ldap_request *urequest =
                (struct userdb_iter_ldap_request *)request;
        struct ldap_userdb_iterate_context *ctx = urequest->ctx;
-       struct db_ldap_result_iterate_context *ldap_iter;
-       const char *name, *const *values;
 
        if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) {
                if (res == NULL)
@@ -189,20 +174,54 @@ static void userdb_ldap_iterate_callback(struct ldap_connection *conn,
        request->create_time = ioloop_time;
 
        ctx->in_callback = TRUE;
-       ldap_iter = db_ldap_result_iterate_init(conn, &urequest->request,
-                                               res, TRUE);
-       while (db_ldap_result_iterate_next(ldap_iter, &name, &values)) {
-               if (strcmp(name, "user") != 0) {
-                       e_warning(authdb_event(request->auth_request), "iterate: "
-                                 "Ignoring field not named 'user': %s", name);
-                       continue;
-               }
-               for (; *values != NULL; values++) {
+
+       struct db_ldap_field_expand_context fctx = {
+               .event = authdb_event(request->auth_request),
+               .fields = ldap_query_get_fields(pool_datastack_create(), conn,
+                                               &urequest->request, res, TRUE)
+       };
+
+       struct var_expand_params params = {
+               .func_table = db_ldap_field_expand_fn_table,
+               .func_context = &fctx
+       };
+
+       struct event *event = event_create(authdb_event(urequest->request.request.auth_request));
+       event_set_ptr(event, SETTINGS_EVENT_VAR_EXPAND_PARAMS, &params);
+
+       const struct ldap_post_settings *set;
+       const char *error;
+       if (settings_get(event, &ldap_post_setting_parser_info, 0,
+                        &set, &error) < 0) {
+               e_error(event, "%s", error);
+               ctx->ctx.failed = TRUE;
+       }
+       else {
+               unsigned int count;
+               const char *const *items = array_get(&set->iterate_fields, &count);
+               for (unsigned int ndx = 0; ndx < count - 1;) {
+                       const char *name = items[ndx++];
+                       const char *value = items[ndx++];
+                       if (strcmp(name, DB_LDAP_ATTR_MULTI_PREFIX"user") == 0) {
+                               value = t_strsplit(value, DB_LDAP_ATTR_SEPARATOR)[0];
+                               e_warning(authdb_event(request->auth_request),
+                                         "iterate: Taking only first value of %s: %s",
+                                         name + 1, value);
+                               continue;
+                       }
+                       if (strcmp(name, "user") != 0) {
+                               e_warning(authdb_event(request->auth_request),
+                                         "iterate: Ignoring field not named 'user': %s",
+                                         name);
+                               continue;
+                       }
                        ctx->continued = FALSE;
-                       ctx->ctx.callback(*values, ctx->ctx.context);
+                       ctx->ctx.callback(value, ctx->ctx.context);
                }
+               settings_free(set);
        }
-       db_ldap_result_iterate_deinit(&ldap_iter);
+       event_unref(&event);
+
        if (!ctx->continued)
                db_ldap_enable_input(conn, FALSE);
        ctx->in_callback = FALSE;
@@ -216,11 +235,12 @@ userdb_ldap_iterate_init(struct auth_request *auth_request,
        struct ldap_userdb_module *module =
                (struct ldap_userdb_module *)_module;
        struct ldap_connection *conn = module->conn;
+       struct event *event = authdb_event(auth_request);
+
        struct ldap_userdb_iterate_context *ctx;
        struct userdb_iter_ldap_request *request;
        const char **attr_names = (const char **)conn->iterate_attr_names;
        const char *error;
-       string_t *str;
 
        ctx = p_new(auth_request->pool, struct ldap_userdb_iterate_context, 1);
        ctx->ctx.auth_request = auth_request;
@@ -230,34 +250,25 @@ userdb_ldap_iterate_init(struct auth_request *auth_request,
        request = &ctx->request;
        request->ctx = ctx;
 
-       auth_request_ref(auth_request);
-       request->request.request.auth_request = auth_request;
-
-       str = t_str_new(512);
-       if (auth_request_var_expand(str, conn->set->base, auth_request,
-                                   ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand base=%s: %s", conn->set->base, error);
+       const struct ldap_pre_settings *ldap_pre = NULL;
+       if (settings_get(event, &ldap_pre_setting_parser_info, 0,
+                        &ldap_pre, &error) < 0 ||
+           ldap_pre_settings_pre_check(ldap_pre, &error) < 0) {
+               e_error(event, "%s", error);
+               settings_free(ldap_pre);
                ctx->ctx.failed = TRUE;
                return &ctx->ctx;
        }
-       request->request.base = p_strdup(auth_request->pool, str_c(str));
 
-       str_truncate(str, 0);
-       if (auth_request_var_expand(str, conn->set->iterate_filter,
-                                   auth_request, ldap_escape, &error) <= 0) {
-               e_error(authdb_event(auth_request),
-                       "Failed to expand iterate_filter=%s: %s",
-                       conn->set->iterate_filter, error);
-               ctx->ctx.failed = TRUE;
-               return &ctx->ctx;
-       }
-       request->request.filter = p_strdup(auth_request->pool, str_c(str));
-       request->request.attr_map = &conn->iterate_attr_map;
+       auth_request_ref(auth_request);
+       request->request.request.auth_request = auth_request;
+       request->request.base = p_strdup(auth_request->pool, ldap_pre->base);
+       request->request.filter = p_strdup(auth_request->pool, ldap_pre->iterate_filter);
        request->request.attributes = conn->iterate_attr_names;
        request->request.multi_entry = TRUE;
+       settings_free(ldap_pre);
 
-       e_debug(auth_request->event, "ldap: iterate: base=%s scope=%s filter=%s fields=%s",
+       e_debug(event, "ldap: iterate: base=%s scope=%s filter=%s fields=%s",
                request->request.base, conn->set->scope,
                request->request.filter, attr_names == NULL ? "(all)" :
                t_strarray_join(attr_names, ","));
@@ -295,30 +306,43 @@ static int userdb_ldap_preinit(pool_t pool, struct event *event,
                               struct userdb_module **module_r,
                               const char **error_r ATTR_UNUSED)
 {
-       const struct auth_passdb_post_settings *set;
+       const struct auth_userdb_post_settings *auth_post = NULL;
+       const struct ldap_post_settings *ldap_post = NULL;
+       const struct ldap_pre_settings *ldap_pre = NULL;
        struct ldap_userdb_module *module;
        struct ldap_connection *conn;
+       int ret = -1;
+
+       if (settings_get(event, &auth_userdb_post_setting_parser_info,
+                        RAW_SETTINGS, &auth_post, error_r) < 0)
+               goto failed;
+       if (settings_get(event, &ldap_post_setting_parser_info,
+                        RAW_SETTINGS, &ldap_post, error_r) < 0)
+               goto failed;
+       if (settings_get(event, &ldap_pre_setting_parser_info,
+                        RAW_SETTINGS, &ldap_pre, error_r) < 0)
+               goto failed;
 
        module = p_new(pool, struct ldap_userdb_module, 1);
        module->conn = conn = db_ldap_init(event);
-       p_array_init(&conn->user_attr_map, pool, 16);
-       p_array_init(&conn->iterate_attr_map, pool, 16);
-       if (settings_get(event, &auth_passdb_post_setting_parser_info,
-                        SETTINGS_GET_FLAG_NO_CHECK | SETTINGS_GET_FLAG_NO_EXPAND,
-                        &set, error_r) < 0)
-               return -1;
-
-       db_ldap_set_attrs(conn, &set->fields, &conn->user_attr_names,
-                         &conn->user_attr_map, NULL);
-       db_ldap_set_attrs(conn, &conn->set->iterate_attrs,
-                         &conn->iterate_attr_names,
-                         &conn->iterate_attr_map, NULL);
+
+       db_ldap_get_attribute_names(conn, &auth_post->fields,
+                                   &conn->user_attr_names, NULL);
+       db_ldap_get_attribute_names(conn, &ldap_post->iterate_fields,
+                                   &conn->iterate_attr_names, NULL);
+
        module->module.default_cache_key = auth_cache_parse_key_and_fields(
-               pool, conn->set->base, &set->fields, NULL);
+               pool, t_strconcat(ldap_pre->base, ldap_pre->filter, NULL),
+               &auth_post->fields, NULL);
 
-       settings_free(set);
        *module_r = &module->module;
-       return 0;
+       ret = 0;
+
+failed:
+       settings_free(auth_post);
+       settings_free(ldap_pre);
+       settings_free(ldap_post);
+       return ret;
 }
 
 static void userdb_ldap_init(struct userdb_module *_module)
index f33d374ebcec38a112f387b8fb3f71d87b98b84e..aa820d02f207295f78f581d30bc7bd46e61d5459 100644 (file)
@@ -177,7 +177,7 @@ int ldap_connection_init(struct ldap_client *client,
        /* deep copy relevant strings */
        conn->set.uri = p_strdup(pool, set->uri);
        conn->set.bind_dn = p_strdup(pool, set->bind_dn);
-       if (set->password != NULL) {
+       if (*set->password != '\0') {
                conn->set.password = p_strdup(pool, set->password);
                ber_str2bv(conn->set.password, strlen(conn->set.password), 0, &conn->cred);
        }