]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: ldap - Add support for fields { .. }
authorMarco Bettini <marco.bettini@open-xchange.com>
Mon, 25 Mar 2024 14:23:51 +0000 (14:23 +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/test-auth.h
src/auth/test-db-ldap.c
src/auth/test-main.c
src/auth/userdb-ldap.c

index 4dd895156d5944e37f05a80f34bd12ffc723e399..a3600f8ace060d048afe1187b369075c7f25cda3 100644 (file)
@@ -35,11 +35,9 @@ static const struct setting_define ldap_setting_defines[] = {
        DEF(STR, base),
        DEF(UINT, version),
        DEF(STR, debug_level),
-       DEF(STR, user_attrs),
        DEF(STR, user_filter),
-       DEF(STR, pass_attrs),
        DEF(STR, pass_filter),
-       DEF(STR, iterate_attrs),
+       DEF(STRLIST, iterate_attrs),
        DEF(STR, iterate_filter),
        SETTING_DEFINE_LIST_END
 };
@@ -60,16 +58,16 @@ static const struct ldap_settings ldap_default_settings = {
        .base = "",
        .version = 3,
        .debug_level = "0",
-       .user_attrs = "homeDirectory=home,uidNumber=uid,gidNumber=gid",
-       .user_filter = "(&(objectClass=posixAccount)(uid=%u))",
-       .pass_attrs = "uid=user,userPassword=password",
-       .pass_filter = "(&(objectClass=posixAccount)(uid=%u))",
-       .iterate_attrs = "uid=user",
-       .iterate_filter = "(objectClass=posixAccount)",
+       .user_filter = "",
+       .pass_filter = "",
+       .iterate_attrs = ARRAY_INIT,
+       .iterate_filter = "",
 };
 
 static const struct setting_keyvalue ldap_default_settings_keyvalue[] = {
        { "passdb_ldap/passdb_default_password_scheme", "crypt" },
+       { "passdb_ldap/passdb_fields_import_all", "no" },
+       { "userdb_ldap/userdb_fields_import_all", "no" },
        { NULL, NULL }
 };
 
index 25f9718295e952caa59d8029905cb01187667251..7028f1f2c808762090dd85ecad09a9bfe7dad09f 100644 (file)
@@ -20,13 +20,12 @@ struct ldap_settings {
 
        const char *debug_level;
 
-       const char *user_attrs;
        const char *user_filter;
-       const char *pass_attrs;
        const char *pass_filter;
-       const char *iterate_attrs;
        const char *iterate_filter;
 
+       ARRAY_TYPE(const_string) iterate_attrs;
+
        unsigned int version;
 
        uid_t uid;
index 17dde333808fc7fa39e0e96b771602b13d29eb76..8c9d587823e09c8b9d884beb7b2f7522bdda1cb9 100644 (file)
@@ -1072,49 +1072,8 @@ db_ldap_field_find(const char *data, void *context,
        return 1;
 }
 
-const char *const *db_ldap_parse_attrs(const char *cstr)
-{
-       ARRAY_TYPE(const_string) entries;
-       t_array_init(&entries, 32);
-
-       char *ptr = t_strdup_noconst(cstr);
-       const char *start = ptr;
-       unsigned int nesting = 0;
-       while (*ptr != '\0') {
-               switch (*ptr) {
-               case '{':
-                       nesting++;
-                       ptr++;
-                       break;
-               case '}':
-                       if (nesting > 0)
-                               nesting--;
-                       ptr++;
-                       break;
-               case ',':
-                       if (nesting > 0)
-                               ptr++;
-                       else {
-                               *ptr = '\0';
-                               if (*start != '\0')
-                                       array_push_back(&entries, &start);
-                               start = ++ptr;
-                       }
-                       break;
-               default:
-                       ptr++;
-                       break;
-               }
-       }
-       if (*start != '\0')
-               array_push_back(&entries, &start);
-
-       unsigned int count ATTR_UNUSED;
-       array_append_zero(&entries);
-       return array_get(&entries, &count);
-}
-
-void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
+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)
 {
@@ -1124,40 +1083,38 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
                { "ldap_ptr", db_ldap_field_find },
                { NULL, NULL }
        };
-       struct ldap_field_find_context ctx;
-       struct ldap_field *field;
-       string_t *tmp_str;
-       const char *const *attr, *attr_data, *p, *error;
-       char *ldap_attr, *name, *templ;
-       unsigned int i;
 
-       attr = db_ldap_parse_attrs(attrlist);
-       if (*attr == NULL)
-               return;
+       unsigned int count = array_is_empty(attrlist) ? 0 : array_count(attrlist);
+       i_assert(count % 2 == 0);
 
-       tmp_str = t_str_new(128);
+       struct ldap_field_find_context ctx;
        ctx.pool = conn->pool;
-       p_array_init(&ctx.attr_names, conn->pool, 16);
-       for (i = 0; attr[i] != NULL; i++) {
-               /* allow spaces here so "foo=1, bar=2" works */
-               attr_data = attr[i];
-               while (*attr_data == ' ') attr_data++;
-
-               p = strchr(attr_data, '=');
-               if (p == NULL)
-                       ldap_attr = name = p_strdup(conn->pool, attr_data);
-               else {
-                       ldap_attr = p_strdup_until(conn->pool, attr_data, p);
-                       name = p_strdup(conn->pool, p + 1);
+       p_array_init(&ctx.attr_names, conn->pool, count / 2);
+       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;
                }
 
-               templ = strchr(name, '=');
+               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,
@@ -1179,9 +1136,9 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
                }
 
                if (*name == '\0')
-                       e_error(conn->event, "Invalid attrs entry: %s", attr_data);
+                       e_error(conn->event, "Invalid empty attribute name");
                else if (skip_attr == NULL || strcmp(skip_attr, name) != 0) {
-                       field = array_append_space(attr_map);
+                       struct ldap_field *field = array_append_space(attr_map);
                        if (name[0] == '!' && name == ldap_attr) {
                                /* !ldapAttr */
                                name = "";
@@ -1681,7 +1638,7 @@ db_ldap_conn_find(const struct ldap_settings *set, const struct ssl_settings *ss
 
 struct ldap_connection *db_ldap_init(struct event *event)
 {
-        const struct ldap_settings *set;
+       const struct ldap_settings *set;
        const struct ssl_settings *ssl_set;
        const char *error;
 
index 7a163ff795d4e2007166b97a87f60e800529f819..8de5bb74ed454755bb80513b054f424248adbbb2 100644 (file)
@@ -145,7 +145,8 @@ struct ldap_connection {
 void db_ldap_request(struct ldap_connection *conn,
                     struct ldap_request *request);
 
-void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
+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);
 
index 4996d6dc55caf5f58cb1f819e042a242fb6ed69c..ccaddc6ff16182c96613fd08fa253cf094311c11 100644 (file)
@@ -10,6 +10,8 @@
 #include "str.h"
 #include "password-scheme.h"
 #include "auth-cache.h"
+#include "settings.h"
+#include "auth-settings.h"
 #include "db-ldap.h"
 
 #include <ldap.h>
@@ -34,21 +36,26 @@ struct passdb_ldap_request {
        } callback;
 
        unsigned int entries;
-       bool require_password;
+       bool require_password:1;
+       bool failed:1;
 };
 
-static void
+static int
 ldap_query_save_result(struct ldap_connection *conn,
                       struct auth_request *auth_request,
                       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;
@@ -62,6 +69,7 @@ ldap_query_save_result(struct ldap_connection *conn,
                                       _module->default_pass_scheme);
        }
        db_ldap_result_iterate_deinit(&ldap_iter);
+       return auth_request_set_passdb_fields(auth_request, fields);
 }
 
 static void
@@ -72,7 +80,7 @@ ldap_lookup_finish(struct auth_request *auth_request,
        enum passdb_result passdb_result;
        const char *password = NULL, *scheme;
 
-       if (res == NULL) {
+       if (res == NULL || ldap_request->failed) {
                passdb_result = PASSDB_RESULT_INTERNAL_FAILURE;
        } else if (ldap_request->entries == 0) {
                passdb_result = PASSDB_RESULT_USER_UNKNOWN;
@@ -128,8 +136,9 @@ ldap_lookup_pass_callback(struct ldap_connection *conn,
 
        if (ldap_request->entries++ == 0) {
                /* first entry */
-               ldap_query_save_result(conn, auth_request,
-                                      &ldap_request->request.search, res);
+               if (ldap_query_save_result(conn, auth_request,
+                                          &ldap_request->request.search, res) < 0)
+                       ldap_request->failed = TRUE;
        }
 }
 
@@ -455,22 +464,27 @@ static void ldap_lookup_credentials(struct auth_request *request,
 
 static int passdb_ldap_preinit(pool_t pool, struct event *event,
                               struct passdb_module **module_r,
-                              const char **error_r ATTR_UNUSED)
+                              const char **error_r)
 {
+       const struct auth_passdb_post_settings *set;
        struct ldap_passdb_module *module;
        struct ldap_connection *conn;
 
        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);
-       db_ldap_set_attrs(conn, conn->set->pass_attrs, &conn->pass_attr_names,
+       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);
-       module->module.default_cache_key =
-               auth_cache_parse_key(pool,
-                                    t_strconcat(conn->set->base,
-                                                conn->set->pass_attrs,
-                                                conn->set->pass_filter, NULL));
+       module->module.default_cache_key = auth_cache_parse_key_and_fields(
+               pool, conn->set->base, &set->fields, NULL);
+
+       settings_free(set);
        *module_r = &module->module;
        return 0;
 }
@@ -499,6 +513,7 @@ struct passdb_module_interface passdb_ldap_plugin =
 #endif
 {
        .name = "ldap",
+       .fields_supported = TRUE,
 
        .preinit = passdb_ldap_preinit,
        .init = passdb_ldap_init,
index a707b7a56eff87498fdf648471b783d10a0b631d..ff72073e3db39679c3777c21d3233d44652e7024 100644 (file)
@@ -19,7 +19,6 @@ struct auth_passdb *passdb_mock(void);
 void passdb_mock_mod_init(void);
 void passdb_mock_mod_deinit(void);
 
-void test_db_ldap_parse_attrs(void);
 void test_db_ldap_field_multi_expand_parse_data(void);
 
 void test_auth_init(void);
index b22d32c7f6e3974ea4e320af9997a8b8c1e228ee..ffae3bb00b4802a645c92bb84b1505e6fa5cae99 100644 (file)
@@ -8,59 +8,6 @@
 #include "db-ldap.h"
 #include <stdio.h>
 
-void test_db_ldap_parse_attrs(void)
-{
-       struct vectors {
-               const char *inp;
-               const char *out;
-       } vectors[] = {
-               { .inp = "",                    .out = ""},
-               { .inp = "a",                   .out = "a"},
-
-               /* tests with leading/trailing/no spaces*/
-               { .inp = "a,b,c",               .out = "a|b|c"},
-               { .inp = "a, b, c",             .out = "a| b| c"},
-               { .inp = "a ,b ,c",             .out = "a |b |c"},
-
-               /* leading empty field */
-               { .inp = ",a,b",                .out = "a|b"},
-               /* trailing empty field */
-               { .inp = "a,b,",                .out = "a|b"},
-               /* middle empty field */
-               { .inp = "a,,b",                .out = "a|b"},
-
-
-               /* simple nesting at begining/end of field */
-               { .inp = "a,{b,c},d",           .out = "a|{b,c}|d"},
-
-               /* simple nesting in the middle of the field */
-               { .inp = "a,b{c,d}e,f",         .out = "a|b{c,d}e|f"},
-
-               /* multiple nesting, balanced, prefixed and suffixed */
-               { .inp = "a, {{b, c}, d}, e",   .out = "a| {{b, c}, d}| e"},
-               { .inp = "a, {b, {c, d}}, e",   .out = "a| {b, {c, d}}| e"},
-
-               /* unbalanced nesting, excess of {s */
-               { .inp = "{",                   .out = "{"},
-               { .inp = "a, {b, {c, d}, e",    .out = "a| {b, {c, d}, e"},
-
-               /* unbalanced nesting, excess of }s */
-               { .inp = "}",                   .out = "}"},
-               { .inp = "a, {b, {c, d}}}, e",  .out = "a| {b, {c, d}}}| e"},
-
-               {}
-       };
-
-       test_begin("db ldap parse attrs");
-       unsigned int index = 0;
-       for (struct vectors *vector = vectors; vector->inp != NULL; vector++, index++) {
-               const char *const *array = db_ldap_parse_attrs(vector->inp);
-               const char *out = t_strarray_join(array, "|");
-               test_assert_strcmp_idx(vector->out, out, index);
-       }
-       test_end();
-}
-
 void test_db_ldap_field_multi_expand_parse_data(void)
 {
        struct vectors {
index 6ce00d7b077fbf52f0a33253314627313a78ef19..7c23ae173e3ced39f1edb002779b33f7b44d5b34 100644 (file)
@@ -19,7 +19,6 @@ int main(int argc, char *argv[])
        int ret;
        static const struct named_test test_functions[] = {
 #if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)
-               TEST_NAMED(test_db_ldap_parse_attrs)
                TEST_NAMED(test_db_ldap_field_multi_expand_parse_data)
 #endif
                TEST_NAMED(test_auth_request_var_expand)
index 1a30017f5136a69860054a6e8f9541d2f031f1f9..5d3ed1832b0ba39d1e1a4d5d22fe48d37c4fdbf8 100644 (file)
@@ -9,6 +9,8 @@
 #include "array.h"
 #include "str.h"
 #include "auth-cache.h"
+#include "settings.h"
+#include "auth-settings.h"
 #include "db-ldap.h"
 
 #include <ldap.h>
@@ -23,6 +25,7 @@ struct userdb_ldap_request {
        struct ldap_request_search request;
        userdb_callback_t *userdb_callback;
        unsigned int entries;
+       bool failed:1;
 };
 
 struct userdb_iter_ldap_request {
@@ -39,21 +42,26 @@ struct ldap_userdb_iterate_context {
        bool continued, in_callback, deinitialized;
 };
 
-static void
+static int
 ldap_query_get_result(struct ldap_connection *conn,
                      struct auth_request *auth_request,
                      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);
 }
 
 static void
@@ -63,7 +71,7 @@ userdb_ldap_lookup_finish(struct auth_request *auth_request,
 {
        enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE;
 
-       if (res == NULL) {
+       if (res == NULL || urequest->failed) {
                result = USERDB_RESULT_INTERNAL_FAILURE;
        } else if (urequest->entries == 0) {
                result = USERDB_RESULT_USER_UNKNOWN;
@@ -96,8 +104,9 @@ static void userdb_ldap_lookup_callback(struct ldap_connection *conn,
 
        if (urequest->entries++ == 0) {
                /* first entry */
-               ldap_query_get_result(conn, auth_request,
-                                     &urequest->request, res);
+               if (ldap_query_get_result(conn, auth_request,
+                                         &urequest->request, res) < 0)
+                       urequest->failed = TRUE;
        }
 }
 
@@ -286,6 +295,7 @@ 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;
        struct ldap_userdb_module *module;
        struct ldap_connection *conn;
 
@@ -293,17 +303,20 @@ static int userdb_ldap_preinit(pool_t pool, struct event *event,
        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, conn->set->user_attrs, &conn->user_attr_names,
+       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,
+       db_ldap_set_attrs(conn, &conn->set->iterate_attrs,
                          &conn->iterate_attr_names,
                          &conn->iterate_attr_map, NULL);
-       module->module.default_cache_key =
-               auth_cache_parse_key(pool,
-                                    t_strconcat(conn->set->base,
-                                                conn->set->user_attrs,
-                                                conn->set->user_filter, NULL));
+       module->module.default_cache_key = auth_cache_parse_key_and_fields(
+               pool, conn->set->base, &set->fields, NULL);
+
+       settings_free(set);
        *module_r = &module->module;
        return 0;
 }
@@ -332,6 +345,7 @@ struct userdb_module_interface userdb_ldap_plugin =
 #endif
 {
        .name = "ldap",
+       .fields_supported = TRUE,
 
        .preinit = userdb_ldap_preinit,
        .init = userdb_ldap_init,