From: Marco Bettini Date: Mon, 25 Mar 2024 14:23:51 +0000 (+0000) Subject: auth: ldap - Add support for fields { .. } X-Git-Tag: 2.4.1~767 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3f82213c6ba20c4e6b57e8e0862656bd977db205;p=thirdparty%2Fdovecot%2Fcore.git auth: ldap - Add support for fields { .. } --- diff --git a/src/auth/db-ldap-settings.c b/src/auth/db-ldap-settings.c index 4dd895156d..a3600f8ace 100644 --- a/src/auth/db-ldap-settings.c +++ b/src/auth/db-ldap-settings.c @@ -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 } }; diff --git a/src/auth/db-ldap-settings.h b/src/auth/db-ldap-settings.h index 25f9718295..7028f1f2c8 100644 --- a/src/auth/db-ldap-settings.h +++ b/src/auth/db-ldap-settings.h @@ -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; diff --git a/src/auth/db-ldap.c b/src/auth/db-ldap.c index 17dde33380..8c9d587823 100644 --- a/src/auth/db-ldap.c +++ b/src/auth/db-ldap.c @@ -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; diff --git a/src/auth/db-ldap.h b/src/auth/db-ldap.h index 7a163ff795..8de5bb74ed 100644 --- a/src/auth/db-ldap.h +++ b/src/auth/db-ldap.h @@ -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); diff --git a/src/auth/passdb-ldap.c b/src/auth/passdb-ldap.c index 4996d6dc55..ccaddc6ff1 100644 --- a/src/auth/passdb-ldap.c +++ b/src/auth/passdb-ldap.c @@ -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 @@ -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, diff --git a/src/auth/test-auth.h b/src/auth/test-auth.h index a707b7a56e..ff72073e3d 100644 --- a/src/auth/test-auth.h +++ b/src/auth/test-auth.h @@ -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); diff --git a/src/auth/test-db-ldap.c b/src/auth/test-db-ldap.c index b22d32c7f6..ffae3bb00b 100644 --- a/src/auth/test-db-ldap.c +++ b/src/auth/test-db-ldap.c @@ -8,59 +8,6 @@ #include "db-ldap.h" #include -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 { diff --git a/src/auth/test-main.c b/src/auth/test-main.c index 6ce00d7b07..7c23ae173e 100644 --- a/src/auth/test-main.c +++ b/src/auth/test-main.c @@ -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) diff --git a/src/auth/userdb-ldap.c b/src/auth/userdb-ldap.c index 1a30017f51..5d3ed1832b 100644 --- a/src/auth/userdb-ldap.c +++ b/src/auth/userdb-ldap.c @@ -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 @@ -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,