diff options
Diffstat (limited to 'src/auth/userdb-ldap.c')
-rw-r--r-- | src/auth/userdb-ldap.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/src/auth/userdb-ldap.c b/src/auth/userdb-ldap.c new file mode 100644 index 0000000..ecbf09e --- /dev/null +++ b/src/auth/userdb-ldap.c @@ -0,0 +1,343 @@ +/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ + +#include "auth-common.h" +#include "userdb.h" + +#if defined(USERDB_LDAP) && (defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)) + +#include "ioloop.h" +#include "array.h" +#include "str.h" +#include "auth-cache.h" +#include "db-ldap.h" + +#include <ldap.h> + +struct ldap_userdb_module { + struct userdb_module module; + + struct ldap_connection *conn; +}; + +struct userdb_ldap_request { + struct ldap_request_search request; + userdb_callback_t *userdb_callback; + unsigned int entries; +}; + +struct userdb_iter_ldap_request { + struct ldap_request_search request; + struct ldap_userdb_iterate_context *ctx; + userdb_callback_t *userdb_callback; +}; + +struct ldap_userdb_iterate_context { + struct userdb_iterate_context ctx; + struct userdb_iter_ldap_request request; + pool_t pool; + struct ldap_connection *conn; + bool continued, in_callback, deinitialized; +}; + +static void +ldap_query_get_result(struct ldap_connection *conn, + struct auth_request *auth_request, + struct ldap_request_search *ldap_request, + LDAPMessage *res) +{ + 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_request_set_userdb_field_values(auth_request, + name, values); + } + db_ldap_result_iterate_deinit(&ldap_iter); +} + +static void +userdb_ldap_lookup_finish(struct auth_request *auth_request, + struct userdb_ldap_request *urequest, + LDAPMessage *res) +{ + enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; + + if (res == NULL) { + result = USERDB_RESULT_INTERNAL_FAILURE; + } else if (urequest->entries == 0) { + result = USERDB_RESULT_USER_UNKNOWN; + auth_request_log_unknown_user(auth_request, AUTH_SUBSYS_DB); + } else if (urequest->entries > 1) { + e_error(authdb_event(auth_request), + "user_filter matched multiple objects, aborting"); + result = USERDB_RESULT_INTERNAL_FAILURE; + } else { + result = USERDB_RESULT_OK; + } + + urequest->userdb_callback(result, auth_request); +} + +static void userdb_ldap_lookup_callback(struct ldap_connection *conn, + struct ldap_request *request, + LDAPMessage *res) +{ + struct userdb_ldap_request *urequest = + (struct userdb_ldap_request *) request; + struct auth_request *auth_request = + urequest->request.request.auth_request; + + if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { + userdb_ldap_lookup_finish(auth_request, urequest, res); + auth_request_unref(&auth_request); + return; + } + + if (urequest->entries++ == 0) { + /* first entry */ + ldap_query_get_result(conn, auth_request, + &urequest->request, res); + } +} + +static void userdb_ldap_lookup(struct auth_request *auth_request, + userdb_callback_t *callback) +{ + struct userdb_module *_module = auth_request->userdb->userdb; + struct ldap_userdb_module *module = + (struct ldap_userdb_module *)_module; + struct ldap_connection *conn = module->conn; + 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); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } + request->request.base = p_strdup(auth_request->pool, str_c(str)); + + str_truncate(str, 0); + if (auth_request_var_expand(str, conn->set.user_filter, auth_request, + ldap_escape, &error) <= 0) { + e_error(authdb_event(auth_request), + "Failed to expand user_filter=%s: %s", + conn->set.user_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; + request->request.attributes = conn->user_attr_names; + + e_debug(authdb_event(auth_request), "user search: " + "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, ",")); + + request->request.request.auth_request = auth_request; + request->request.request.callback = userdb_ldap_lookup_callback; + db_ldap_request(conn, &request->request.request); +} + +static void userdb_ldap_iterate_callback(struct ldap_connection *conn, + struct ldap_request *request, + LDAPMessage *res) +{ + 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) + ctx->ctx.failed = TRUE; + if (!ctx->deinitialized) + ctx->ctx.callback(NULL, ctx->ctx.context); + auth_request_unref(&request->auth_request); + return; + } + + if (ctx->deinitialized) + return; + + /* the iteration can take a while. reset the request's create time so + it won't be aborted while it's still running */ + 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++) { + ctx->continued = FALSE; + ctx->ctx.callback(*values, ctx->ctx.context); + } + } + db_ldap_result_iterate_deinit(&ldap_iter); + if (!ctx->continued) + db_ldap_enable_input(conn, FALSE); + ctx->in_callback = FALSE; +} + +static struct userdb_iterate_context * +userdb_ldap_iterate_init(struct auth_request *auth_request, + userdb_iter_callback_t *callback, void *context) +{ + struct userdb_module *_module = auth_request->userdb->userdb; + struct ldap_userdb_module *module = + (struct ldap_userdb_module *)_module; + struct ldap_connection *conn = module->conn; + 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; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + ctx->conn = conn; + 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); + ctx->ctx.failed = TRUE; + } + 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; + } + request->request.filter = p_strdup(auth_request->pool, str_c(str)); + request->request.attr_map = &conn->iterate_attr_map; + request->request.attributes = conn->iterate_attr_names; + request->request.multi_entry = TRUE; + + e_debug(auth_request->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, ",")); + request->request.request.callback = userdb_ldap_iterate_callback; + db_ldap_request(conn, &request->request.request); + return &ctx->ctx; +} + +static void userdb_ldap_iterate_next(struct userdb_iterate_context *_ctx) +{ + struct ldap_userdb_iterate_context *ctx = + (struct ldap_userdb_iterate_context *)_ctx; + + ctx->continued = TRUE; + if (!ctx->in_callback) + db_ldap_enable_input(ctx->conn, TRUE); +} + +static int userdb_ldap_iterate_deinit(struct userdb_iterate_context *_ctx) +{ + struct ldap_userdb_iterate_context *ctx = + (struct ldap_userdb_iterate_context *)_ctx; + int ret = _ctx->failed ? -1 : 0; + + db_ldap_enable_input(ctx->conn, TRUE); + ctx->deinitialized = TRUE; + return ret; +} + +static struct userdb_module * +userdb_ldap_preinit(pool_t pool, const char *args) +{ + struct ldap_userdb_module *module; + struct ldap_connection *conn; + + module = p_new(pool, struct ldap_userdb_module, 1); + module->conn = conn = db_ldap_init(args, TRUE); + p_array_init(&conn->user_attr_map, pool, 16); + p_array_init(&conn->iterate_attr_map, pool, 16); + + db_ldap_set_attrs(conn, conn->set.user_attrs, &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); + module->module.blocking = conn->set.blocking; + module->module.default_cache_key = + auth_cache_parse_key(pool, + t_strconcat(conn->set.base, + conn->set.user_attrs, + conn->set.user_filter, NULL)); + return &module->module; +} + +static void userdb_ldap_init(struct userdb_module *_module) +{ + struct ldap_userdb_module *module = + (struct ldap_userdb_module *)_module; + + if (!module->module.blocking || worker) + db_ldap_connect_delayed(module->conn); +} + +static void userdb_ldap_deinit(struct userdb_module *_module) +{ + struct ldap_userdb_module *module = + (struct ldap_userdb_module *)_module; + + db_ldap_unref(&module->conn); +} + +#ifndef PLUGIN_BUILD +struct userdb_module_interface userdb_ldap = +#else +struct userdb_module_interface userdb_ldap_plugin = +#endif +{ + "ldap", + + userdb_ldap_preinit, + userdb_ldap_init, + userdb_ldap_deinit, + + userdb_ldap_lookup, + + userdb_ldap_iterate_init, + userdb_ldap_iterate_next, + userdb_ldap_iterate_deinit +}; +#else +struct userdb_module_interface userdb_ldap = { + .name = "ldap" +}; +#endif |