]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Implemented support for listing all users in userdb.
authorTimo Sirainen <tss@iki.fi>
Wed, 13 May 2009 21:51:16 +0000 (17:51 -0400)
committerTimo Sirainen <tss@iki.fi>
Wed, 13 May 2009 21:51:16 +0000 (17:51 -0400)
--HG--
branch : HEAD

28 files changed:
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

index d908f9cdb0d3aeb3e6a0bb134e51b6e0003f2e9a..e137f738e6937b78535c710ad7119ce6aaaa3473 100644 (file)
@@ -129,6 +129,10 @@ base = uid=someone, dc=foo, dc=bar, dc=org
 # 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
index 915548d9dafe9bfb58bd4a32775293181d6c9214..8e40c1dfafe0e881390adb1598c58df5e38c369c 100644 (file)
 #  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
index a213dae0151ff29276b31b9f075544ba71ac9855..9358526cb0576515f0e3f1a5eb9c0d912ab8378e 100644 (file)
@@ -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 master_userdb_request {
        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 @@ master_input_user(struct auth_master_connection *conn, const char *args)
        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 @@ auth_master_input_line(struct auth_master_connection *conn, const char *line)
                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");
index 7df8b388579967fa2bfeceaef7c0a50072c0347a..3d78283c10b6662906961d9865422ac8b26bab62 100644 (file)
@@ -25,6 +25,17 @@ struct auth_worker_client {
        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 @@ auth_worker_handle_user(struct auth_worker_client *client,
                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_line(struct auth_worker_client *client, const char *line)
                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;
 }
 
index 13e472f196a9c62ca309504a35e9b0c77f6dcceb..df5c5d174e718f83f67d8e8777e06269d239b23d 100644 (file)
@@ -23,8 +23,8 @@ struct auth_worker_request {
        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 @@ static void auth_worker_destroy(struct auth_worker_connection **_conn,
        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 @@ static void auth_worker_request_handle(struct auth_worker_connection *conn,
                                       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 @@ static void worker_input(struct auth_worker_connection *conn)
                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 @@ void auth_worker_call(struct auth_request *auth_request,
                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 @@ void auth_worker_call(struct auth_request *auth_request,
                /* 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;
index 3e5e120b5dbd2c0c37d04610a9fa76ebf89b48f8..d9bae6c5c59fc7aa59e7d65db3c44620cfe09bb6 100644 (file)
@@ -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
index 83660b264ac0436917bc186bba5a14361d3982d1..58fae6a6ba1fa9bdfabc12d65ca4ed211f110eac 100644 (file)
@@ -105,6 +105,8 @@ static struct setting_def setting_defs[] = {
        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 @@ static struct ldap_settings default_ldap_settings = {
        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 @@ static int db_ldap_request_search(struct ldap_connection *conn,
                            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 void db_ldap_abort_requests(struct ldap_connection *conn,
        }
 }
 
-static void
-db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
+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 *request;
+       unsigned int idx;
        int msgid, ret;
 
        msgid = ldap_msgid(res);
@@ -524,32 +552,38 @@ db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
                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;
+               }
+       }
+       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);
        }
-       i_assert(conn->pending_count > 0);
-       conn->pending_count--;
-       aqueue_delete(conn->request_queue, i);
-
-       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 @@ db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
                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 @@ static void ldap_input(struct ldap_connection *conn)
        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 @@ static void ldap_input(struct ldap_connection *conn)
 
                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 @@ int db_ldap_connect(struct ldap_connection *conn)
        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 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
 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;
 
index 5e44e471efda49f04dea7ac75a77e539317758e1..0b51b85b7446c48f46a9581131d479b001e1be84 100644 (file)
@@ -61,6 +61,8 @@ struct ldap_settings {
        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 @@ struct ldap_request {
 
        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 @@ struct ldap_connection {
        /* 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 @@ void db_ldap_unref(struct ldap_connection **conn);
 
 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);
 
index dbb30bd93d07759065c7fe4a2134229861c02311..c3685441ebc33b8eb03d95171998397e3c7eec18 100644 (file)
@@ -16,6 +16,7 @@ struct passwd_user {
 struct passwd_file {
         struct db_passwd_file *db;
        pool_t pool;
+       int refcount;
 
        char *path;
        time_t stamp;
index 3e7cc125f86123dbe2595cf83eb7a96b8f198c38..1a651f5e3b406623c6f4370d8ca11cc03cc12b62 100644 (file)
@@ -21,6 +21,7 @@ static struct setting_def setting_defs[] = {
        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 @@ static struct sql_settings default_sql_settings = {
        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"
 };
 
index ddb3d559c0d2fbd7a6e7f6932e7d9f77b3f63296..7a58c97f42722599e94494ecbb6c0cf4f982532d 100644 (file)
@@ -9,6 +9,7 @@ struct sql_settings {
        const char *password_query;
        const char *user_query;
        const char *update_query;
+       const char *iterate_query;
        const char *default_pass_scheme;
 };
 
index d36f96c0539fd154de25d3bce8b20b01bd5f2bfc..ab6ea698f1cd4289d1fbc47c8f27570752494283 100644 (file)
@@ -76,6 +76,7 @@ static void main_init(void)
        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();
index 08663007af046b35cb40ecabdfafa7b3a80f0d5a..46d4f0bdeca6971a0a289692ba739f42a6a0523d 100644 (file)
@@ -59,13 +59,16 @@ auth_worker_reply_parse(struct auth_request *request, const char *reply)
        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 @@ void passdb_blocking_verify_plain(struct auth_request *request)
        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 @@ lookup_credentials_callback(struct auth_request *request, const char *reply)
        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 @@ void passdb_blocking_lookup_credentials(struct auth_request *request)
        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 @@ void passdb_blocking_set_credentials(struct auth_request *request,
        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);
 }
index f3e6302b2fefecb5e1af04e4209422c73443d04a..9c6ca625150853626187eaf4196fb4f78eeaa46f 100644 (file)
@@ -33,45 +33,9 @@ struct passdb_ldap_request {
                verify_plain_callback_t *verify_plain;
                 lookup_credentials_callback_t *lookup_credentials;
        } callback;
-};
-
-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;
-}
+       unsigned int entries;
+};
 
 static void
 ldap_query_save_result(struct ldap_connection *conn,
@@ -89,29 +53,24 @@ ldap_query_save_result(struct ldap_connection *conn,
 }
 
 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_lookup_pass_callback(struct ldap_connection *conn,
                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 @@ static void ldap_auth_bind(struct ldap_connection *conn,
        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 @@ static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn,
        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;
index 09f013ca81b60d21d2f4cb13df27a6777b158c65..f51c498bcb3f1ee6ccd4bc23c82d1ca1992ea591 100644 (file)
@@ -202,11 +202,6 @@ void passdb_init(struct auth_passdb *passdb)
 
        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)
index f18d116d988ba782b1f669dce1cf1cd3970976ff..58b4268ce501eca3f95a1f0a9e93b371b4b6853b 100644 (file)
@@ -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 @@ static void user_callback(struct auth_request *request, const char *reply)
        }
 
         auth_request_userdb_callback(result, request);
+       auth_request_unref(&request);
+       return TRUE;
 }
 
 void userdb_blocking_lookup(struct auth_request *request)
@@ -37,5 +47,69 @@ void userdb_blocking_lookup(struct auth_request *request)
        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;
 }
index d19fd8040793b27a0dfad9b9b91a4cfcd820b5de..3d45d36de27ff41c17aef069d7a0388c53fff5a2 100644 (file)
@@ -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
index 926c795d82d607361af6d091c9174dec90d8e7be..15fff43b0daac496c43e7fe05d66bd16c99d16a1 100644 (file)
@@ -261,6 +261,10 @@ struct userdb_module_interface userdb_checkpassword = {
        checkpassword_deinit,
 
        checkpassword_lookup,
+
+       NULL,
+       NULL,
+       NULL
 };
 #else
 struct userdb_module_interface userdb_checkpassword = {
index 55501a6c493193258e9bad761b333a9764967c47..20d9ebf154769e807418da42f19945e848e9dba0 100644 (file)
@@ -22,7 +22,22 @@ struct ldap_userdb_module {
 
 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 @@ ldap_query_get_result(struct ldap_connection *conn, LDAPMessage *entry,
        }
 }
 
+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 @@ static void userdb_ldap_lookup_callback(struct ldap_connection *conn,
                (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 @@ static void userdb_ldap_lookup(struct auth_request *auth_request,
        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 @@ userdb_ldap_preinit(struct auth_userdb *auth_userdb, const char *args)
        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 @@ struct userdb_module_interface userdb_ldap = {
        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 = {
index 00e1dc240f406f80c1da203478b742c08007f16d..6e06f45c15f0dcccd954ca38d4c45c94f0079278 100644 (file)
@@ -147,7 +147,11 @@ struct userdb_module_interface userdb_nss = {
        NULL,
        userdb_nss_deinit,
 
-       userdb_nss_lookup
+       userdb_nss_lookup,
+
+       NULL,
+       NULL,
+       NULL
 };
 #else
 struct userdb_module_interface userdb_nss = {
index 60c9cc2f33a8123bf30180ccc0969a9f45bd6b15..db60ccc578167151a0ac6f2e76650015b0f69305 100644 (file)
@@ -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 @@ static void passwd_file_lookup(struct auth_request *auth_request,
        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 @@ struct userdb_module_interface userdb_passwd_file = {
        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 = {
index 3eb69bcf7ee8f1fa9236d4809fba1d8ec4d4b3f3..c2251374c2b57f80bcd9b59009d184d1100124f7 100644 (file)
@@ -5,6 +5,7 @@
 
 #ifdef USERDB_PASSWD
 
+#include "ioloop.h"
 #include "userdb-static.h"
 
 #include <pwd.h>
@@ -16,6 +17,14 @@ struct passwd_userdb_module {
        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 @@ static void passwd_lookup(struct auth_request *auth_request,
        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 @@ struct userdb_module_interface userdb_passwd = {
        NULL,
        NULL,
 
-       passwd_lookup
+       passwd_lookup,
+
+       passwd_iterate_init,
+       passwd_iterate_next,
+       passwd_iterate_deinit
 };
 #else
 struct userdb_module_interface userdb_passwd = {
index 3537014f72d4295090151f28bcb4245df0fdf3de..6d9725e3c379efa8a3f53886f0a9f38f695a2e3e 100644 (file)
@@ -47,7 +47,11 @@ struct userdb_module_interface userdb_prefetch = {
        NULL,
        NULL,
 
-       prefetch_lookup
+       prefetch_lookup,
+
+       NULL,
+       NULL,
+       NULL
 };
 #else
 struct userdb_module_interface userdb_prefetch = {
index ac76bd84c8553eec2bc5bbb76914b247dc010bd6..7d87f0a4b9451cb8916f4c9dd95b8987fa64248d 100644 (file)
@@ -25,6 +25,16 @@ struct userdb_sql_request {
        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 @@ static void userdb_sql_lookup(struct auth_request *auth_request,
                  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 @@ struct userdb_module_interface userdb_sql = {
        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 = {
index dace7119d863979aca1ab629d57aff7760029190..7baa104e498a0fba2467bcc0d6e876e3595e5da9 100644 (file)
@@ -236,7 +236,11 @@ struct userdb_module_interface userdb_static = {
        NULL,
        NULL,
 
-       static_lookup
+       static_lookup,
+
+       NULL,
+       NULL,
+       NULL
 };
 #else
 struct userdb_module_interface userdb_static = {
index efd1b0d61592a45289066ab6a58e29d2a1adca1b..1d79bd1d3d00426778a85bc64bd29b81b102b91f 100644 (file)
@@ -170,7 +170,11 @@ struct userdb_module_interface userdb_vpopmail = {
        NULL,
        NULL,
 
-       vpopmail_lookup
+       vpopmail_lookup,
+
+       NULL,
+       NULL,
+       NULL
 };
 #else
 struct userdb_module_interface userdb_vpopmail = {
index ec55a833b4ea1ae3113770dbf60a7f9328a428d0..82bb4d4da21e452183e53dbbf28adf6b216f4de0 100644 (file)
@@ -148,11 +148,6 @@ void userdb_init(struct auth_userdb *userdb)
 {
        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)
index 623c18a1928bafb031aee7de3303f85fc14a129e..91fa6f61354a7e5924fb4987aa8ef8286675d8d8 100644 (file)
@@ -14,6 +14,8 @@ enum userdb_result {
 
 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 @@ struct userdb_module {
        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 @@ struct userdb_module_interface {
 
        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);