Mercurial > dovecot > core-2.2
changeset 9273:1d7965092e0e HEAD
Implemented support for listing all users in userdb.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 13 May 2009 17:51:16 -0400 |
parents | f5b6974cbee7 |
children | 39c2db5f1fcc |
files | doc/dovecot-ldap-example.conf doc/dovecot-sql-example.conf src/auth/auth-master-connection.c src/auth/auth-worker-client.c src/auth/auth-worker-server.c src/auth/auth-worker-server.h src/auth/db-ldap.c src/auth/db-ldap.h src/auth/db-passwd-file.h src/auth/db-sql.c src/auth/db-sql.h src/auth/main.c src/auth/passdb-blocking.c src/auth/passdb-ldap.c src/auth/passdb.c src/auth/userdb-blocking.c src/auth/userdb-blocking.h src/auth/userdb-checkpassword.c src/auth/userdb-ldap.c src/auth/userdb-nss.c src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-prefetch.c src/auth/userdb-sql.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.c src/auth/userdb.h |
diffstat | 28 files changed, 1022 insertions(+), 181 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/dovecot-ldap-example.conf Wed May 13 12:40:58 2009 -0400 +++ b/doc/dovecot-ldap-example.conf Wed May 13 17:51:16 2009 -0400 @@ -129,6 +129,10 @@ # Filter for password lookups #pass_filter = (&(objectClass=posixAccount)(uid=%u)) +# Attributes and filter to get a list of all users +#iterate_attrs = uid=user +#iterate_filter = (objectClass=posixAccount) + # Default password scheme. "{scheme}" before password overrides this. # List of supported schemes is in: http://wiki.dovecot.org/Authentication #default_pass_scheme = CRYPT
--- a/doc/dovecot-sql-example.conf Wed May 13 12:40:58 2009 -0400 +++ b/doc/dovecot-sql-example.conf Wed May 13 17:51:16 2009 -0400 @@ -128,3 +128,6 @@ # SELECT userid AS user, password, \ # home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ # FROM users WHERE userid = '%u' + +# Query to get a list of all usernames. +#iterate_query = SELECT username AS user FROM users
--- a/src/auth/auth-master-connection.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/auth-master-connection.c Wed May 13 17:51:16 2009 -0400 @@ -4,6 +4,7 @@ #include "array.h" #include "hash.h" #include "str.h" +#include "strescape.h" #include "hostpid.h" #include "str-sanitize.h" #include "ioloop.h" @@ -12,6 +13,7 @@ #include "ostream.h" #include "master-service.h" #include "userdb.h" +#include "userdb-blocking.h" #include "auth-request-handler.h" #include "auth-master-interface.h" #include "auth-client-connection.h" @@ -29,6 +31,16 @@ struct auth_request *auth_request; }; +struct master_list_iter_ctx { + struct auth_master_connection *conn; + struct auth_userdb *userdb; + struct userdb_iterate_context *iter; + unsigned int id; + bool failed; +}; + +static void master_input(struct auth_master_connection *conn); + ARRAY_TYPE(auth_master_connections) auth_master_connections; void auth_master_request_callback(struct auth_stream_reply *reply, @@ -162,6 +174,112 @@ return TRUE; } +static void master_input_list_finish(struct master_list_iter_ctx *ctx) +{ + ctx->conn->io = io_add(ctx->conn->fd, IO_READ, master_input, ctx->conn); + + if (ctx->iter != NULL) + (void)userdb_blocking_iter_deinit(&ctx->iter); + o_stream_unset_flush_callback(ctx->conn->output); + i_free(ctx); +} + +static int master_output_list(struct master_list_iter_ctx *ctx) +{ + int ret; + + if ((ret = o_stream_flush(ctx->conn->output)) < 0) { + master_input_list_finish(ctx); + return 1; + } + if (ret > 0) + userdb_blocking_iter_next(ctx->iter); + return 1; +} + +static void master_input_list_callback(const char *user, void *context) +{ + struct master_list_iter_ctx *ctx = context; + int ret; + + if (user == NULL) { + if (userdb_blocking_iter_deinit(&ctx->iter) < 0) + ctx->failed = TRUE; + + do { + ctx->userdb = ctx->userdb->next; + } while (ctx->userdb != NULL && + ctx->userdb->userdb->iface->iterate_init == NULL); + if (ctx->userdb == NULL) { + /* iteration is finished */ + const char *str; + + str = t_strdup_printf("DONE\t%u\t%s\n", ctx->id, + ctx->failed ? "fail" : ""); + (void)o_stream_send_str(ctx->conn->output, str); + master_input_list_finish(ctx); + return; + } + + /* continue iterating next userdb */ + userdb_blocking_iter_init(ctx->userdb, + master_input_list_callback, ctx); + userdb_blocking_iter_next(ctx->iter); + return; + } + + T_BEGIN { + const char *str; + + str = t_strdup_printf("LIST\t%u\t%s\n", ctx->id, + str_tabescape(user)); + ret = o_stream_send_str(ctx->conn->output, str); + } T_END; + if (ret < 0) { + /* disconnected, don't bother finishing */ + master_input_list_finish(ctx); + return; + } + if (o_stream_get_buffer_used_size(ctx->conn->output) == 0) + userdb_blocking_iter_next(ctx->iter); +} + +static bool +master_input_list(struct auth_master_connection *conn, const char *args) +{ + struct auth_userdb *userdb = conn->auth->userdbs; + struct master_list_iter_ctx *ctx; + const char *str; + unsigned int id; + + /* <id> */ + if (*args == '\0') { + i_error("BUG: Master sent broken LIST"); + return FALSE; + } + id = strtoul(args, NULL, 10); + + while (userdb != NULL && userdb->userdb->iface->iterate_init == NULL) + userdb = userdb->next; + if (userdb == NULL) { + i_error("Trying to iterate users, but userdbs don't suppor it"); + str = t_strdup_printf("DONE\t%u\tfail", id); + (void)o_stream_send_str(conn->output, str); + return TRUE; + } + + ctx = i_new(struct master_list_iter_ctx, 1); + ctx->conn = conn; + ctx->userdb = userdb; + ctx->id = id; + + io_remove(&conn->io); + o_stream_set_flush_callback(conn->output, master_output_list, ctx); + ctx->iter = userdb_blocking_iter_init(ctx->userdb, + master_input_list_callback, ctx); + return TRUE; +} + static bool auth_master_input_line(struct auth_master_connection *conn, const char *line) { @@ -172,6 +290,8 @@ return master_input_request(conn, line + 8); else if (strncmp(line, "USER\t", 5) == 0) return master_input_user(conn, line + 5); + else if (strncmp(line, "LIST\t", 5) == 0) + return master_input_list(conn, line + 5); else if (strncmp(line, "CPID\t", 5) == 0) { i_error("Authentication client trying to connect to " "master socket");
--- a/src/auth/auth-worker-client.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/auth-worker-client.c Wed May 13 17:51:16 2009 -0400 @@ -25,6 +25,17 @@ struct ostream *output; }; +struct auth_worker_list_context { + struct auth_worker_client *client; + struct userdb_module *userdb; + struct userdb_iterate_context *iter; + unsigned int id; + bool sending, sent, done; +}; + +static void auth_worker_input(struct auth_worker_client *client); +static int auth_worker_output(struct auth_worker_client *client); + static void auth_worker_client_check_throttle(struct auth_worker_client *client) { @@ -395,6 +406,104 @@ lookup(auth_request, lookup_user_callback); } +static void list_iter_deinit(struct auth_worker_list_context *ctx) +{ + struct auth_worker_client *client = ctx->client; + string_t *str; + + str = t_str_new(32); + if (ctx->userdb->iface->iterate_deinit(ctx->iter) < 0) + str_printfa(str, "%u\tFAIL\n", ctx->id); + else + str_printfa(str, "%u\tOK\n", ctx->id); + auth_worker_send_reply(client, str); + + client->io = io_add(client->fd, IO_READ, auth_worker_input, client); + o_stream_set_flush_callback(client->output, auth_worker_output, client); + auth_worker_client_unref(&client); + i_free(ctx); +} + +static void list_iter_callback(const char *user, void *context) +{ + struct auth_worker_list_context *ctx = context; + string_t *str; + + if (user == NULL) { + if (ctx->sending) + ctx->done = TRUE; + else + list_iter_deinit(ctx); + return; + } + + T_BEGIN { + str = t_str_new(128); + str_printfa(str, "%u\t*\t%s\n", ctx->id, user); + o_stream_send(ctx->client->output, str_data(str), str_len(str)); + } T_END; + + if (ctx->sending) { + /* avoid recursively looping to this same function */ + ctx->sent = TRUE; + return; + } + + do { + ctx->sending = TRUE; + ctx->sent = FALSE; + ctx->userdb->iface->iterate_next(ctx->iter); + } while (ctx->sent && + o_stream_get_buffer_used_size(ctx->client->output) == 0); + ctx->sending = FALSE; + if (ctx->done) + list_iter_deinit(ctx); +} + +static int auth_worker_list_output(struct auth_worker_list_context *ctx) +{ + int ret; + + if ((ret = o_stream_flush(ctx->client->output)) < 0) { + list_iter_deinit(ctx); + return 1; + } + if (ret > 0) + ctx->userdb->iface->iterate_next(ctx->iter); + return 1; +} + +static void +auth_worker_handle_list(struct auth_worker_client *client, + unsigned int id, const char *args) +{ + struct auth_worker_list_context *ctx; + struct auth_userdb *userdb; + unsigned int num; + + userdb = client->auth->userdbs; + for (num = atoi(args); num > 0; num--) { + userdb = userdb->next; + if (userdb == NULL) { + i_error("BUG: LIST had invalid userdb num"); + return; + } + } + + ctx = i_new(struct auth_worker_list_context, 1); + ctx->client = client; + ctx->id = id; + ctx->userdb = userdb->userdb; + + io_remove(&ctx->client->io); + o_stream_set_flush_callback(ctx->client->output, + auth_worker_list_output, ctx); + client->refcount++; + ctx->iter = ctx->userdb->iface-> + iterate_init(userdb, list_iter_callback, ctx); + ctx->userdb->iface->iterate_next(ctx->iter); +} + static bool auth_worker_handle_line(struct auth_worker_client *client, const char *line) { @@ -416,7 +525,10 @@ auth_worker_handle_setcred(client, id, line + 8); else if (strncmp(line, "USER\t", 5) == 0) auth_worker_handle_user(client, id, line + 5); - + else if (strncmp(line, "LIST\t", 5) == 0) + auth_worker_handle_list(client, id, line + 5); + else + i_error("BUG: Auth-worker received unknown command: %s", line); return TRUE; }
--- a/src/auth/auth-worker-server.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/auth-worker-server.c Wed May 13 17:51:16 2009 -0400 @@ -23,8 +23,8 @@ unsigned int id; time_t created; const char *data_str; - struct auth_request *auth_request; - auth_worker_callback_t *callback; + auth_worker_callback_t *callback; + void *context; }; struct auth_worker_connection { @@ -189,15 +189,12 @@ if (conn->request == NULL) idle_count--; - if (conn->request != NULL) T_BEGIN { - struct auth_request *auth_request = conn->request->auth_request; - - auth_request_log_error(auth_request, "worker-server", - "Aborted: %s", reason); - conn->request->callback(auth_request, t_strdup_printf( - "FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE)); - auth_request_unref(&conn->request->auth_request); - } T_END; + if (conn->request != NULL) { + i_error("auth worker: Aborted request: %s", reason); + conn->request->callback(t_strdup_printf( + "FAIL\t%d", PASSDB_RESULT_INTERNAL_FAILURE), + conn->request->context); + } io_remove(&conn->io); i_stream_destroy(&conn->input); @@ -236,14 +233,18 @@ struct auth_worker_request *request, const char *line) { - conn->request = NULL; - timeout_remove(&conn->to); - conn->to = timeout_add(AUTH_WORKER_MAX_IDLE_SECS * 1000, - auth_worker_idle_timeout, conn); - idle_count++; + if (strncmp(line, "*\t", 2) == 0) { + /* multi-line reply, not finished yet */ + } else { + conn->request = NULL; + timeout_remove(&conn->to); + conn->to = timeout_add(AUTH_WORKER_MAX_IDLE_SECS * 1000, + auth_worker_idle_timeout, conn); + idle_count++; + } - request->callback(request->auth_request, line); - auth_request_unref(&request->auth_request); + if (!request->callback(line, request->context) && conn->io != NULL) + io_remove(&conn->io); } static void worker_input(struct auth_worker_connection *conn) @@ -301,20 +302,19 @@ auth_worker_request_send_next(conn); } -void auth_worker_call(struct auth_request *auth_request, - struct auth_stream_reply *data, - auth_worker_callback_t *callback) +struct auth_worker_connection * +auth_worker_call(struct auth *auth, pool_t pool, + struct auth_stream_reply *data, + auth_worker_callback_t *callback, void *context) { struct auth_worker_connection *conn; struct auth_worker_request *request; - request = p_new(auth_request->pool, struct auth_worker_request, 1); + request = p_new(pool, struct auth_worker_request, 1); request->created = ioloop_time; - request->data_str = p_strdup(auth_request->pool, - auth_stream_reply_export(data)); - request->auth_request = auth_request; + request->data_str = p_strdup(pool, auth_stream_reply_export(data)); request->callback = callback; - auth_request_ref(auth_request); + request->context = context; if (aqueue_count(worker_request_queue) > 0) { /* requests are already being queued, no chance of @@ -324,7 +324,7 @@ conn = auth_worker_find_free(); if (conn == NULL) { /* no free connections, create a new one */ - conn = auth_worker_create(auth_request->auth); + conn = auth_worker_create(auth); } } if (conn != NULL) @@ -333,31 +333,29 @@ /* reached the limit, queue the request */ aqueue_append(worker_request_queue, &request); } + return conn; } -void auth_worker_server_init(struct auth *auth) +void auth_worker_server_resume_input(struct auth_worker_connection *conn) { - if (array_is_created(&connections)) { - /* already initialized */ - return; - } + if (conn->io == NULL) + conn->io = io_add(conn->fd, IO_READ, worker_input, conn); +} +void auth_worker_server_init(void) +{ worker_socket_path = "auth-worker"; i_array_init(&worker_request_array, 128); worker_request_queue = aqueue_init(&worker_request_array.arr); i_array_init(&connections, 16); - (void)auth_worker_create(auth); } void auth_worker_server_deinit(void) { struct auth_worker_connection **connp, *conn; - if (!array_is_created(&connections)) - return; - while (array_count(&connections) > 0) { connp = array_idx_modifiable(&connections, 0); conn = *connp;
--- a/src/auth/auth-worker-server.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/auth-worker-server.h Wed May 13 17:51:16 2009 -0400 @@ -4,14 +4,15 @@ struct auth_request; struct auth_stream_reply; -typedef void auth_worker_callback_t(struct auth_request *request, - const char *reply); +typedef bool auth_worker_callback_t(const char *reply, void *context); -void auth_worker_call(struct auth_request *auth_request, - struct auth_stream_reply *data, - auth_worker_callback_t *callback); +struct auth_worker_connection * +auth_worker_call(struct auth *auth, pool_t pool, + struct auth_stream_reply *data, + auth_worker_callback_t *callback, void *context); +void auth_worker_server_resume_input(struct auth_worker_connection *conn); -void auth_worker_server_init(struct auth *auth); +void auth_worker_server_init(void); void auth_worker_server_deinit(void); #endif
--- a/src/auth/db-ldap.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/db-ldap.c Wed May 13 17:51:16 2009 -0400 @@ -105,6 +105,8 @@ DEF_STR(user_filter), DEF_STR(pass_attrs), DEF_STR(pass_filter), + DEF_STR(iterate_attrs), + DEF_STR(iterate_filter), DEF_STR(default_pass_scheme), { 0, NULL, 0 } @@ -138,6 +140,8 @@ MEMBER(user_filter) "(&(objectClass=posixAccount)(uid=%u))", MEMBER(pass_attrs) "uid=user,userPassword=password", MEMBER(pass_filter) "(&(objectClass=posixAccount)(uid=%u))", + MEMBER(iterate_attrs) "uid=user", + MEMBER(iterate_filter) "(objectClass=posixAccount)", MEMBER(default_pass_scheme) "crypt" }; @@ -309,7 +313,7 @@ srequest->filter, srequest->attributes, 0); if (request->msgid == -1) { auth_request_log_error(request->auth_request, "ldap", - "ldap_search() failed (filter %s): %s", + "ldap_search(%s) parsing failed: %s", srequest->filter, ldap_get_error(conn)); if (ldap_handle_error(conn) < 0) { /* broken request, remove it */ @@ -511,11 +515,35 @@ } } +static struct ldap_request * +db_ldap_find_request(struct ldap_connection *conn, int msgid, + unsigned int *idx_r) +{ + struct ldap_request *const *requests, *request = NULL; + unsigned int i, count; + + count = aqueue_count(conn->request_queue); + if (count == 0) + return NULL; + + requests = array_idx(&conn->request_array, 0); + for (i = 0; i < count; i++) { + request = requests[aqueue_idx(conn->request_queue, i)]; + if (request->msgid == msgid) { + *idx_r = i; + return request; + } + if (request->msgid == -1) + break; + } + return NULL; +} + static void db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res) { - struct ldap_request *const *requests, *request = NULL; - unsigned int i, count; + struct ldap_request *request; + unsigned int idx; int msgid, ret; msgid = ldap_msgid(res); @@ -524,32 +552,38 @@ return; } - count = aqueue_count(conn->request_queue); - requests = count == 0 ? NULL : array_idx(&conn->request_array, 0); - for (i = 0; i < count; i++) { - request = requests[aqueue_idx(conn->request_queue, i)]; - if (request->msgid == msgid) - break; - if (request->msgid == -1) { - request = NULL; - break; - } - } + request = db_ldap_find_request(conn, msgid, &idx); if (request == NULL) { i_error("LDAP: Reply with unknown msgid %d", msgid); return; } + i_assert(conn->pending_count > 0); if (request->type == LDAP_REQUEST_TYPE_BIND) { i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING); i_assert(conn->pending_count == 1); conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH; + } else { + switch (ldap_msgtype(res)) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_RESULT: + break; + case LDAP_RES_SEARCH_REFERENCE: + /* we're going to ignore this */ + return; + default: + i_error("LDAP: Reply with unexpected type %d", + ldap_msgtype(res)); + return; + } } - i_assert(conn->pending_count > 0); - conn->pending_count--; - aqueue_delete(conn->request_queue, i); - - ret = ldap_result2error(conn->ld, res, 0); + if (ldap_msgtype(res) == LDAP_RES_SEARCH_ENTRY) + ret = LDAP_SUCCESS; + else { + conn->pending_count--; + aqueue_delete(conn->request_queue, idx); + ret = ldap_result2error(conn->ld, res, 0); + } if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) { /* handle search failures here */ struct ldap_request_search *srequest = @@ -565,9 +599,9 @@ request->callback(conn, request, res); } T_END; - if (i > 0) { + if (idx > 0) { /* see if there are timed out requests */ - db_ldap_abort_requests(conn, i, + db_ldap_abort_requests(conn, idx, DB_LDAP_REQUEST_LOST_TIMEOUT_SECS, TRUE, "Request lost"); } @@ -579,16 +613,16 @@ LDAPMessage *res; int ret; - for (;;) { + do { if (conn->ld == NULL) return; memset(&timeout, 0, sizeof(timeout)); - ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, &timeout, &res); + ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &res); #ifdef OPENLDAP_ASYNC_WORKAROUND if (ret == 0) { /* try again, there may be another in buffer */ - ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, + ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &res); } #endif @@ -597,10 +631,13 @@ db_ldap_handle_result(conn, res); ldap_msgfree(res); - } + } while (conn->io != NULL); conn->last_reply_stamp = ioloop_time; - if (ret == 0) { + if (ret > 0) { + /* input disabled, continue once it's enabled */ + i_assert(conn->io == NULL); + } else if (ret == 0) { /* send more requests */ while (db_ldap_request_queue_next(conn)) ; @@ -858,6 +895,19 @@ return 0; } +void db_ldap_enable_input(struct ldap_connection *conn, bool enable) +{ + if (!enable) { + if (conn->io != NULL) + io_remove(&conn->io); + } else { + if (conn->io == NULL && conn->fd != -1) { + conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); + ldap_input(conn); + } + } +} + static void db_ldap_disconnect_timeout(struct ldap_connection *conn) { db_ldap_abort_requests(conn, -1U, @@ -967,7 +1017,7 @@ struct var_expand_table * db_ldap_value_get_var_expand_table(struct auth_request *auth_request) { - const struct var_expand_table *auth_table; + const struct var_expand_table *auth_table = NULL; struct var_expand_table *table; unsigned int count;
--- a/src/auth/db-ldap.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/db-ldap.h Wed May 13 17:51:16 2009 -0400 @@ -61,6 +61,8 @@ const char *user_filter; const char *pass_attrs; const char *pass_filter; + const char *iterate_attrs; + const char *iterate_filter; const char *default_pass_scheme; @@ -85,6 +87,12 @@ db_search_callback_t *callback; struct auth_request *auth_request; + + /* If expect_one_reply=TRUE, this contains the first LDAP entry. + If another one comes, we'll return an error. */ + LDAPMessage *first_entry; + + unsigned int expect_one_reply:1; }; struct ldap_request_search { @@ -139,8 +147,8 @@ /* Timestamp when we last received a reply */ time_t last_reply_stamp; - char **pass_attr_names, **user_attr_names; - struct hash_table *pass_attr_map, *user_attr_map; + char **pass_attr_names, **user_attr_names, **iterate_attr_names; + struct hash_table *pass_attr_map, *user_attr_map, *iterate_attr_map; }; /* Send/queue request */ @@ -156,6 +164,8 @@ int db_ldap_connect(struct ldap_connection *conn); +void db_ldap_enable_input(struct ldap_connection *conn, bool enable); + struct var_expand_table * db_ldap_value_get_var_expand_table(struct auth_request *auth_request);
--- a/src/auth/db-passwd-file.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/db-passwd-file.h Wed May 13 17:51:16 2009 -0400 @@ -16,6 +16,7 @@ struct passwd_file { struct db_passwd_file *db; pool_t pool; + int refcount; char *path; time_t stamp;
--- a/src/auth/db-sql.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/db-sql.c Wed May 13 17:51:16 2009 -0400 @@ -21,6 +21,7 @@ DEF_STR(password_query), DEF_STR(user_query), DEF_STR(update_query), + DEF_STR(iterate_query), DEF_STR(default_pass_scheme), { 0, NULL, 0 } @@ -32,6 +33,7 @@ MEMBER(password_query) "SELECT username, domain, password FROM users WHERE username = '%n' AND domain = '%d'", MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE username = '%n' AND domain = '%d'", MEMBER(update_query) "UPDATE users SET password = '%w' WHERE username = '%n' AND domain = '%d'", + MEMBER(iterate_query) "SELECT username, domain FROM users", MEMBER(default_pass_scheme) "MD5" };
--- a/src/auth/db-sql.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/db-sql.h Wed May 13 17:51:16 2009 -0400 @@ -9,6 +9,7 @@ const char *password_query; const char *user_query; const char *update_query; + const char *iterate_query; const char *default_pass_scheme; };
--- a/src/auth/main.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/main.c Wed May 13 17:51:16 2009 -0400 @@ -76,6 +76,7 @@ child_wait_init(); mech_init(auth->set); password_schemes_init(); + auth_worker_server_init(); auth_init(auth); auth_request_handler_init(); auth_master_connections_init();
--- a/src/auth/passdb-blocking.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/passdb-blocking.c Wed May 13 17:51:16 2009 -0400 @@ -59,13 +59,16 @@ return PASSDB_RESULT_INTERNAL_FAILURE; } -static void -verify_plain_callback(struct auth_request *request, const char *reply) +static bool +verify_plain_callback(const char *reply, void *context) { + struct auth_request *request = context; enum passdb_result result; result = auth_worker_reply_parse(request, reply); auth_request_verify_plain_callback(result, request); + auth_request_unref(&request); + return TRUE; } void passdb_blocking_verify_plain(struct auth_request *request) @@ -81,12 +84,14 @@ auth_stream_reply_add(reply, NULL, request->mech_password); auth_request_export(request, reply); - auth_worker_call(request, reply, verify_plain_callback); + auth_request_ref(request); + auth_worker_call(request->auth, request->pool, reply, + verify_plain_callback, request); } -static void -lookup_credentials_callback(struct auth_request *request, const char *reply) +static bool lookup_credentials_callback(const char *reply, void *context) { + struct auth_request *request = context; enum passdb_result result; const char *password = NULL, *scheme = NULL; @@ -105,6 +110,8 @@ passdb_handle_credentials(result, password, scheme, auth_request_lookup_credentials_callback, request); + auth_request_unref(&request); + return TRUE; } void passdb_blocking_lookup_credentials(struct auth_request *request) @@ -120,16 +127,21 @@ auth_stream_reply_add(reply, NULL, request->credentials_scheme); auth_request_export(request, reply); - auth_worker_call(request, reply, lookup_credentials_callback); + auth_request_ref(request); + auth_worker_call(request->auth, request->pool, reply, + lookup_credentials_callback, request); } -static void -set_credentials_callback(struct auth_request *request, const char *reply) +static bool +set_credentials_callback(const char *reply, void *context) { + struct auth_request *request = context; bool success; success = strcmp(reply, "OK") == 0 || strncmp(reply, "OK\t", 3) == 0; request->private_callback.set_credentials(success, request); + auth_request_unref(&request); + return TRUE; } void passdb_blocking_set_credentials(struct auth_request *request, @@ -143,5 +155,7 @@ auth_stream_reply_add(reply, NULL, new_credentials); auth_request_export(request, reply); - auth_worker_call(request, reply, set_credentials_callback); + auth_request_ref(request); + auth_worker_call(request->auth, request->pool, reply, + set_credentials_callback, request); }
--- a/src/auth/passdb-ldap.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/passdb-ldap.c Wed May 13 17:51:16 2009 -0400 @@ -33,46 +33,10 @@ verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; } callback; + + unsigned int entries; }; -static LDAPMessage * -handle_request_get_entry(struct ldap_connection *conn, - struct auth_request *auth_request, - struct passdb_ldap_request *request, LDAPMessage *res) -{ - enum passdb_result passdb_result; - LDAPMessage *entry; - - passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; - - if (res != NULL) { - /* LDAP search was successful */ - entry = ldap_first_entry(conn->ld, res); - if (entry == NULL) { - passdb_result = PASSDB_RESULT_USER_UNKNOWN; - auth_request_log_info(auth_request, "ldap", - "unknown user"); - } else { - if (ldap_next_entry(conn->ld, entry) == NULL) { - /* success */ - return entry; - } - - auth_request_log_error(auth_request, "ldap", - "Multiple replies found for user"); - } - } - - if (auth_request->credentials_scheme != NULL) { - request->callback.lookup_credentials(passdb_result, NULL, 0, - auth_request); - } else { - request->callback.verify_plain(passdb_result, auth_request); - } - auth_request_unref(&auth_request); - return NULL; -} - static void ldap_query_save_result(struct ldap_connection *conn, LDAPMessage *entry, struct auth_request *auth_request) @@ -89,29 +53,24 @@ } static void -ldap_lookup_pass_callback(struct ldap_connection *conn, - struct ldap_request *request, LDAPMessage *res) +ldap_lookup_finish(struct auth_request *auth_request, + struct passdb_ldap_request *ldap_request, + LDAPMessage *res) { - struct passdb_ldap_request *ldap_request = - (struct passdb_ldap_request *)request; - struct auth_request *auth_request = request->auth_request; enum passdb_result passdb_result; - LDAPMessage *entry; - const char *password, *scheme; + const char *password = NULL, *scheme; int ret; - entry = handle_request_get_entry(conn, auth_request, ldap_request, res); - if (entry == NULL) - return; - - /* got first LDAP entry */ - passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; - password = NULL; - - ldap_query_save_result(conn, entry, auth_request); - if (ldap_next_entry(conn->ld, entry) != NULL) { + if (res == NULL) { + passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; + } else if (ldap_request->entries == 0) { + passdb_result = PASSDB_RESULT_USER_UNKNOWN; + auth_request_log_info(auth_request, "ldap", + "unknown user"); + } else if (ldap_request->entries > 1) { auth_request_log_error(auth_request, "ldap", "pass_filter matched multiple objects, aborting"); + passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } else if (auth_request->passdb_password == NULL && !auth_request->no_password) { auth_request_log_info(auth_request, "ldap", @@ -144,7 +103,26 @@ ldap_request->callback.verify_plain(passdb_result, auth_request); } - auth_request_unref(&auth_request); +} + +static void +ldap_lookup_pass_callback(struct ldap_connection *conn, + struct ldap_request *request, LDAPMessage *res) +{ + struct passdb_ldap_request *ldap_request = + (struct passdb_ldap_request *)request; + struct auth_request *auth_request = request->auth_request; + + if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { + ldap_lookup_finish(auth_request, ldap_request, res); + auth_request_unref(&auth_request); + return; + } + + if (ldap_request->entries++ == 0) { + /* first entry */ + ldap_query_save_result(conn, res, auth_request); + } } static void @@ -208,6 +186,35 @@ db_ldap_request(conn, &brequest->request); } +static void +ldap_bind_lookup_dn_fail(struct auth_request *auth_request, + struct passdb_ldap_request *request, + LDAPMessage *res) +{ + enum passdb_result passdb_result; + + if (res == NULL) + passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; + else if (request->entries == 0) { + passdb_result = PASSDB_RESULT_USER_UNKNOWN; + auth_request_log_info(auth_request, "ldap", + "unknown user"); + } else { + i_assert(request->entries > 1); + auth_request_log_error(auth_request, "ldap", + "pass_filter matched multiple objects, aborting"); + passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; + } + + if (auth_request->credentials_scheme != NULL) { + request->callback.lookup_credentials(passdb_result, NULL, 0, + auth_request); + } else { + request->callback.verify_plain(passdb_result, auth_request); + } + auth_request_unref(&auth_request); +} + static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn, struct ldap_request *ldap_request, LDAPMessage *res) @@ -219,12 +226,18 @@ LDAPMessage *entry; char *dn; - entry = handle_request_get_entry(conn, auth_request, - passdb_ldap_request, res); - if (entry == NULL) + if (res != NULL && ldap_msgtype(res) != LDAP_RES_SEARCH_RESULT) { + if (passdb_ldap_request->entries++ == 0) { + /* first entry */ + ldap_query_save_result(conn, res, auth_request); + } return; + } - ldap_query_save_result(conn, entry, auth_request); + if (res == NULL || passdb_ldap_request->entries != 0) { + ldap_bind_lookup_dn_fail(auth_request, passdb_ldap_request, res); + return; + } /* convert search request to bind request */ brequest = &passdb_ldap_request->request.bind;
--- a/src/auth/passdb.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/passdb.c Wed May 13 17:51:16 2009 -0400 @@ -202,11 +202,6 @@ i_assert(passdb->passdb->default_pass_scheme != NULL || passdb->passdb->cache_key == NULL); - - if (passdb->passdb->blocking && !worker) { - /* blocking passdb - we need an auth server */ - auth_worker_server_init(passdb->auth); - } } void passdb_deinit(struct auth_passdb *passdb)
--- a/src/auth/userdb-blocking.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-blocking.c Wed May 13 17:51:16 2009 -0400 @@ -8,8 +8,16 @@ #include <stdlib.h> -static void user_callback(struct auth_request *request, const char *reply) +struct blocking_userdb_iterate_context { + struct userdb_iterate_context ctx; + pool_t pool; + struct auth_worker_connection *conn; + bool next; +}; + +static bool user_callback(const char *reply, void *context) { + struct auth_request *request = context; enum userdb_result result; if (strncmp(reply, "FAIL\t", 5) == 0) @@ -26,6 +34,8 @@ } auth_request_userdb_callback(result, request); + auth_request_unref(&request); + return TRUE; } void userdb_blocking_lookup(struct auth_request *request) @@ -37,5 +47,69 @@ auth_stream_reply_add(reply, NULL, dec2str(request->userdb->num)); auth_request_export(request, reply); - auth_worker_call(request, reply, user_callback); + auth_request_ref(request); + auth_worker_call(request->auth, request->pool, reply, + user_callback, request); +} + +static bool iter_callback(const char *reply, void *context) +{ + struct blocking_userdb_iterate_context *ctx = context; + + if (strncmp(reply, "*\t", 2) == 0) { + ctx->next = FALSE; + ctx->ctx.callback(reply + 2, ctx->ctx.context); + return ctx->next; + } else if (strcmp(reply, "OK") == 0) { + ctx->ctx.callback(NULL, ctx->ctx.context); + return TRUE; + } else { + ctx->ctx.failed = TRUE; + ctx->ctx.callback(NULL, ctx->ctx.context); + return TRUE; + } } + +struct userdb_iterate_context * +userdb_blocking_iter_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context) +{ + struct blocking_userdb_iterate_context *ctx; + struct auth_stream_reply *reply; + pool_t pool; + + reply = auth_stream_reply_init(pool_datastack_create()); + auth_stream_reply_add(reply, "LIST", NULL); + auth_stream_reply_add(reply, NULL, dec2str(userdb->num)); + + pool = pool_alloconly_create("userdb iter", 512); + ctx = p_new(pool, struct blocking_userdb_iterate_context, 1); + ctx->ctx.userdb = userdb->userdb; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + ctx->pool = pool; + + ctx->conn = auth_worker_call(userdb->auth, pool, reply, + iter_callback, ctx); + return &ctx->ctx; +} + +void userdb_blocking_iter_next(struct userdb_iterate_context *_ctx) +{ + struct blocking_userdb_iterate_context *ctx = + (struct blocking_userdb_iterate_context *)_ctx; + + ctx->next = TRUE; + auth_worker_server_resume_input(ctx->conn); +} + +int userdb_blocking_iter_deinit(struct userdb_iterate_context **_ctx) +{ + struct blocking_userdb_iterate_context *ctx = + (struct blocking_userdb_iterate_context *)*_ctx; + int ret = ctx->ctx.failed ? -1 : 0; + + *_ctx = NULL; + pool_unref(&ctx->pool); + return ret; +}
--- a/src/auth/userdb-blocking.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-blocking.h Wed May 13 17:51:16 2009 -0400 @@ -3,4 +3,10 @@ void userdb_blocking_lookup(struct auth_request *request); +struct userdb_iterate_context * +userdb_blocking_iter_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context); +void userdb_blocking_iter_next(struct userdb_iterate_context *ctx); +int userdb_blocking_iter_deinit(struct userdb_iterate_context **ctx); + #endif
--- a/src/auth/userdb-checkpassword.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-checkpassword.c Wed May 13 17:51:16 2009 -0400 @@ -261,6 +261,10 @@ checkpassword_deinit, checkpassword_lookup, + + NULL, + NULL, + NULL }; #else struct userdb_module_interface userdb_checkpassword = {
--- a/src/auth/userdb-ldap.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-ldap.c Wed May 13 17:51:16 2009 -0400 @@ -22,7 +22,22 @@ struct userdb_ldap_request { struct ldap_request_search request; - userdb_callback_t *userdb_callback; + 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; + struct auth *auth; + struct ldap_connection *conn; + bool continued, in_callback; }; static void @@ -42,6 +57,30 @@ } } +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_info(auth_request, "ldap", + "unknown user"); + } else if (urequest->entries > 1) { + auth_request_log_error(auth_request, "ldap", + "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) @@ -50,28 +89,17 @@ (struct userdb_ldap_request *) request; struct auth_request *auth_request = urequest->request.request.auth_request; - LDAPMessage *entry; - enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE; - if (res != NULL) { - entry = ldap_first_entry(conn->ld, res); - if (entry == NULL) { - result = USERDB_RESULT_USER_UNKNOWN; - auth_request_log_info(auth_request, "ldap", - "Unknown user"); - } else { - ldap_query_get_result(conn, entry, auth_request); - if (ldap_next_entry(conn->ld, entry) == NULL) - result = USERDB_RESULT_OK; - else { - auth_request_log_error(auth_request, "ldap", - "Multiple replies found for user"); - } - } + if (res == NULL || ldap_msgtype(res) == LDAP_RES_SEARCH_RESULT) { + userdb_ldap_lookup_finish(auth_request, urequest, res); + auth_request_unref(&auth_request); + return; } - urequest->userdb_callback(result, auth_request); - auth_request_unref(&auth_request); + if (urequest->entries++ == 0) { + /* first entry */ + ldap_query_get_result(conn, res, auth_request); + } } static void userdb_ldap_lookup(struct auth_request *auth_request, @@ -114,6 +142,97 @@ 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; + ctx->ctx.callback(NULL, ctx->ctx.context); + return; + } + + ctx->in_callback = TRUE; + ldap_iter = db_ldap_result_iterate_init(conn, res, + request->auth_request, + conn->iterate_attr_map); + while (db_ldap_result_iterate_next_all(ldap_iter, &name, &values)) { + for (; *values != NULL; values++) { + ctx->continued = FALSE; + ctx->ctx.callback(*values, ctx->ctx.context); + } + } + if (!ctx->continued) + db_ldap_enable_input(conn, FALSE); + ctx->in_callback = FALSE; +} + +static struct userdb_iterate_context * +userdb_ldap_iterate_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context) +{ + struct ldap_userdb_module *module = + (struct ldap_userdb_module *)userdb->userdb; + 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; + + ctx = i_new(struct ldap_userdb_iterate_context, 1); + ctx->ctx.userdb = userdb->userdb; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + ctx->auth = userdb->auth; + ctx->conn = conn; + request = &ctx->request; + request->ctx = ctx; + + request->request.request.auth_request = + auth_request_new_dummy(userdb->auth); + request->request.base = conn->set.base; + request->request.filter = conn->set.iterate_filter; + request->request.attributes = conn->iterate_attr_names; + + if (userdb->auth->set->debug) { + i_info("ldap: iterate: base=%s scope=%s filter=%s fields=%s", + conn->set.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); + auth_request_unref(&ctx->request.request.request.auth_request); + i_free(ctx); + return ret; +} + static struct userdb_module * userdb_ldap_preinit(struct auth_userdb *auth_userdb, const char *args) { @@ -125,9 +244,15 @@ conn->user_attr_map = hash_table_create(default_pool, conn->pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + conn->iterate_attr_map = + hash_table_create(default_pool, conn->pool, 0, str_hash, + (hash_cmp_callback_t *)strcmp); 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.cache_key = auth_cache_parse_key(auth_userdb->auth->pool, t_strconcat(conn->set.base, @@ -159,7 +284,11 @@ userdb_ldap_init, userdb_ldap_deinit, - userdb_ldap_lookup + userdb_ldap_lookup, + + userdb_ldap_iterate_init, + userdb_ldap_iterate_next, + userdb_ldap_iterate_deinit }; #else struct userdb_module_interface userdb_ldap = {
--- a/src/auth/userdb-nss.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-nss.c Wed May 13 17:51:16 2009 -0400 @@ -147,7 +147,11 @@ NULL, userdb_nss_deinit, - userdb_nss_lookup + userdb_nss_lookup, + + NULL, + NULL, + NULL }; #else struct userdb_module_interface userdb_nss = {
--- a/src/auth/userdb-passwd-file.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-passwd-file.c Wed May 13 17:51:16 2009 -0400 @@ -5,13 +5,23 @@ #ifdef USERDB_PASSWD_FILE +#include "istream.h" #include "str.h" #include "auth-cache.h" #include "var-expand.h" #include "db-passwd-file.h" +#include <unistd.h> +#include <fcntl.h> + #define PASSWD_FILE_CACHE_KEY "%u" +struct passwd_file_userdb_iterate_context { + struct userdb_iterate_context ctx; + struct istream *input; + char *path; +}; + struct passwd_file_userdb_module { struct userdb_module module; @@ -73,6 +83,66 @@ callback(USERDB_RESULT_OK, auth_request); } +static struct userdb_iterate_context * +passwd_file_iterate_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context) +{ + struct passwd_file_userdb_module *module = + (struct passwd_file_userdb_module *)userdb; + struct passwd_file_userdb_iterate_context *ctx; + int fd; + + ctx = i_new(struct passwd_file_userdb_iterate_context, 1); + ctx->ctx.userdb = userdb->userdb; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + ctx->path = i_strdup(module->pwf->default_file->path); + + /* for now we support only a single passwd-file */ + fd = open(ctx->path, O_RDONLY); + if (fd == -1) { + i_error("open(%s) failed: %m", ctx->path); + ctx->ctx.failed = TRUE; + } else { + ctx->input = i_stream_create_fd(fd, (size_t)-1, TRUE); + } + return &ctx->ctx; +} + +static void passwd_file_iterate_next(struct userdb_iterate_context *_ctx) +{ + struct passwd_file_userdb_iterate_context *ctx = + (struct passwd_file_userdb_iterate_context *)_ctx; + const char *line; + + if (ctx->input == NULL) + line = NULL; + else { + line = i_stream_read_next_line(ctx->input); + if (line == NULL && ctx->input->stream_errno != 0) { + i_error("read(%s) failed: %m", ctx->path); + _ctx->failed = TRUE; + } + } + if (line == NULL) + _ctx->callback(NULL, _ctx->context); + else T_BEGIN { + _ctx->callback(t_strcut(line, ':'), _ctx->context); + } T_END; +} + +static int passwd_file_iterate_deinit(struct userdb_iterate_context *_ctx) +{ + struct passwd_file_userdb_iterate_context *ctx = + (struct passwd_file_userdb_iterate_context *)_ctx; + int ret = _ctx->failed ? -1 : 0; + + i_stream_destroy(&ctx->input); + i_free(ctx->path); + i_free(ctx); + return ret; +} + static struct userdb_module * passwd_file_preinit(struct auth_userdb *auth_userdb, const char *args) { @@ -135,7 +205,11 @@ passwd_file_init, passwd_file_deinit, - passwd_file_lookup + passwd_file_lookup, + + passwd_file_iterate_init, + passwd_file_iterate_next, + passwd_file_iterate_deinit }; #else struct userdb_module_interface userdb_passwd_file = {
--- a/src/auth/userdb-passwd.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-passwd.c Wed May 13 17:51:16 2009 -0400 @@ -5,6 +5,7 @@ #ifdef USERDB_PASSWD +#include "ioloop.h" #include "userdb-static.h" #include <pwd.h> @@ -16,6 +17,14 @@ struct userdb_static_template *tmpl; }; +struct passwd_userdb_iterate_context { + struct userdb_iterate_context ctx; + struct passwd_userdb_iterate_context *next_waiting; +}; + +static struct passwd_userdb_iterate_context *cur_userdb_iter = NULL; +static struct timeout *cur_userdb_iter_to = NULL; + static void passwd_lookup(struct auth_request *auth_request, userdb_callback_t *callback) { @@ -59,6 +68,72 @@ callback(USERDB_RESULT_OK, auth_request); } +static struct userdb_iterate_context * +passwd_iterate_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context) +{ + struct passwd_userdb_iterate_context *ctx; + + ctx = i_new(struct passwd_userdb_iterate_context, 1); + ctx->ctx.userdb = userdb->userdb; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + setpwent(); + + if (cur_userdb_iter == NULL) + cur_userdb_iter = ctx; + return &ctx->ctx; +} + +static void passwd_iterate_next(struct userdb_iterate_context *_ctx) +{ + struct passwd_userdb_iterate_context *ctx = + (struct passwd_userdb_iterate_context *)_ctx; + struct passwd *pw; + + if (cur_userdb_iter != NULL && cur_userdb_iter != ctx) { + /* we can't support concurrent userdb iteration. + wait until the previous one is done */ + ctx->next_waiting = cur_userdb_iter->next_waiting; + cur_userdb_iter->next_waiting = ctx; + return; + } + + errno = 0; + pw = getpwent(); + if (pw == NULL) { + if (errno != 0) { + i_error("getpwent() failed: %m"); + _ctx->failed = TRUE; + } + _ctx->callback(NULL, _ctx->context); + } else { + _ctx->callback(pw->pw_name, _ctx->context); + } +} + +static void passwd_iterate_next_timeout(void *context ATTR_UNUSED) +{ + timeout_remove(&cur_userdb_iter_to); + passwd_iterate_next(&cur_userdb_iter->ctx); +} + +static int passwd_iterate_deinit(struct userdb_iterate_context *_ctx) +{ + struct passwd_userdb_iterate_context *ctx = + (struct passwd_userdb_iterate_context *)_ctx; + int ret = _ctx->failed ? -1 : 0; + + cur_userdb_iter = ctx->next_waiting; + i_free(ctx); + + if (cur_userdb_iter != NULL) { + cur_userdb_iter_to = + timeout_add(0, passwd_iterate_next_timeout, NULL); + } + return ret; +} + static struct userdb_module * passwd_passwd_preinit(struct auth_userdb *auth_userdb, const char *args) { @@ -85,7 +160,11 @@ NULL, NULL, - passwd_lookup + passwd_lookup, + + passwd_iterate_init, + passwd_iterate_next, + passwd_iterate_deinit }; #else struct userdb_module_interface userdb_passwd = {
--- a/src/auth/userdb-prefetch.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-prefetch.c Wed May 13 17:51:16 2009 -0400 @@ -47,7 +47,11 @@ NULL, NULL, - prefetch_lookup + prefetch_lookup, + + NULL, + NULL, + NULL }; #else struct userdb_module_interface userdb_prefetch = {
--- a/src/auth/userdb-sql.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-sql.c Wed May 13 17:51:16 2009 -0400 @@ -25,6 +25,16 @@ userdb_callback_t *callback; }; +struct sql_userdb_iterate_context { + struct userdb_iterate_context ctx; + struct sql_result *result; + unsigned int freed:1; + unsigned int call_iter:1; +}; + +static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx); +static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx); + static void sql_query_get_result(struct sql_result *result, struct auth_request *auth_request) @@ -106,6 +116,115 @@ sql_query_callback, sql_request); } +static void sql_iter_query_callback(struct sql_result *sql_result, + struct sql_userdb_iterate_context *ctx) +{ + ctx->result = sql_result; + sql_result_ref(sql_result); + + if (ctx->freed) + userdb_sql_iterate_deinit(&ctx->ctx); + else if (ctx->call_iter) + userdb_sql_iterate_next(&ctx->ctx); +} + +static struct userdb_iterate_context * +userdb_sql_iterate_init(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, void *context) +{ + struct sql_userdb_module *module = + (struct sql_userdb_module *)userdb; + struct sql_userdb_iterate_context *ctx; + + ctx = i_new(struct sql_userdb_iterate_context, 1); + ctx->ctx.userdb = userdb->userdb; + ctx->ctx.callback = callback; + ctx->ctx.context = context; + + sql_query(module->conn->db, module->conn->set.iterate_query, + sql_iter_query_callback, ctx); + return &ctx->ctx; +} + +static int userdb_sql_iterate_get_user(struct sql_userdb_iterate_context *ctx, + const char **user_r) +{ + const char *domain; + int idx; + + /* try user first */ + idx = sql_result_find_field(ctx->result, "user"); + if (idx == 0) { + *user_r = sql_result_get_field_value(ctx->result, idx); + return 0; + } + + /* username [+ domain]? */ + idx = sql_result_find_field(ctx->result, "username"); + if (idx < 0) { + /* no user or username, fail */ + return -1; + } + + *user_r = sql_result_get_field_value(ctx->result, idx); + if (*user_r == NULL) + return 0; + + domain = sql_result_find_field_value(ctx->result, "domain"); + if (domain != NULL) + *user_r = t_strconcat(*user_r, "@", domain, NULL); + return 0; +} + +static void userdb_sql_iterate_next(struct userdb_iterate_context *_ctx) +{ + struct sql_userdb_iterate_context *ctx = + (struct sql_userdb_iterate_context *)_ctx; + const char *user; + int ret; + + if (ctx->result == NULL) { + /* query not finished yet */ + ctx->call_iter = TRUE; + return; + } + + ret = sql_result_next_row(ctx->result); + if (ret > 0) { + if (userdb_sql_iterate_get_user(ctx, &user) < 0) + i_error("sql: Iterate query didn't return 'user' field"); + else if (user == NULL) + i_error("sql: Iterate query returned NULL user"); + else { + _ctx->callback(user, _ctx->context); + return; + } + _ctx->failed = TRUE; + } else if (ret < 0) { + i_error("sql: Iterate query failed: %s", + sql_result_get_error(ctx->result)); + _ctx->failed = TRUE; + } + _ctx->callback(NULL, _ctx->context); +} + +static int userdb_sql_iterate_deinit(struct userdb_iterate_context *_ctx) +{ + struct sql_userdb_iterate_context *ctx = + (struct sql_userdb_iterate_context *)_ctx; + int ret = _ctx->failed ? -1 : 0; + + if (ctx->result == NULL) { + /* sql query hasn't finished yet */ + ctx->freed = TRUE; + } else { + if (ctx->result != NULL) + sql_result_unref(ctx->result); + i_free(ctx); + } + return ret; +} + static struct userdb_module * userdb_sql_preinit(struct auth_userdb *auth_userdb, const char *args) { @@ -149,7 +268,11 @@ userdb_sql_init, userdb_sql_deinit, - userdb_sql_lookup + userdb_sql_lookup, + + userdb_sql_iterate_init, + userdb_sql_iterate_next, + userdb_sql_iterate_deinit }; #else struct userdb_module_interface userdb_sql = {
--- a/src/auth/userdb-static.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-static.c Wed May 13 17:51:16 2009 -0400 @@ -236,7 +236,11 @@ NULL, NULL, - static_lookup + static_lookup, + + NULL, + NULL, + NULL }; #else struct userdb_module_interface userdb_static = {
--- a/src/auth/userdb-vpopmail.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb-vpopmail.c Wed May 13 17:51:16 2009 -0400 @@ -170,7 +170,11 @@ NULL, NULL, - vpopmail_lookup + vpopmail_lookup, + + NULL, + NULL, + NULL }; #else struct userdb_module_interface userdb_vpopmail = {
--- a/src/auth/userdb.c Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb.c Wed May 13 17:51:16 2009 -0400 @@ -148,11 +148,6 @@ { if (userdb->userdb->iface->init != NULL) userdb->userdb->iface->init(userdb->userdb, userdb->args); - - if (userdb->userdb->blocking && !worker) { - /* blocking userdb - we need an auth server */ - auth_worker_server_init(userdb->auth); - } } void userdb_deinit(struct auth_userdb *userdb)
--- a/src/auth/userdb.h Wed May 13 12:40:58 2009 -0400 +++ b/src/auth/userdb.h Wed May 13 17:51:16 2009 -0400 @@ -14,6 +14,8 @@ typedef void userdb_callback_t(enum userdb_result result, struct auth_request *request); +/* user=NULL when there are no more users */ +typedef void userdb_iter_callback_t(const char *user, void *context); struct userdb_module { /* The caching key for this module, or NULL if caching isn't wanted. */ @@ -26,6 +28,13 @@ const struct userdb_module_interface *iface; }; +struct userdb_iterate_context { + struct userdb_module *userdb; + userdb_iter_callback_t *callback; + void *context; + bool failed; +}; + struct userdb_module_interface { const char *name; @@ -36,6 +45,13 @@ void (*lookup)(struct auth_request *auth_request, userdb_callback_t *callback); + + struct userdb_iterate_context * + (*iterate_init)(struct auth_userdb *userdb, + userdb_iter_callback_t *callback, + void *context); + void (*iterate_next)(struct userdb_iterate_context *ctx); + int (*iterate_deinit)(struct userdb_iterate_context *ctx); }; uid_t userdb_parse_uid(struct auth_request *request, const char *str);