# 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
# 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
#include "array.h"
#include "hash.h"
#include "str.h"
+#include "strescape.h"
#include "hostpid.h"
#include "str-sanitize.h"
#include "ioloop.h"
#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"
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,
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)
{
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");
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)
{
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)
{
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;
}
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 {
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);
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)
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
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)
/* 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;
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
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 }
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"
};
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 */
}
}
-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);
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 =
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");
}
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
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))
;
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,
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;
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;
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 {
/* 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 */
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);
struct passwd_file {
struct db_passwd_file *db;
pool_t pool;
+ int refcount;
char *path;
time_t stamp;
DEF_STR(password_query),
DEF_STR(user_query),
DEF_STR(update_query),
+ DEF_STR(iterate_query),
DEF_STR(default_pass_scheme),
{ 0, NULL, 0 }
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"
};
const char *password_query;
const char *user_query;
const char *update_query;
+ const char *iterate_query;
const char *default_pass_scheme;
};
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();
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)
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;
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)
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,
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);
}
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,
}
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",
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
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)
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;
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)
#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)
}
auth_request_userdb_callback(result, request);
+ auth_request_unref(&request);
+ return TRUE;
}
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;
}
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
checkpassword_deinit,
checkpassword_lookup,
+
+ NULL,
+ NULL,
+ NULL
};
#else
struct userdb_module_interface userdb_checkpassword = {
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
}
}
+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)
(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,
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)
{
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,
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 = {
NULL,
userdb_nss_deinit,
- userdb_nss_lookup
+ userdb_nss_lookup,
+
+ NULL,
+ NULL,
+ NULL
};
#else
struct userdb_module_interface userdb_nss = {
#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;
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)
{
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 = {
#ifdef USERDB_PASSWD
+#include "ioloop.h"
#include "userdb-static.h"
#include <pwd.h>
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)
{
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)
{
NULL,
NULL,
- passwd_lookup
+ passwd_lookup,
+
+ passwd_iterate_init,
+ passwd_iterate_next,
+ passwd_iterate_deinit
};
#else
struct userdb_module_interface userdb_passwd = {
NULL,
NULL,
- prefetch_lookup
+ prefetch_lookup,
+
+ NULL,
+ NULL,
+ NULL
};
#else
struct userdb_module_interface userdb_prefetch = {
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)
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)
{
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 = {
NULL,
NULL,
- static_lookup
+ static_lookup,
+
+ NULL,
+ NULL,
+ NULL
};
#else
struct userdb_module_interface userdb_static = {
NULL,
NULL,
- vpopmail_lookup
+ vpopmail_lookup,
+
+ NULL,
+ NULL,
+ NULL
};
#else
struct userdb_module_interface userdb_vpopmail = {
{
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)
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. */
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;
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);