Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3166:e6a487d80288 HEAD
Restructuring of auth code. Balancer auth processes were a bad idea. Usually
the balancer itself took as much CPU as the actual workers because it acted
as a proxy.
Now auth worker means different thing: they're used to execute blocking
passdb and userdb queries. Currently just MySQL (PAM and checkpassword in
TODO).
line wrap: on
line diff
--- a/dovecot-example.conf Tue Mar 01 00:15:25 2005 +0200 +++ b/dovecot-example.conf Tue Mar 01 00:19:21 2005 +0200 @@ -535,6 +535,11 @@ # queries. #auth_debug = no +# Maximum number of dovecot-auth worker processes. They're used to execute +# blocking passdb and userdb queries (eg. MySQL and PAM). They're +# automatically created and destroyed as needed. +#auth_worker_max_count = 30 + auth default { # Space separated list of wanted authentication mechanisms: # plain digest-md5 cram-md5 apop anonymous
--- a/src/auth/Makefile.am Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/Makefile.am Tue Mar 01 00:19:21 2005 +0200 @@ -38,11 +38,9 @@ auth-master-connection.c \ auth-module.c \ auth-request.c \ - auth-request-balancer-child.c \ - auth-request-balancer-worker.c \ auth-request-handler.c \ - auth-request-handler-balancer.c \ - auth-request-handler-default.c \ + auth-worker-client.c \ + auth-worker-server.c \ db-ldap.c \ db-sql.c \ db-passwd-file.c \ @@ -57,6 +55,7 @@ mech-rpa.c \ mech-apop.c \ passdb.c \ + passdb-blocking.c \ passdb-bsdauth.c \ passdb-cache.c \ passdb-ldap.c \ @@ -68,6 +67,7 @@ passdb-vpopmail.c \ passdb-sql.c \ userdb.c \ + userdb-blocking.c \ userdb-ldap.c \ userdb-passdb.c \ userdb-passwd.c \ @@ -85,8 +85,9 @@ auth-master-connection.h \ auth-module.h \ auth-request.h \ - auth-request-balancer.h \ auth-request-handler.h \ + auth-worker-client.h \ + auth-worker-server.h \ db-ldap.h \ db-sql.h \ db-passwd-file.h \ @@ -94,9 +95,11 @@ mech.h \ mycrypt.h \ passdb.h \ + passdb-blocking.h \ passdb-cache.h \ password-scheme.h \ userdb.h \ + userdb-blocking.h \ userdb-vpopmail.h checkpassword_reply_LDADD = \
--- a/src/auth/auth-master-connection.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-master-connection.c Tue Mar 01 00:19:21 2005 +0200 @@ -10,7 +10,6 @@ #include "network.h" #include "userdb.h" #include "auth-request-handler.h" -#include "auth-request-balancer.h" #include "auth-master-interface.h" #include "auth-client-connection.h" #include "auth-master-connection.h" @@ -292,10 +291,6 @@ auth_master_connection_set_fd(l->master, fd); auth_master_connection_send_handshake(l->master); break; - case LISTENER_BALANCER: - /* worker process connected to us */ - auth_request_balancer_add_child(fd); - break; } } }
--- a/src/auth/auth-master-connection.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-master-connection.h Tue Mar 01 00:19:21 2005 +0200 @@ -3,8 +3,7 @@ enum listener_type { LISTENER_MASTER, - LISTENER_CLIENT, - LISTENER_BALANCER + LISTENER_CLIENT }; struct auth_master_connection {
--- a/src/auth/auth-request-balancer-child.c Tue Mar 01 00:15:25 2005 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,227 +0,0 @@ -/* Copyright (C) 2002-2005 Timo Sirainen */ - -#include "common.h" -#include "ioloop.h" -#include "network.h" -#include "buffer.h" -#include "hash.h" -#include "istream.h" -#include "ostream.h" -#include "auth-request-balancer.h" - -#include <stdlib.h> -#include <unistd.h> - -struct auth_balancer_child { - unsigned int id; - int fd; - - struct io *io; - struct istream *input; - struct ostream *output; -}; - -static buffer_t *balancer_children; -static struct hash_table *balancer_handlers; -static unsigned int balancer_next_idx = 0; - -static void -auth_request_balancer_remove_child(struct auth_balancer_child *child); - -static void balancer_input(void *context) -{ - struct auth_balancer_child *child = context; - struct auth_request_handler *handler; - const char *line, *id_str; - unsigned int id; - - switch (i_stream_read(child->input)) { - case 0: - return; - case -1: - /* disconnected */ - auth_request_balancer_remove_child(child); - return; - case -2: - /* buffer full */ - i_error("BUG: Auth balancer child sent us more than %d bytes", - (int)AUTH_BALANCER_MAX_LINE_LENGTH); - auth_request_balancer_remove_child(child); - return; - } - - while ((line = i_stream_next_line(child->input)) != NULL) { - id_str = line; - line = strchr(line, '\t'); - if (line == NULL) - continue; - - t_push(); - id = (unsigned int)strtoul(t_strcut(id_str, '\t'), NULL, 10); - handler = hash_lookup(balancer_handlers, POINTER_CAST(id)); - t_pop(); - - if (handler != NULL) { - auth_request_handler_balancer_reply(handler, - line + 1); - } - } -} - -static int balancer_output(void *context) -{ - struct auth_balancer_child *child = context; - - if (o_stream_flush(child->output) < 0) { - auth_request_balancer_remove_child(child); - return 1; - } - - /* FIXME: throttle control.. */ - return 1; -} - -void auth_request_balancer_add_child(int fd) -{ - static unsigned int balancer_id_counter = 0; - struct auth_balancer_child *child; - - net_set_nonblock(fd, TRUE); - - child = i_new(struct auth_balancer_child, 1); - child->id = ++balancer_id_counter; - child->fd = fd; - child->input = - i_stream_create_file(fd, default_pool, - AUTH_BALANCER_MAX_LINE_LENGTH, FALSE); - child->output = - o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); - o_stream_set_flush_callback(child->output, balancer_output, child); - child->io = io_add(fd, IO_READ, balancer_input, child); - - buffer_append(balancer_children, &child, sizeof(child)); -} - -static void -auth_request_balancer_remove_child(struct auth_balancer_child *child) -{ - struct auth_balancer_child **children; - size_t i, size; - - children = buffer_get_modifyable_data(balancer_children, &size); - size /= sizeof(*children); - - for (i = 0; i < size; i++) { - if (children[i] == child) { - buffer_delete(balancer_children, - i * sizeof(child), sizeof(child)); - break; - } - } - i_assert(i != size); - - if (child->io != NULL) - io_remove(child->io); - - i_stream_unref(child->input); - o_stream_unref(child->output); - - if (close(child->fd) < 0) - i_error("close(balancer) failed: %m"); - i_free(child); -} - -static void balancer_send(struct auth_balancer_child *child, const char *line) -{ - struct const_iovec iov[2]; - - iov[0].iov_base = line; - iov[0].iov_len = strlen(line); - iov[1].iov_base = "\n"; - iov[1].iov_len = 1; - - (void)o_stream_sendv(child->output, iov, 2); - /* FIXME: throttle control */ -} - -unsigned int auth_request_balancer_send(const char *line) -{ - struct auth_balancer_child **child, *min_child; - size_t size, used_size, min_size; - unsigned int i, start; - - child = buffer_get_modifyable_data(balancer_children, &size); - size /= sizeof(*child); - - i_assert(size > 0); - - start = i = balancer_next_idx % size; - balancer_next_idx++; - - min_size = (size_t)-1; - min_child = NULL; - do { - used_size = o_stream_get_buffer_used_size(child[i]->output); - if (used_size == 0) { - /* nothing in output buffer, use this */ - balancer_send(child[i], line); - return child[i]->id; - } - if (used_size < min_size) { - min_size = used_size; - min_child = child[i]; - } - } while (++i != start); - - /* min_child has the smallest amount of data in output buffer */ - balancer_send(min_child, line); - return min_child->id; -} - -void auth_request_balancer_send_to(unsigned int id, const char *line) -{ - struct auth_balancer_child **child; - size_t i, size; - - child = buffer_get_modifyable_data(balancer_children, &size); - size /= sizeof(*child); - - for (i = 0; i < size; i++) { - if (child[i]->id == id) { - balancer_send(child[i], line); - return; - } - } - - // FIXME: ? -} - -void auth_request_balancer_add_handler(struct auth_request_handler *handler, - unsigned int connect_uid) -{ - hash_insert(balancer_handlers, POINTER_CAST(connect_uid), handler); -} - -void auth_request_balancer_remove_handler(unsigned int connect_uid) -{ - hash_remove(balancer_handlers, POINTER_CAST(connect_uid)); -} - -void auth_request_balancer_child_init(void) -{ - balancer_children = buffer_create_dynamic(default_pool, 32); - balancer_handlers = - hash_create(default_pool, default_pool, 0, NULL, NULL); -} - -void auth_request_balancer_child_deinit(void) -{ - while (balancer_children->used > 0) { - struct auth_balancer_child **child; - - child = buffer_get_modifyable_data(balancer_children, NULL); - auth_request_balancer_remove_child(*child); - } - buffer_free(balancer_children); - hash_destroy(balancer_handlers); -}
--- a/src/auth/auth-request-balancer-worker.c Tue Mar 01 00:15:25 2005 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +0,0 @@ -/* Copyright (C) 2002-2005 Timo Sirainen */ - -#include "common.h" -#include "ioloop.h" -#include "network.h" -#include "istream.h" -#include "ostream.h" -#include "safe-memset.h" -#include "auth-request-handler.h" -#include "auth-request-balancer.h" - -#include <stdlib.h> -#include <unistd.h> - -struct auth_balancer_worker { - int fd; - - struct auth_request_handler *request_handler; - - struct io *io; - struct istream *input; - struct ostream *output; -}; - -static unsigned int next_uint(const char **line) -{ - const char *p, *value = *line; - - p = strchr(*line, '\t'); - if (p == NULL) - *line += strlen(value); - else { - value = t_strdup_until(value, p); - *line = p + 1; - } - return (unsigned int)strtoul(value, NULL, 10); -} - -static char *balancer_socket_path; -static struct timeout *to_connect; - -static void -auth_client_handle_line(struct auth_balancer_worker *worker, const char *line) -{ - struct auth_request_handler *rh = worker->request_handler; - unsigned int connect_uid, client_pid, id; - - connect_uid = next_uint(&line); - client_pid = next_uint(&line); - - auth_request_handler_set(rh, connect_uid, client_pid); - - if (strncmp(line, "AUTH\t", 5) == 0) - (void)auth_request_handler_auth_begin(rh, line + 5); - else if (strncmp(line, "CONT\t", 5) == 0) - (void)auth_request_handler_auth_continue(rh, line + 5); - else if (strncmp(line, "REQUEST\t", 8) == 0) { - id = (unsigned int)strtoul(line + 8, NULL, 10); - (void)auth_request_handler_master_request(rh, id, id); - } -} - -static void balancer_worker_input(void *context) -{ - struct auth_balancer_worker *worker = context; - char *line; - - switch (i_stream_read(worker->input)) { - case 0: - return; - case -1: - /* disconnected */ - auth_request_balancer_worker_destroy(worker); - return; - case -2: - /* buffer full */ - i_error("BUG: Auth balancer server sent us more than %d bytes", - (int)AUTH_BALANCER_MAX_LINE_LENGTH); - auth_request_balancer_worker_destroy(worker); - return; - } - - while ((line = i_stream_next_line(worker->input)) != NULL) { - t_push(); - - auth_client_handle_line(worker, line); - safe_memset(line, 0, strlen(line)); - - t_pop(); - } -} - -static int balancer_worker_output(void *context) -{ - struct auth_balancer_worker *worker = context; - - if (o_stream_flush(worker->output) < 0) { - auth_request_balancer_worker_destroy(worker); - return 1; - } - - /* FIXME: throttle control.. */ - return 1; -} - -static void auth_callback(const char *reply, void *context) -{ - struct auth_balancer_worker *worker = context; - struct const_iovec iov[2]; - - if (reply == NULL) { - /* request handler was destroyed */ - return; - } - - iov[0].iov_base = reply; - iov[0].iov_len = strlen(reply); - iov[1].iov_base = "\n"; - iov[1].iov_len = 1; - - (void)o_stream_sendv(worker->output, iov, 2); -} - -void auth_request_balancer_add_worker(struct auth *auth, int fd) -{ - struct auth_balancer_worker *worker; - - net_set_nonblock(fd, TRUE); - - worker = i_new(struct auth_balancer_worker, 1); - worker->fd = fd; - worker->input = - i_stream_create_file(fd, default_pool, - AUTH_BALANCER_MAX_LINE_LENGTH, FALSE); - worker->output = - o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); - o_stream_set_flush_callback(worker->output, balancer_worker_output, - worker); - worker->io = io_add(fd, IO_READ, balancer_worker_input, worker); - - worker->request_handler = - auth_request_handler_create(auth, TRUE, auth_callback, worker, - auth_callback, worker); - - i_assert(auth->balancer_worker == NULL); - auth->balancer_worker = worker; -} - -void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker) -{ - io_loop_stop(ioloop); - auth_request_handler_unref(worker->request_handler); - - if (worker->io != NULL) - io_remove(worker->io); - - i_stream_unref(worker->input); - o_stream_unref(worker->output); - - if (close(worker->fd) < 0) - i_error("close(balancer) failed: %m"); - i_free(worker); -} - -static int auth_request_balancer_connect(struct auth *auth) -{ - int fd; - - fd = net_connect_unix(balancer_socket_path); - if (fd < 0) { - if (errno != EAGAIN) { - i_fatal("net_connect_unix(%s) failed: %m", - balancer_socket_path); - } - /* busy */ - return FALSE; - } - - auth_request_balancer_add_worker(auth, fd); - return TRUE; -} - -static void balancer_connect_timeout(void *context) -{ - struct auth *auth = context; - - if (auth_request_balancer_connect(auth)) { - timeout_remove(to_connect); - to_connect = NULL; - } -} - -void auth_request_balancer_worker_init(struct auth *auth) -{ - const char *name; - - name = getenv("AUTH_NAME"); - if (name == NULL) name = "auth"; - balancer_socket_path = i_strconcat(name, "-balancer", NULL); - - if (!auth_request_balancer_connect(auth)) { - /* couldn't connect to balancer yet, it's probably still - starting. try again later. */ - to_connect = timeout_add(1000, balancer_connect_timeout, auth); - } -} - -void auth_request_balancer_worker_deinit(void) -{ - if (to_connect != NULL) - timeout_remove(to_connect); - i_free(balancer_socket_path); -}
--- a/src/auth/auth-request-balancer.h Tue Mar 01 00:15:25 2005 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -#ifndef __AUTH_REQUEST_BALANCER_H -#define __AUTH_REQUEST_BALANCER_H - -#include "auth-client-interface.h" - -#define AUTH_BALANCER_MAX_LINE_LENGTH \ - (AUTH_CLIENT_MAX_LINE_LENGTH + 64) - -struct auth_request_handler; - -void auth_request_balancer_add_child(int fd); -void auth_request_balancer_add_worker(struct auth *auth, int fd); -void auth_request_balancer_worker_destroy(struct auth_balancer_worker *worker); - -void auth_request_balancer_add_handler(struct auth_request_handler *handler, - unsigned int connect_uid); -void auth_request_balancer_remove_handler(unsigned int connect_uid); - -unsigned int auth_request_balancer_send(const char *line); -void auth_request_balancer_send_to(unsigned int id, const char *line); - -void auth_request_handler_balancer_reply(struct auth_request_handler *handler, - const char *line); - -void auth_request_balancer_child_init(void); -void auth_request_balancer_child_deinit(void); - -void auth_request_balancer_worker_init(struct auth *auth); -void auth_request_balancer_worker_deinit(void); - -#endif
--- a/src/auth/auth-request-handler-balancer.c Tue Mar 01 00:15:25 2005 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -/* Copyright (C) 2005 Timo Sirainen */ - -#include "common.h" -#include "ioloop.h" -#include "hash.h" -#include "str.h" -#include "str-sanitize.h" -#include "mech.h" -#include "auth-client-interface.h" -#include "auth-request-handler.h" -#include "auth-request-balancer.h" - -#include <stdlib.h> - -struct auth_balancer_request { - unsigned int client_id; - unsigned int master_id; - unsigned int balancer_pid; - time_t created; -}; - -struct auth_request_handler { - int refcount; - pool_t pool; - struct hash_table *client_requests; - - struct auth *auth; - unsigned int connect_uid, client_pid; - - auth_request_callback_t *callback; - void *context; - - auth_request_callback_t *master_callback; - void *master_context; -}; - -static struct auth_request_handler * -_create(struct auth *auth, int prepend_connect_uid, - auth_request_callback_t *callback, void *context, - auth_request_callback_t *master_callback, void *master_context) -{ - struct auth_request_handler *handler; - pool_t pool; - - i_assert(!prepend_connect_uid); - - pool = pool_alloconly_create("auth request handler", 4096); - - handler = p_new(pool, struct auth_request_handler, 1); - handler->refcount = 1; - handler->pool = pool; - handler->client_requests = - hash_create(default_pool, pool, 0, NULL, NULL); - handler->auth = auth; - handler->callback = callback; - handler->context = context; - handler->master_callback = master_callback; - handler->master_context = master_context; - - return handler; -} - -static void _set(struct auth_request_handler *handler, - unsigned int connect_uid, unsigned int client_pid) -{ - i_assert(handler->connect_uid == 0); - - handler->connect_uid = connect_uid; - handler->client_pid = client_pid; - - auth_request_balancer_add_handler(handler, connect_uid); -} - -static void _unref(struct auth_request_handler *handler) -{ - struct hash_iterate_context *iter; - void *key, *value; - - i_assert(handler->refcount > 0); - if (--handler->refcount > 0) - return; - - auth_request_balancer_remove_handler(handler->connect_uid); - - iter = hash_iterate_init(handler->client_requests); - while (hash_iterate(iter, &key, &value)) - i_free(value); - hash_iterate_deinit(iter); - - /* notify parent that we're done with all requests */ - handler->callback(NULL, handler->context); - - hash_destroy(handler->client_requests); - pool_unref(handler->pool); -} - -static void auth_request_handler_remove(struct auth_request_handler *handler, - struct auth_balancer_request *request) -{ - hash_remove(handler->client_requests, POINTER_CAST(request->client_id)); - i_free(request); -} - -static void _check_timeouts(struct auth_request_handler *handler) -{ - struct hash_iterate_context *iter; - void *key, *value; - - iter = hash_iterate_init(handler->client_requests); - while (hash_iterate(iter, &key, &value)) { - struct auth_balancer_request *request = value; - - if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) - auth_request_handler_remove(handler, request); - } - hash_iterate_deinit(iter); -} - -static int _auth_begin(struct auth_request_handler *handler, const char *args) -{ - struct auth_balancer_request *request; - struct mech_module *mech; - const char *mech_name; - string_t *str; - unsigned int id; - - /* <id> <mechanism> [...] */ - id = (unsigned int)strtoul(t_strcut(args, '\t'), NULL, 10); - mech_name = strchr(args, '\t'); - if (mech_name == NULL) { - i_error("BUG: Authentication client %u " - "sent broken AUTH request", handler->client_pid); - return FALSE; - } - args = strchr(++mech_name, '\t'); - mech_name = t_strcut(mech_name, '\t'); - - mech = mech_module_find(mech_name); - if (mech == NULL) { - /* unsupported mechanism */ - i_error("BUG: Authentication client %u requested unsupported " - "authentication mechanism %s", handler->client_pid, - str_sanitize(mech_name, MAX_MECH_NAME_LEN)); - return FALSE; - } - - request = i_new(struct auth_balancer_request, 1); - request->created = ioloop_time; - request->client_id = id; - - str = t_str_new(256); - str_printfa(str, "%u\t%u\tAUTH\t%u\t%s%s", - handler->connect_uid, handler->client_pid, - id, mech->mech_name, args == NULL ? "" : args); - request->balancer_pid = auth_request_balancer_send(str_c(str)); - - hash_insert(handler->client_requests, POINTER_CAST(id), request); - return TRUE; -} - -static int -_auth_continue(struct auth_request_handler *handler, const char *args) -{ - struct auth_balancer_request *request; - const char *data; - string_t *str; - unsigned int id; - - data = strchr(args, '\t'); - if (data++ == NULL) { - i_error("BUG: Authentication client sent broken CONT request"); - return FALSE; - } - - id = (unsigned int)strtoul(args, NULL, 10); - - request = hash_lookup(handler->client_requests, POINTER_CAST(id)); - if (request == NULL) { - data = t_strdup_printf("FAIL\t%u\treason=Timeouted", id); - handler->callback(data, handler->context); - return TRUE; - } - - str = t_str_new(128); - str_printfa(str, "%u\t%u\tCONT\t%u\t%s", - handler->connect_uid, handler->client_pid, id, data); - auth_request_balancer_send_to(request->balancer_pid, str_c(str)); - return TRUE; -} - -static void _master_request(struct auth_request_handler *handler, - unsigned int id, unsigned int client_id) -{ - struct auth_balancer_request *request; - const char *reply; - string_t *str; - - request = hash_lookup(handler->client_requests, - POINTER_CAST(client_id)); - if (request == NULL || request->balancer_pid == 0) { - i_error("Master request %u.%u not found from balancer", - handler->client_pid, client_id); - reply = t_strdup_printf("NOTFOUND\t%u", id); - handler->master_callback(reply, handler->master_context); - return; - } - - request->master_id = id; - - str = t_str_new(128); - str_printfa(str, "%u\t%u\tREQUEST\t%u", - handler->connect_uid, handler->client_pid, client_id); - auth_request_balancer_send_to(request->balancer_pid, str_c(str)); -} - -static void _flush_failures(void) -{ -} - -static void _init(void) -{ - auth_request_balancer_child_init(); -} - -static void _deinit(void) -{ - auth_request_balancer_child_deinit(); -} - -struct auth_request_handler_api auth_request_handler_balancer = { - _create, - _unref, - _set, - _check_timeouts, - _auth_begin, - _auth_continue, - _master_request, - _flush_failures, - _init, - _deinit -}; - -void auth_request_handler_balancer_reply(struct auth_request_handler *handler, - const char *line) -{ - struct auth_balancer_request *request; - const char *cmd, *id_str, *args; - unsigned int id; - - /* <cmd> <id> [...] */ - args = strchr(line, '\t'); - if (args == NULL) { - i_error("Balancer worker sent invalid reply: %s", line); - return; - } - - cmd = t_strdup_until(line, args); - id_str = args + 1; - args = strchr(id_str, '\t'); - if (args == NULL) - args = ""; - else - id_str = t_strdup_until(id_str, args); - - id = (unsigned int)strtoul(id_str, NULL, 10); - request = hash_lookup(handler->client_requests, POINTER_CAST(id)); - if (request == NULL) { - i_error("Balancer worker sent unknown request %u", id); - return; - } - - if (request->master_id == 0) { - handler->callback(line, handler->context); - if (strcmp(cmd, "CONT") != 0 && - (strcmp(cmd, "OK") != 0 || - strstr(line, "\tnologin") != NULL || - handler->master_callback == NULL)) { - /* this request doesn't have to wait for master - process to pick it up. delete it */ - auth_request_handler_remove(handler, request); - } - } else { - /* replace client id with master id */ - line = t_strdup_printf("%s\t%u%s", cmd, - request->master_id, args); - handler->master_callback(line, handler->master_context); - auth_request_handler_remove(handler, request); - } -}
--- a/src/auth/auth-request-handler-default.c Tue Mar 01 00:15:25 2005 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,484 +0,0 @@ -/* Copyright (C) 2005 Timo Sirainen */ - -#include "common.h" -#include "ioloop.h" -#include "buffer.h" -#include "base64.h" -#include "hash.h" -#include "str.h" -#include "str-sanitize.h" -#include "auth-request.h" -#include "auth-request-handler.h" - -#include <stdlib.h> - -struct auth_request_handler { - int refcount; - pool_t pool; - struct hash_table *requests; - - struct auth *auth; - unsigned int connect_uid, client_pid; - - auth_request_callback_t *callback; - void *context; - - auth_request_callback_t *master_callback; - void *master_context; - - unsigned int prepend_connect_uid:1; -}; - -static buffer_t *auth_failures_buf; -static struct timeout *to_auth_failures; - -static struct auth_request_handler * -_create(struct auth *auth, int prepend_connect_uid, - auth_request_callback_t *callback, void *context, - auth_request_callback_t *master_callback, void *master_context) -{ - struct auth_request_handler *handler; - pool_t pool; - - pool = pool_alloconly_create("auth request handler", 4096); - - handler = p_new(pool, struct auth_request_handler, 1); - handler->refcount = 1; - handler->pool = pool; - handler->requests = hash_create(default_pool, pool, 0, NULL, NULL); - handler->auth = auth; - handler->callback = callback; - handler->context = context; - handler->master_callback = master_callback; - handler->master_context = master_context; - handler->prepend_connect_uid = prepend_connect_uid; - return handler; -} - -static void _set(struct auth_request_handler *handler, - unsigned int connect_uid, unsigned int client_pid) -{ - handler->connect_uid = connect_uid; - handler->client_pid = client_pid; -} - -static void _unref(struct auth_request_handler *handler) -{ - struct hash_iterate_context *iter; - void *key, *value; - - i_assert(handler->refcount > 0); - if (--handler->refcount > 0) - return; - - iter = hash_iterate_init(handler->requests); - while (hash_iterate(iter, &key, &value)) - auth_request_unref(value); - hash_iterate_deinit(iter); - - /* notify parent that we're done with all requests */ - handler->callback(NULL, handler->context); - - hash_destroy(handler->requests); - pool_unref(handler->pool); -} - -static void auth_request_handler_remove(struct auth_request_handler *handler, - struct auth_request *request) -{ - hash_remove(handler->requests, POINTER_CAST(request->id)); - auth_request_unref(request); -} - -static void _check_timeouts(struct auth_request_handler *handler) -{ - struct hash_iterate_context *iter; - void *key, *value; - - iter = hash_iterate_init(handler->requests); - while (hash_iterate(iter, &key, &value)) { - struct auth_request *request = value; - - if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) - auth_request_handler_remove(handler, request); - } - hash_iterate_deinit(iter); -} - -static const char *get_client_extra_fields(struct auth_request *request) -{ - const char **fields; - unsigned int src, dest; - - if (request->extra_fields == NULL) - return NULL; - - /* we only wish to remove all fields prefixed with "userdb_" */ - if (strstr(str_c(request->extra_fields), "userdb_") == NULL) - return str_c(request->extra_fields); - - fields = t_strsplit(str_c(request->extra_fields), "\t"); - for (src = dest = 0; fields[src] != NULL; src++) { - if (strncmp(fields[src], "userdb_", 7) != 0) - fields[dest++] = fields[src]; - } - fields[dest] = NULL; - return t_strarray_join(fields, "\t"); -} - -static void auth_callback(struct auth_request *request, - enum auth_client_result result, - const void *reply, size_t reply_size) -{ - struct auth_request_handler *handler = request->context; - string_t *str; - const char *fields; - - t_push(); - - str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size)); - if (handler->prepend_connect_uid) - str_printfa(str, "%u\t", request->connect_uid); - - switch (result) { - case AUTH_CLIENT_RESULT_CONTINUE: - str_printfa(str, "CONT\t%u\t", request->id); - base64_encode(reply, reply_size, str); - request->accept_input = TRUE; - handler->callback(str_c(str), handler->context); - break; - case AUTH_CLIENT_RESULT_SUCCESS: - str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user); - if (reply_size > 0) { - str_append(str, "\tresp="); - base64_encode(reply, reply_size, str); - } - fields = get_client_extra_fields(request); - if (fields != NULL) { - str_append_c(str, '\t'); - str_append(str, fields); - } - - if (request->no_login || handler->master_callback == NULL) { - /* this request doesn't have to wait for master - process to pick it up. delete it */ - auth_request_handler_remove(handler, request); - } - handler->callback(str_c(str), handler->context); - break; - case AUTH_CLIENT_RESULT_FAILURE: - str_printfa(str, "FAIL\t%u", request->id); - if (request->user != NULL) - str_printfa(str, "\tuser=%s", request->user); - if (request->internal_failure) - str_append(str, "\ttemp"); - fields = get_client_extra_fields(request); - if (fields != NULL) { - str_append_c(str, '\t'); - str_append(str, fields); - } - - if (request->delayed_failure) { - /* we came here from flush_failures() */ - handler->callback(str_c(str), handler->context); - break; - } - - /* remove the request from requests-list */ - auth_request_ref(request); - auth_request_handler_remove(handler, request); - - if (request->no_failure_delay) { - /* passdb specifically requested not to delay the - reply. */ - handler->callback(str_c(str), handler->context); - auth_request_unref(request); - } else { - /* failure. don't announce it immediately to avoid - a) timing attacks, b) flooding */ - request->delayed_failure = TRUE; - handler->refcount++; - buffer_append(auth_failures_buf, - &request, sizeof(request)); - } - break; - } - /* NOTE: request may be destroyed now */ - - auth_request_handler_unref(handler); - - t_pop(); -} - -static void auth_request_handler_auth_fail(struct auth_request_handler *handler, - struct auth_request *request, - const char *reason) -{ - string_t *reply = t_str_new(64); - - auth_request_log_info(request, request->mech->mech_name, "%s", reason); - - if (handler->prepend_connect_uid) - str_printfa(reply, "%u\t", request->connect_uid); - str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason); - handler->callback(str_c(reply), handler->context); - - auth_request_handler_remove(handler, request); -} - -static int _auth_begin(struct auth_request_handler *handler, const char *args) -{ - struct mech_module *mech; - struct auth_request *request; - const char *const *list, *name, *arg, *initial_resp; - const void *initial_resp_data; - size_t initial_resp_len; - unsigned int id; - buffer_t *buf; - int valid_client_cert; - - /* <id> <mechanism> [...] */ - list = t_strsplit(args, "\t"); - if (list[0] == NULL || list[1] == NULL) { - i_error("BUG: Authentication client %u " - "sent broken AUTH request", handler->client_pid); - return FALSE; - } - - id = (unsigned int)strtoul(list[0], NULL, 10); - - mech = mech_module_find(list[1]); - if (mech == NULL) { - /* unsupported mechanism */ - i_error("BUG: Authentication client %u requested unsupported " - "authentication mechanism %s", handler->client_pid, - str_sanitize(list[1], MAX_MECH_NAME_LEN)); - return FALSE; - } - - request = auth_request_new(handler->auth, mech, auth_callback, handler); - request->connect_uid = handler->connect_uid; - request->client_pid = handler->client_pid; - request->id = id; - - /* parse optional parameters */ - initial_resp = NULL; - valid_client_cert = FALSE; - for (list += 2; *list != NULL; list++) { - arg = strchr(*list, '='); - if (arg == NULL) { - name = *list; - arg = ""; - } else { - name = t_strdup_until(*list, arg); - arg++; - } - - if (strcmp(name, "lip") == 0) - (void)net_addr2ip(arg, &request->local_ip); - else if (strcmp(name, "rip") == 0) - (void)net_addr2ip(arg, &request->remote_ip); - else if (strcmp(name, "service") == 0) - request->service = p_strdup(request->pool, arg); - else if (strcmp(name, "resp") == 0) - initial_resp = arg; - else if (strcmp(name, "valid-client-cert") == 0) - valid_client_cert = TRUE; - } - - if (request->service == NULL) { - i_error("BUG: Authentication client %u " - "didn't specify service in request", - handler->client_pid); - auth_request_unref(request); - return FALSE; - } - - hash_insert(handler->requests, POINTER_CAST(id), request); - - if (request->auth->ssl_require_client_cert && !valid_client_cert) { - /* we fail without valid certificate */ - auth_request_handler_auth_fail(handler, request, - "Client didn't present valid SSL certificate"); - return TRUE; - } - - if (initial_resp == NULL) { - initial_resp_data = NULL; - initial_resp_len = 0; - } else { - size_t len = strlen(initial_resp); - buf = buffer_create_dynamic(pool_datastack_create(), - MAX_BASE64_DECODED_SIZE(len)); - if (base64_decode(initial_resp, len, NULL, buf) < 0) { - auth_request_handler_auth_fail(handler, request, - "Invalid base64 data in initial response"); - return TRUE; - } - initial_resp_data = buf->data; - initial_resp_len = buf->used; - } - - /* handler is referenced until auth_callback is called. */ - handler->refcount++; - auth_request_initial(request, initial_resp_data, initial_resp_len); - return TRUE; -} - -static int -_auth_continue(struct auth_request_handler *handler, const char *args) -{ - struct auth_request *request; - const char *data; - size_t data_len; - buffer_t *buf; - unsigned int id; - - data = strchr(args, '\t'); - if (data++ == NULL) { - i_error("BUG: Authentication client sent broken CONT request"); - return FALSE; - } - - id = (unsigned int)strtoul(args, NULL, 10); - - request = hash_lookup(handler->requests, POINTER_CAST(id)); - if (request == NULL) { - string_t *reply = t_str_new(64); - - if (handler->prepend_connect_uid) - str_printfa(reply, "%u\t", handler->connect_uid); - str_printfa(reply, "FAIL\t%u\treason=Timeouted", id); - handler->callback(str_c(reply), handler->context); - return TRUE; - } - - /* accept input only once after mechanism has sent a CONT reply */ - if (!request->accept_input) { - auth_request_handler_auth_fail(handler, request, - "Unexpected continuation"); - return TRUE; - } - request->accept_input = FALSE; - - data_len = strlen(data); - buf = buffer_create_dynamic(pool_datastack_create(), - MAX_BASE64_DECODED_SIZE(data_len)); - if (base64_decode(data, data_len, NULL, buf) < 0) { - auth_request_handler_auth_fail(handler, request, - "Invalid base64 data in continued response"); - return TRUE; - } - - /* handler is referenced until auth_callback is called. */ - handler->refcount++; - auth_request_continue(request, buf->data, buf->used); - return TRUE; -} - -static void userdb_callback(const char *result, void *context) -{ - struct auth_request *request = context; - struct auth_request_handler *handler = request->context; - string_t *reply; - - reply = t_str_new(256); - if (handler->prepend_connect_uid) - str_printfa(reply, "%u\t", request->connect_uid); - if (result == NULL) - str_printfa(reply, "NOTFOUND\t%u", request->id); - else { - str_printfa(reply, "USER\t%u\t", request->id); - str_append(reply, result); - } - handler->master_callback(str_c(reply), handler->master_context); - - auth_request_unref(request); - auth_request_handler_unref(handler); -} - -static void _master_request(struct auth_request_handler *handler, - unsigned int id, unsigned int client_id) -{ - struct auth_request *request; - string_t *reply; - - reply = t_str_new(64); - if (handler->prepend_connect_uid) - str_printfa(reply, "%u\t", handler->connect_uid); - - request = hash_lookup(handler->requests, POINTER_CAST(client_id)); - if (request == NULL) { - i_error("Master request %u.%u not found", - handler->client_pid, client_id); - str_printfa(reply, "NOTFOUND\t%u", id); - handler->master_callback(str_c(reply), handler->master_context); - return; - } - - auth_request_ref(request); - auth_request_handler_remove(handler, request); - - if (!request->successful) { - i_error("Master requested unfinished authentication request " - "%u.%u", handler->client_pid, client_id); - str_printfa(reply, "NOTFOUND\t%u", id); - handler->master_callback(str_c(reply), handler->master_context); - } else { - /* the request isn't being referenced anywhere anymore, - so we can do a bit of kludging.. replace the request's - old client_id with master's id. */ - request->id = id; - request->context = handler; - - /* handler is referenced until userdb_callback is called. */ - handler->refcount++; - auth_request_lookup_user(request, userdb_callback, request); - } -} - -static void _flush_failures(void) -{ - struct auth_request **auth_request; - size_t i, size; - - auth_request = buffer_get_modifyable_data(auth_failures_buf, &size); - size /= sizeof(*auth_request); - - for (i = 0; i < size; i++) { - auth_request[i]->callback(auth_request[i], - AUTH_CLIENT_RESULT_FAILURE, NULL, 0); - auth_request_unref(auth_request[i]); - } - buffer_set_used_size(auth_failures_buf, 0); -} - -static void auth_failure_timeout(void *context __attr_unused__) -{ - _flush_failures(); -} - -static void _init(void) -{ - auth_failures_buf = buffer_create_dynamic(default_pool, 1024); - to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL); -} - -static void _deinit(void) -{ - buffer_free(auth_failures_buf); - timeout_remove(to_auth_failures); -} - -struct auth_request_handler_api auth_request_handler_default = { - _create, - _unref, - _set, - _check_timeouts, - _auth_begin, - _auth_continue, - _master_request, - _flush_failures, - _init, - _deinit -};
--- a/src/auth/auth-request-handler.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-request-handler.c Tue Mar 01 00:19:21 2005 +0200 @@ -1,10 +1,36 @@ /* Copyright (C) 2005 Timo Sirainen */ #include "common.h" +#include "ioloop.h" +#include "buffer.h" +#include "base64.h" +#include "hash.h" +#include "str.h" +#include "str-sanitize.h" +#include "auth-request.h" #include "auth-request-handler.h" -#include "auth-request-balancer.h" + +#include <stdlib.h> + +struct auth_request_handler { + int refcount; + pool_t pool; + struct hash_table *requests; + + struct auth *auth; + unsigned int connect_uid, client_pid; -struct auth_request_handler_api *auth_request_handler_api; + auth_request_callback_t *callback; + void *context; + + auth_request_callback_t *master_callback; + void *master_context; + + unsigned int prepend_connect_uid:1; +}; + +static buffer_t *auth_failures_buf; +static struct timeout *to_auth_failures; struct auth_request_handler * auth_request_handler_create(struct auth *auth, int prepend_connect_uid, @@ -12,62 +38,437 @@ auth_request_callback_t *master_callback, void *master_context) { - return auth_request_handler_api-> - create(auth, prepend_connect_uid, callback, context, - master_callback, master_context); + struct auth_request_handler *handler; + pool_t pool; + + pool = pool_alloconly_create("auth request handler", 4096); + + handler = p_new(pool, struct auth_request_handler, 1); + handler->refcount = 1; + handler->pool = pool; + handler->requests = hash_create(default_pool, pool, 0, NULL, NULL); + handler->auth = auth; + handler->callback = callback; + handler->context = context; + handler->master_callback = master_callback; + handler->master_context = master_context; + handler->prepend_connect_uid = prepend_connect_uid; + return handler; +} + +void auth_request_handler_unref(struct auth_request_handler *handler) +{ + struct hash_iterate_context *iter; + void *key, *value; + + i_assert(handler->refcount > 0); + if (--handler->refcount > 0) + return; + + iter = hash_iterate_init(handler->requests); + while (hash_iterate(iter, &key, &value)) + auth_request_unref(value); + hash_iterate_deinit(iter); + + /* notify parent that we're done with all requests */ + handler->callback(NULL, handler->context); + + hash_destroy(handler->requests); + pool_unref(handler->pool); } void auth_request_handler_set(struct auth_request_handler *handler, unsigned int connect_uid, unsigned int client_pid) { - auth_request_handler_api->set(handler, connect_uid, client_pid); + handler->connect_uid = connect_uid; + handler->client_pid = client_pid; } -void auth_request_handler_unref(struct auth_request_handler *handler) +static void auth_request_handler_remove(struct auth_request_handler *handler, + struct auth_request *request) { - auth_request_handler_api->unref(handler); + hash_remove(handler->requests, POINTER_CAST(request->id)); + auth_request_unref(request); } void auth_request_handler_check_timeouts(struct auth_request_handler *handler) { - auth_request_handler_api->check_timeouts(handler); + struct hash_iterate_context *iter; + void *key, *value; + + iter = hash_iterate_init(handler->requests); + while (hash_iterate(iter, &key, &value)) { + struct auth_request *request = value; + + if (request->created + AUTH_REQUEST_TIMEOUT < ioloop_time) + auth_request_handler_remove(handler, request); + } + hash_iterate_deinit(iter); +} + +static const char *get_client_extra_fields(struct auth_request *request) +{ + const char **fields; + unsigned int src, dest; + + if (request->extra_fields == NULL) + return NULL; + + /* we only wish to remove all fields prefixed with "userdb_" */ + if (strstr(str_c(request->extra_fields), "userdb_") == NULL) + return str_c(request->extra_fields); + + fields = t_strsplit(str_c(request->extra_fields), "\t"); + for (src = dest = 0; fields[src] != NULL; src++) { + if (strncmp(fields[src], "userdb_", 7) != 0) + fields[dest++] = fields[src]; + } + fields[dest] = NULL; + return t_strarray_join(fields, "\t"); +} + +static void auth_callback(struct auth_request *request, + enum auth_client_result result, + const void *reply, size_t reply_size) +{ + struct auth_request_handler *handler = request->context; + string_t *str; + const char *fields; + + t_push(); + + str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size)); + if (handler->prepend_connect_uid) + str_printfa(str, "%u\t", request->connect_uid); + + switch (result) { + case AUTH_CLIENT_RESULT_CONTINUE: + str_printfa(str, "CONT\t%u\t", request->id); + base64_encode(reply, reply_size, str); + request->accept_input = TRUE; + handler->callback(str_c(str), handler->context); + break; + case AUTH_CLIENT_RESULT_SUCCESS: + str_printfa(str, "OK\t%u\tuser=%s", request->id, request->user); + if (reply_size > 0) { + str_append(str, "\tresp="); + base64_encode(reply, reply_size, str); + } + fields = get_client_extra_fields(request); + if (fields != NULL) { + str_append_c(str, '\t'); + str_append(str, fields); + } + + if (request->no_login || handler->master_callback == NULL) { + /* this request doesn't have to wait for master + process to pick it up. delete it */ + auth_request_handler_remove(handler, request); + } + handler->callback(str_c(str), handler->context); + break; + case AUTH_CLIENT_RESULT_FAILURE: + str_printfa(str, "FAIL\t%u", request->id); + if (request->user != NULL) + str_printfa(str, "\tuser=%s", request->user); + if (request->internal_failure) + str_append(str, "\ttemp"); + fields = get_client_extra_fields(request); + if (fields != NULL) { + str_append_c(str, '\t'); + str_append(str, fields); + } + + if (request->delayed_failure) { + /* we came here from flush_failures() */ + handler->callback(str_c(str), handler->context); + break; + } + + /* remove the request from requests-list */ + auth_request_ref(request); + auth_request_handler_remove(handler, request); + + if (request->no_failure_delay) { + /* passdb specifically requested not to delay the + reply. */ + handler->callback(str_c(str), handler->context); + auth_request_unref(request); + } else { + /* failure. don't announce it immediately to avoid + a) timing attacks, b) flooding */ + request->delayed_failure = TRUE; + handler->refcount++; + buffer_append(auth_failures_buf, + &request, sizeof(request)); + } + break; + } + /* NOTE: request may be destroyed now */ + + auth_request_handler_unref(handler); + + t_pop(); +} + +static void auth_request_handler_auth_fail(struct auth_request_handler *handler, + struct auth_request *request, + const char *reason) +{ + string_t *reply = t_str_new(64); + + auth_request_log_info(request, request->mech->mech_name, "%s", reason); + + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", request->connect_uid); + str_printfa(reply, "FAIL\t%u\treason=%s", request->id, reason); + handler->callback(str_c(reply), handler->context); + + auth_request_handler_remove(handler, request); } int auth_request_handler_auth_begin(struct auth_request_handler *handler, const char *args) { - return auth_request_handler_api->auth_begin(handler, args); + struct mech_module *mech; + struct auth_request *request; + const char *const *list, *name, *arg, *initial_resp; + const void *initial_resp_data; + size_t initial_resp_len; + unsigned int id; + buffer_t *buf; + int valid_client_cert; + + /* <id> <mechanism> [...] */ + list = t_strsplit(args, "\t"); + if (list[0] == NULL || list[1] == NULL) { + i_error("BUG: Authentication client %u " + "sent broken AUTH request", handler->client_pid); + return FALSE; + } + + id = (unsigned int)strtoul(list[0], NULL, 10); + + mech = mech_module_find(list[1]); + if (mech == NULL) { + /* unsupported mechanism */ + i_error("BUG: Authentication client %u requested unsupported " + "authentication mechanism %s", handler->client_pid, + str_sanitize(list[1], MAX_MECH_NAME_LEN)); + return FALSE; + } + + request = auth_request_new(handler->auth, mech, auth_callback, handler); + request->connect_uid = handler->connect_uid; + request->client_pid = handler->client_pid; + request->id = id; + + /* parse optional parameters */ + initial_resp = NULL; + valid_client_cert = FALSE; + for (list += 2; *list != NULL; list++) { + arg = strchr(*list, '='); + if (arg == NULL) { + name = *list; + arg = ""; + } else { + name = t_strdup_until(*list, arg); + arg++; + } + + if (strcmp(name, "lip") == 0) + (void)net_addr2ip(arg, &request->local_ip); + else if (strcmp(name, "rip") == 0) + (void)net_addr2ip(arg, &request->remote_ip); + else if (strcmp(name, "service") == 0) + request->service = p_strdup(request->pool, arg); + else if (strcmp(name, "resp") == 0) + initial_resp = arg; + else if (strcmp(name, "valid-client-cert") == 0) + valid_client_cert = TRUE; + } + + if (request->service == NULL) { + i_error("BUG: Authentication client %u " + "didn't specify service in request", + handler->client_pid); + auth_request_unref(request); + return FALSE; + } + + hash_insert(handler->requests, POINTER_CAST(id), request); + + if (request->auth->ssl_require_client_cert && !valid_client_cert) { + /* we fail without valid certificate */ + auth_request_handler_auth_fail(handler, request, + "Client didn't present valid SSL certificate"); + return TRUE; + } + + if (initial_resp == NULL) { + initial_resp_data = NULL; + initial_resp_len = 0; + } else { + size_t len = strlen(initial_resp); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(len)); + if (base64_decode(initial_resp, len, NULL, buf) < 0) { + auth_request_handler_auth_fail(handler, request, + "Invalid base64 data in initial response"); + return TRUE; + } + initial_resp_data = buf->data; + initial_resp_len = buf->used; + } + + /* handler is referenced until auth_callback is called. */ + handler->refcount++; + auth_request_initial(request, initial_resp_data, initial_resp_len); + return TRUE; } int auth_request_handler_auth_continue(struct auth_request_handler *handler, const char *args) { - return auth_request_handler_api->auth_continue(handler, args); + struct auth_request *request; + const char *data; + size_t data_len; + buffer_t *buf; + unsigned int id; + + data = strchr(args, '\t'); + if (data++ == NULL) { + i_error("BUG: Authentication client sent broken CONT request"); + return FALSE; + } + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(handler->requests, POINTER_CAST(id)); + if (request == NULL) { + string_t *reply = t_str_new(64); + + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", handler->connect_uid); + str_printfa(reply, "FAIL\t%u\treason=Timeouted", id); + handler->callback(str_c(reply), handler->context); + return TRUE; + } + + /* accept input only once after mechanism has sent a CONT reply */ + if (!request->accept_input) { + auth_request_handler_auth_fail(handler, request, + "Unexpected continuation"); + return TRUE; + } + request->accept_input = FALSE; + + data_len = strlen(data); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(data_len)); + if (base64_decode(data, data_len, NULL, buf) < 0) { + auth_request_handler_auth_fail(handler, request, + "Invalid base64 data in continued response"); + return TRUE; + } + + /* handler is referenced until auth_callback is called. */ + handler->refcount++; + auth_request_continue(request, buf->data, buf->used); + return TRUE; +} + +static void userdb_callback(const char *result, struct auth_request *request) +{ + struct auth_request_handler *handler = request->context; + string_t *reply; + + reply = t_str_new(256); + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", request->connect_uid); + if (result == NULL) + str_printfa(reply, "NOTFOUND\t%u", request->id); + else { + str_printfa(reply, "USER\t%u\t", request->id); + str_append(reply, result); + } + handler->master_callback(str_c(reply), handler->master_context); + + auth_request_unref(request); + auth_request_handler_unref(handler); } void auth_request_handler_master_request(struct auth_request_handler *handler, unsigned int id, unsigned int client_id) { - auth_request_handler_api->master_request(handler, id, client_id); -} + struct auth_request *request; + string_t *reply; + + reply = t_str_new(64); + if (handler->prepend_connect_uid) + str_printfa(reply, "%u\t", handler->connect_uid); + + request = hash_lookup(handler->requests, POINTER_CAST(client_id)); + if (request == NULL) { + i_error("Master request %u.%u not found", + handler->client_pid, client_id); + str_printfa(reply, "NOTFOUND\t%u", id); + handler->master_callback(str_c(reply), handler->master_context); + return; + } -void auth_request_handlers_flush_failures(void) -{ - auth_request_handler_api->flush_failures(); + auth_request_ref(request); + auth_request_handler_remove(handler, request); + + if (!request->successful) { + i_error("Master requested unfinished authentication request " + "%u.%u", handler->client_pid, client_id); + str_printfa(reply, "NOTFOUND\t%u", id); + handler->master_callback(str_c(reply), handler->master_context); + } else { + /* the request isn't being referenced anywhere anymore, + so we can do a bit of kludging.. replace the request's + old client_id with master's id. */ + request->id = id; + request->context = handler; + + /* handler is referenced until userdb_callback is called. */ + handler->refcount++; + auth_request_lookup_user(request, userdb_callback); + } } -void auth_request_handlers_init(int balancer) +void auth_request_handler_flush_failures(void) { - /* use balancer if we have it */ - auth_request_handler_api = balancer ? - &auth_request_handler_balancer : &auth_request_handler_default; + struct auth_request **auth_request; + size_t i, size; + + auth_request = buffer_get_modifyable_data(auth_failures_buf, &size); + size /= sizeof(*auth_request); - auth_request_handler_api->init(); + for (i = 0; i < size; i++) { + auth_request[i]->callback(auth_request[i], + AUTH_CLIENT_RESULT_FAILURE, NULL, 0); + auth_request_unref(auth_request[i]); + } + buffer_set_used_size(auth_failures_buf, 0); } -void auth_request_handlers_deinit(void) +static void auth_failure_timeout(void *context __attr_unused__) +{ + auth_request_handler_flush_failures(); +} + +void auth_request_handler_init(void) { - auth_request_handler_api->deinit(); + auth_failures_buf = buffer_create_dynamic(default_pool, 1024); + to_auth_failures = timeout_add(2000, auth_failure_timeout, NULL); } + +void auth_request_handler_deinit(void) +{ + buffer_free(auth_failures_buf); + timeout_remove(to_auth_failures); +}
--- a/src/auth/auth-request-handler.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-request-handler.h Tue Mar 01 00:19:21 2005 +0200 @@ -5,35 +5,6 @@ typedef void auth_request_callback_t(const char *reply, void *context); -struct auth_request_handler_api { - struct auth_request_handler * - (*create)(struct auth *auth, int prepend_connect_uid, - auth_request_callback_t *callback, void *context, - auth_request_callback_t *master_callback, - void *master_context); - void (*unref)(struct auth_request_handler *handler); - - void (*set)(struct auth_request_handler *handler, - unsigned int connect_uid, unsigned int client_pid); - - void (*check_timeouts)(struct auth_request_handler *handler); - int (*auth_begin)(struct auth_request_handler *handler, - const char *args); - int (*auth_continue)(struct auth_request_handler *handler, - const char *args); - void (*master_request)(struct auth_request_handler *handler, - unsigned int id, unsigned int client_id); - - void (*flush_failures)(void); - - void (*init)(void); - void (*deinit)(void); -}; - -extern struct auth_request_handler_api auth_request_handler_default; -extern struct auth_request_handler_api auth_request_handler_balancer; -extern struct auth_request_handler_api *auth_request_handler_api; - struct auth_request_handler * auth_request_handler_create(struct auth *auth, int prepend_connect_uid, auth_request_callback_t *callback, void *context, @@ -55,9 +26,9 @@ unsigned int id, unsigned int client_id); -void auth_request_handlers_flush_failures(void); +void auth_request_handler_flush_failures(void); -void auth_request_handlers_init(int balancer); -void auth_request_handlers_deinit(void); +void auth_request_handler_init(void); +void auth_request_handler_deinit(void); #endif
--- a/src/auth/auth-request.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-request.c Tue Mar 01 00:19:21 2005 +0200 @@ -12,6 +12,8 @@ #include "auth-client-connection.h" #include "auth-master-connection.h" #include "passdb.h" +#include "passdb-blocking.h" +#include "userdb-blocking.h" #include "passdb-cache.h" struct auth_request * @@ -72,6 +74,18 @@ return FALSE; } +void auth_request_export(struct auth_request *request, string_t *str) +{ + str_append(str, "user="); + str_append(str, request->user); + str_append(str, "\tservice="); + str_append(str, request->service); + str_append(str, "\tlip="); + str_append(str, net_ip2addr(&request->local_ip)); + str_append(str, "\trip="); + str_append(str, net_ip2addr(&request->remote_ip)); +} + void auth_request_initial(struct auth_request *request, const unsigned char *data, size_t data_size) { @@ -105,17 +119,21 @@ return; } + if (request->passdb_password == NULL) { + /* no password given by passdb, cannot cache this */ + return; + } + /* save all except the currently given password in cache */ str = t_str_new(32 + str_len(request->extra_fields)); - if (request->passdb_password != NULL) { - if (*request->passdb_password != '{') { - /* cached passwords must have a known scheme */ - str_append_c(str, '{'); - str_append(str, passdb->default_pass_scheme); - str_append_c(str, '}'); - } - str_append(str, request->passdb_password); + if (*request->passdb_password != '{') { + /* cached passwords must have a known scheme */ + str_append_c(str, '{'); + str_append(str, passdb->default_pass_scheme); + str_append_c(str, '}'); } + str_append(str, request->passdb_password); + if (request->extra_fields != NULL) { str_append_c(str, '\t'); str_append_str(str, request->extra_fields); @@ -127,8 +145,8 @@ auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str)); } -static void auth_request_verify_plain_callback(enum passdb_result result, - struct auth_request *request) +void auth_request_verify_plain_callback(enum passdb_result result, + struct auth_request *request) { auth_request_save_cache(request, result); @@ -157,6 +175,7 @@ const char *cache_key; request->mech_password = p_strdup(request->pool, password); + request->private_callback.verify_plain = callback; cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; if (cache_key != NULL) { @@ -167,15 +186,17 @@ } } - request->private_callback.verify_plain = callback; - passdb->verify_plain(request, password, - auth_request_verify_plain_callback); + if (passdb->blocking) + passdb_blocking_verify_plain(request); + else { + passdb->verify_plain(request, password, + auth_request_verify_plain_callback); + } } -static void -auth_request_lookup_credentials_callback(enum passdb_result result, - const char *credentials, - struct auth_request *request) +void auth_request_lookup_credentials_callback(enum passdb_result result, + const char *credentials, + struct auth_request *request) { auth_request_save_cache(request, result); @@ -208,15 +229,26 @@ } } + request->credentials = credentials; request->private_callback.lookup_credentials = callback; - passdb->lookup_credentials(request, credentials, - auth_request_lookup_credentials_callback); + + if (passdb->blocking) + passdb_blocking_lookup_credentials(request); + else { + passdb->lookup_credentials(request, credentials, + auth_request_lookup_credentials_callback); + } } void auth_request_lookup_user(struct auth_request *request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { - request->auth->userdb->lookup(request, callback, context); + struct userdb_module *userdb = request->auth->userdb; + + if (userdb->blocking) + userdb_blocking_lookup(request, callback); + else + userdb->lookup(request, callback); } int auth_request_set_username(struct auth_request *request,
--- a/src/auth/auth-request.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth-request.h Tue Mar 01 00:19:21 2005 +0200 @@ -30,8 +30,10 @@ union { verify_plain_callback_t *verify_plain; - lookup_credentials_callback_t *lookup_credentials; + lookup_credentials_callback_t *lookup_credentials; + userdb_callback_t *userdb; } private_callback; + enum passdb_credentials credentials; mech_callback_t *callback; void *context; @@ -58,6 +60,8 @@ void auth_request_fail(struct auth_request *request); void auth_request_internal_failure(struct auth_request *request); +void auth_request_export(struct auth_request *request, string_t *str); + void auth_request_initial(struct auth_request *request, const unsigned char *data, size_t data_size); void auth_request_continue(struct auth_request *request, @@ -70,7 +74,7 @@ enum passdb_credentials credentials, lookup_credentials_callback_t *callback); void auth_request_lookup_user(struct auth_request *request, - userdb_callback_t *callback, void *context); + userdb_callback_t *callback); int auth_request_set_username(struct auth_request *request, const char *username, const char **error_r); @@ -92,4 +96,10 @@ const char *subsystem, const char *format, ...) __attr_format__(3, 4); +void auth_request_verify_plain_callback(enum passdb_result result, + struct auth_request *request); +void auth_request_lookup_credentials_callback(enum passdb_result result, + const char *credentials, + struct auth_request *request); + #endif
--- a/src/auth/auth.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth.c Tue Mar 01 00:19:21 2005 +0200 @@ -9,7 +9,6 @@ #include "passdb.h" #include "auth.h" #include "auth-request-handler.h" -#include "auth-request-balancer.h" #include <stdlib.h> #include <unistd.h> @@ -174,8 +173,5 @@ userdb_deinit(auth); passdb_deinit(auth); - if (auth->balancer_worker != NULL) - auth_request_balancer_worker_destroy(auth->balancer_worker); - str_free(auth->mech_handshake); }
--- a/src/auth/auth.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/auth.h Tue Mar 01 00:19:21 2005 +0200 @@ -1,11 +1,7 @@ #ifndef __AUTH_H #define __AUTH_H -struct auth_balancer_worker; - struct auth { - struct auth_balancer_worker *balancer_worker; - struct mech_module_list *mech_modules; buffer_t *mech_handshake;
--- a/src/auth/common.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/common.h Tue Mar 01 00:19:21 2005 +0200 @@ -6,10 +6,10 @@ #define MASTER_SOCKET_FD 0 #define CLIENT_LISTEN_FD 3 -#define BALANCER_LISTEN_FD 4 +#define WORKER_SERVER_FD 4 extern struct ioloop *ioloop; -extern int standalone; +extern int standalone, worker; extern time_t process_start_time; #endif
--- a/src/auth/main.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/main.c Tue Mar 01 00:19:21 2005 +0200 @@ -12,7 +12,8 @@ #include "mech.h" #include "auth.h" #include "auth-request-handler.h" -#include "auth-request-balancer.h" +#include "auth-worker-server.h" +#include "auth-worker-client.h" #include "auth-master-interface.h" #include "auth-master-connection.h" #include "auth-client-connection.h" @@ -26,12 +27,12 @@ #include <sys/stat.h> struct ioloop *ioloop; -int standalone = FALSE; +int standalone = FALSE, worker = FALSE; time_t process_start_time; static buffer_t *masters_buf; static struct auth *auth; -static int balancer = FALSE, balancer_worker = FALSE; +static struct auth_worker_client *worker_client; static void sig_quit(int signo __attr_unused__) { @@ -186,7 +187,7 @@ password_schemes_init(); masters_buf = buffer_create_dynamic(default_pool, 64); - if (!balancer_worker) + if (!worker) add_extra_listeners(); /* Password lookups etc. may require roots, allow it. */ @@ -199,12 +200,17 @@ size_t i, size; process_start_time = ioloop_time; + lib_init_signals(sig_quit); mech_init(); auth_init(auth); - auth_request_handlers_init(balancer); + auth_request_handler_init(); - lib_init_signals(sig_quit); + if (worker) { + worker_client = + auth_worker_client_create(auth, WORKER_SERVER_FD); + return; + } standalone = getenv("DOVECOT_MASTER") == NULL; if (standalone) { @@ -232,23 +238,12 @@ if (chdir("/") < 0) i_fatal("chdir(/) failed: %m"); } - } else if (!balancer_worker) { + } else { master = auth_master_connection_create(auth, MASTER_SOCKET_FD); auth_master_connection_add_listener(master, CLIENT_LISTEN_FD, NULL, LISTENER_CLIENT); - if (balancer) { - auth_master_connection_add_listener(master, - BALANCER_LISTEN_FD, - NULL, - LISTENER_BALANCER); - } auth_client_connections_init(master); buffer_append(masters_buf, &master, sizeof(master)); - } else { - master = auth_master_connection_create(auth, MASTER_SOCKET_FD); - buffer_append(masters_buf, &master, sizeof(master)); - - auth_request_balancer_worker_init(auth); } /* everything initialized, notify masters that all is well */ @@ -266,21 +261,23 @@ if (lib_signal_kill != 0) i_warning("Killed with signal %d", lib_signal_kill); - auth_request_handlers_flush_failures(); - - if (balancer_worker) - auth_request_balancer_worker_deinit(); + if (worker_client != NULL) + auth_worker_client_destroy(worker_client); + else { + auth_request_handler_flush_failures(); - master = buffer_get_modifyable_data(masters_buf, &size); - size /= sizeof(*master); - for (i = 0; i < size; i++) - auth_master_connection_destroy(master[i]); + master = buffer_get_modifyable_data(masters_buf, &size); + size /= sizeof(*master); + for (i = 0; i < size; i++) + auth_master_connection_destroy(master[i]); + } - password_schemes_deinit(); - auth_request_handlers_deinit(); + auth_request_handler_deinit(); auth_deinit(auth); mech_deinit(); + auth_worker_server_deinit(); + password_schemes_deinit(); random_deinit(); closelog(); @@ -292,7 +289,7 @@ #ifdef DEBUG if (getenv("GDB") == NULL) - fd_debug_verify_leaks(BALANCER_LISTEN_FD + 1, 1024); + fd_debug_verify_leaks(WORKER_SERVER_FD + 1, 1024); #endif /* NOTE: we start rooted, so keep the code minimal until restrict_access_by_env() is called */ @@ -302,10 +299,8 @@ while (argv[1] != NULL) { if (strcmp(argv[1], "-F") == 0) foreground = TRUE; - else if (strcmp(argv[1], "-b") == 0) - balancer = TRUE; - else if (strcmp(argv[1], "-bw") == 0) - balancer_worker = TRUE; + else if (strcmp(argv[1], "-w") == 0) + worker = TRUE; argv++; }
--- a/src/auth/passdb-bsdauth.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-bsdauth.c Tue Mar 01 00:19:21 2005 +0200 @@ -58,7 +58,7 @@ struct passdb_module passdb_bsdauth = { "bsdauth", - "%u", "CRYPT", + "%u", "CRYPT", FALSE, NULL, NULL, bsdauth_deinit,
--- a/src/auth/passdb-checkpassword.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-checkpassword.c Tue Mar 01 00:19:21 2005 +0200 @@ -343,7 +343,7 @@ struct passdb_module passdb_checkpassword = { "checkpassword", - NULL, NULL, + NULL, NULL, FALSE, NULL, checkpassword_init,
--- a/src/auth/passdb-ldap.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-ldap.c Tue Mar 01 00:19:21 2005 +0200 @@ -239,7 +239,7 @@ struct passdb_module passdb_ldap = { "ldap", - NULL, NULL, + NULL, NULL, FALSE, passdb_ldap_preinit, passdb_ldap_init,
--- a/src/auth/passdb-pam.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-pam.c Tue Mar 01 00:19:21 2005 +0200 @@ -401,7 +401,7 @@ struct passdb_module passdb_pam = { "pam", - NULL, NULL, + NULL, NULL, FALSE, NULL, pam_init,
--- a/src/auth/passdb-passwd-file.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-passwd-file.c Tue Mar 01 00:19:21 2005 +0200 @@ -84,7 +84,7 @@ struct passdb_module passdb_passwd_file = { "passwd-file", - NULL, NULL, + NULL, NULL, FALSE, NULL, passwd_file_init,
--- a/src/auth/passdb-passwd.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-passwd.c Tue Mar 01 00:19:21 2005 +0200 @@ -55,7 +55,7 @@ struct passdb_module passdb_passwd = { "passwd", - "%u", "CRYPT", + "%u", "CRYPT", FALSE, NULL, NULL, passwd_deinit,
--- a/src/auth/passdb-shadow.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-shadow.c Tue Mar 01 00:19:21 2005 +0200 @@ -55,7 +55,7 @@ struct passdb_module passdb_shadow = { "shadow", - "%u", "CRYPT", + "%u", "CRYPT", FALSE, NULL, NULL, shadow_deinit,
--- a/src/auth/passdb-sql.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-sql.c Tue Mar 01 00:19:21 2005 +0200 @@ -174,7 +174,12 @@ static void passdb_sql_init(const char *args __attr_unused__) { + enum sql_db_flags flags; + db_sql_connect(passdb_sql_conn); + + flags = sql_get_flags(passdb_sql_conn->db); + passdb_sql.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; } static void passdb_sql_deinit(void) @@ -185,7 +190,7 @@ struct passdb_module passdb_sql = { "sql", - NULL, NULL, + NULL, NULL, FALSE, passdb_sql_preinit, passdb_sql_init,
--- a/src/auth/passdb-vpopmail.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb-vpopmail.c Tue Mar 01 00:19:21 2005 +0200 @@ -98,7 +98,7 @@ struct passdb_module passdb_vpopmail = { "vpopmail", - "%u", "CRYPT", + "%u", "CRYPT", FALSE, NULL, NULL, vpopmail_deinit,
--- a/src/auth/passdb.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb.c Tue Mar 01 00:19:21 2005 +0200 @@ -3,6 +3,7 @@ #include "common.h" #include "auth-module.h" #include "password-scheme.h" +#include "auth-worker-server.h" #include "passdb.h" #include "passdb-cache.h" @@ -162,6 +163,11 @@ i_assert(auth->passdb->default_pass_scheme != NULL || auth->passdb->cache_key == NULL); + + if (auth->passdb->blocking && !worker) { + /* blocking passdb - we need an auth server */ + auth_worker_server_init(); + } } void passdb_deinit(struct auth *auth)
--- a/src/auth/passdb.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/passdb.h Tue Mar 01 00:19:21 2005 +0200 @@ -42,6 +42,9 @@ /* Default password scheme for this module. If cache_key is set, must not be NULL. */ const char *default_pass_scheme; + /* If blocking is set to TRUE, use child processes to access + this passdb. */ + int blocking; void (*preinit)(const char *args); void (*init)(const char *args);
--- a/src/auth/userdb-ldap.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-ldap.c Tue Mar 01 00:19:21 2005 +0200 @@ -138,7 +138,7 @@ if (ret != LDAP_SUCCESS) { auth_request_log_error(auth_request, "ldap", "ldap_search() failed: %s", ldap_err2string(ret)); - urequest->userdb_callback(NULL, request->context); + urequest->userdb_callback(NULL, auth_request); return; } @@ -158,11 +158,11 @@ } } - urequest->userdb_callback(result, request->context); + urequest->userdb_callback(result, auth_request); } static void userdb_ldap_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { struct ldap_connection *conn = userdb_ldap_conn; const struct var_expand_table *vars; @@ -183,7 +183,6 @@ request = p_new(auth_request->pool, struct userdb_ldap_request, 1); request->request.callback = handle_request; - request->request.context = context; request->auth_request = auth_request; request->userdb_callback = callback; @@ -217,6 +216,7 @@ struct userdb_module userdb_ldap = { "ldap", + FALSE, userdb_ldap_preinit, userdb_ldap_init,
--- a/src/auth/userdb-passdb.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-passdb.c Tue Mar 01 00:19:21 2005 +0200 @@ -13,7 +13,7 @@ #include <stdlib.h> static void passdb_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { const char *const *args; string_t *str; @@ -24,7 +24,7 @@ if (auth_request->extra_fields == NULL) { auth_request_log_error(auth_request, "passdb", "passdb didn't return userdb entries"); - callback(NULL, context); + callback(NULL, auth_request); return; } @@ -76,14 +76,15 @@ } if (uid == (uid_t)-1 || gid == (gid_t)-1) - callback(NULL, context); + callback(NULL, auth_request); else - callback(str_c(str), context); + callback(str_c(str), auth_request); t_pop(); } struct userdb_module userdb_passdb = { "passdb", + FALSE, NULL, NULL,
--- a/src/auth/userdb-passwd-file.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-passwd-file.c Tue Mar 01 00:19:21 2005 +0200 @@ -13,14 +13,14 @@ struct passwd_file *userdb_pwf = NULL; static void passwd_file_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { struct passwd_user *pu; string_t *str; pu = db_passwd_file_lookup(userdb_pwf, auth_request); if (pu == NULL) { - callback(NULL, context); + callback(NULL, auth_request); return; } @@ -33,7 +33,7 @@ if (pu->mail != NULL) str_printfa(str, "\tmail=%s", pu->mail); - callback(str_c(str), context); + callback(str_c(str), auth_request); } static void passwd_file_init(const char *args) @@ -57,6 +57,7 @@ struct userdb_module userdb_passwd_file = { "passwd-file", + FALSE, NULL, passwd_file_init,
--- a/src/auth/userdb-passwd.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-passwd.c Tue Mar 01 00:19:21 2005 +0200 @@ -11,7 +11,7 @@ #include <pwd.h> static void passwd_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { struct passwd *pw; const char *result; @@ -19,7 +19,7 @@ pw = getpwnam(auth_request->user); if (pw == NULL) { auth_request_log_info(auth_request, "passwd", "unknown user"); - callback(NULL, context); + callback(NULL, auth_request); return; } @@ -27,11 +27,12 @@ "home=%s", pw->pw_name, pw->pw_name, dec2str(pw->pw_uid), dec2str(pw->pw_gid), pw->pw_dir); - callback(result, context); + callback(result, auth_request); } struct userdb_module userdb_passwd = { "passwd", + FALSE, NULL, NULL, NULL, passwd_lookup
--- a/src/auth/userdb-sql.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-sql.c Tue Mar 01 00:19:21 2005 +0200 @@ -18,9 +18,10 @@ struct userdb_sql_request { struct auth_request *auth_request; userdb_callback_t *callback; - void *context; }; +extern struct userdb_module userdb_sql; + static struct sql_connection *userdb_sql_conn; static const char *sql_query_get_result(struct sql_result *result, @@ -94,12 +95,12 @@ user_result = sql_query_get_result(result, auth_request); } - sql_request->callback(user_result, sql_request->context); + sql_request->callback(user_result, auth_request); i_free(sql_request); } static void userdb_sql_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { struct userdb_sql_request *sql_request; string_t *query; @@ -111,7 +112,6 @@ sql_request = i_new(struct userdb_sql_request, 1); sql_request->callback = callback; - sql_request->context = context; sql_request->auth_request = auth_request; auth_request_log_debug(auth_request, "sql", "%s", str_c(query)); @@ -127,7 +127,12 @@ static void userdb_sql_init(const char *args __attr_unused__) { + enum sql_db_flags flags; + db_sql_connect(userdb_sql_conn); + + flags = sql_get_flags(userdb_sql_conn->db); + userdb_sql.blocking = (flags & SQL_DB_FLAG_BLOCKING) != 0; } static void userdb_sql_deinit(void) @@ -137,6 +142,7 @@ struct userdb_module userdb_sql = { "sql", + FALSE, userdb_sql_preinit, userdb_sql_init,
--- a/src/auth/userdb-static.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-static.c Tue Mar 01 00:19:21 2005 +0200 @@ -15,7 +15,7 @@ static char *static_template; static void static_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { string_t *str; @@ -23,7 +23,7 @@ str_append(str, auth_request->user); var_expand(str, static_template, auth_request_get_var_expand_table(auth_request, NULL)); - callback(str_c(str), context); + callback(str_c(str), auth_request); } static void static_init(const char *args) @@ -78,6 +78,7 @@ struct userdb_module userdb_static = { "static", + FALSE, NULL, static_init,
--- a/src/auth/userdb-vpopmail.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb-vpopmail.c Tue Mar 01 00:19:21 2005 +0200 @@ -42,7 +42,7 @@ #ifdef USERDB_VPOPMAIL static void vpopmail_lookup(struct auth_request *auth_request, - userdb_callback_t *callback, void *context) + userdb_callback_t *callback) { char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT]; struct vqpasswd *vpw; @@ -52,7 +52,7 @@ vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain); if (vpw == NULL) { - callback(NULL, context); + callback(NULL, auth_request); return; } @@ -61,7 +61,7 @@ if (vget_assign(vpop_domain, NULL, 0, &uid, &gid) == NULL) { auth_request_log_info(auth_request, "vpopmail", "vget_assign(%s) failed", vpop_domain); - callback(NULL, context); + callback(NULL, auth_request); return; } @@ -74,14 +74,14 @@ auth_request_log_error(auth_request, "vpopmail", "make_user_dir(%s, %s) failed", vpop_user, vpop_domain); - callback(NULL, context); + callback(NULL, auth_request); return; } /* get the user again so pw_dir is visible */ vpw = vauth_getpw(vpop_user, vpop_domain); if (vpw == NULL) { - callback(NULL, context); + callback(NULL, auth_request); return; } } @@ -90,11 +90,12 @@ vpw->pw_name, dec2str(uid), dec2str(gid), vpw->pw_dir); - callback(result, context); + callback(result, auth_request); } struct userdb_module userdb_vpopmail = { "vpopmail", + FALSE, NULL, NULL, NULL, vpopmail_lookup
--- a/src/auth/userdb.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/auth/userdb.h Tue Mar 01 00:19:21 2005 +0200 @@ -3,17 +3,22 @@ struct auth_request; -typedef void userdb_callback_t(const char *result, void *context); +typedef void userdb_callback_t(const char *result, + struct auth_request *request); struct userdb_module { const char *name; + /* If blocking is set to TRUE, use child processes to access + this passdb. */ + int blocking; + void (*preinit)(const char *args); void (*init)(const char *args); void (*deinit)(void); void (*lookup)(struct auth_request *auth_request, - userdb_callback_t *callback, void *context); + userdb_callback_t *callback); }; uid_t userdb_parse_uid(struct auth_request *request, const char *str);
--- a/src/master/auth-process.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/master/auth-process.c Tue Mar 01 00:19:21 2005 +0200 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "env-util.h" #include "fd-close-on-exec.h" +#include "unix-socket-create.h" #include "network.h" #include "istream.h" #include "ostream.h" @@ -18,7 +19,6 @@ #include <unistd.h> #include <pwd.h> #include <syslog.h> -#include <sys/stat.h> #define MAX_INBUF_SIZE 8192 #define MAX_OUTBUF_SIZE 65536 @@ -27,13 +27,10 @@ struct auth_process_group *next; int listen_fd; - int balancer_listen_fd; struct auth_settings *set; unsigned int process_count; struct auth_process *processes; - - unsigned int need_balancer:1; }; struct auth_process { @@ -46,9 +43,11 @@ struct istream *input; struct ostream *output; + int worker_listen_fd; + struct io *worker_io; + struct hash_table *requests; - unsigned int balancer:1; unsigned int external:1; unsigned int version_received:1; unsigned int initialized:1; @@ -60,6 +59,7 @@ static struct auth_process_group *process_groups; static void auth_process_destroy(struct auth_process *p); +static int create_auth_worker(struct auth_process *process, int fd); void auth_process_request(struct auth_process *process, unsigned int login_pid, unsigned int login_id, void *context) @@ -253,12 +253,29 @@ } } +static void auth_worker_input(void *context) +{ + struct auth_process *p = context; + int fd; + + fd = net_accept(p->worker_listen_fd, NULL, NULL); + if (fd < 0) { + if (fd == -2) + i_fatal("net_accept(worker) failed: %m"); + return; + } + + net_set_nonblock(fd, TRUE); + fd_close_on_exec(fd, TRUE); + + create_auth_worker(p, fd); +} + static struct auth_process * -auth_process_new(pid_t pid, int fd, struct auth_process_group *group, - int balancer) +auth_process_new(pid_t pid, int fd, struct auth_process_group *group) { struct auth_process *p; - const char *handshake; + const char *path, *handshake; if (pid != 0) PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH); @@ -274,12 +291,21 @@ FALSE); p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL); - if (balancer) { - p->balancer = TRUE; - group->need_balancer = FALSE; - } else { - group->process_count++; - } + group->process_count++; + + path = t_strdup_printf("%s/auth-worker.%s", + group->set->parent->defaults->base_dir, + dec2str(pid)); + p->worker_listen_fd = + unix_socket_create(path, 0600, group->set->uid, + group->set->gid, 16); + if (p->worker_listen_fd == -1) + i_fatal("Couldn't create auth worker listener"); + + net_set_nonblock(p->worker_listen_fd, TRUE); + fd_close_on_exec(p->worker_listen_fd, TRUE); + p->worker_io = io_add(p->worker_listen_fd, IO_READ, + auth_worker_input, p); handshake = t_strdup_printf("VERSION\t%u\t%u\n", AUTH_MASTER_PROTOCOL_MAJOR_VERSION, @@ -296,6 +322,7 @@ struct hash_iterate_context *iter; void *key, *value; struct auth_process **pos; + const char *path; if (!p->initialized && io_loop_is_running(ioloop) && !p->external) { i_error("Auth process died too early - shutting down"); @@ -308,12 +335,16 @@ break; } } - if (p->balancer) { - /* the balancer process died, restart it */ - p->group->need_balancer = TRUE; - } else { - p->group->process_count--; - } + p->group->process_count--; + + path = t_strdup_printf("%s/auth-worker.%s", + p->group->set->parent->defaults->base_dir, + dec2str(p->pid)); + (void)unlink(path); + + if (close(p->worker_listen_fd) < 0) + i_error("close(worker_listen) failed: %m"); + io_remove(p->worker_io); iter = hash_iterate_init(p->requests); while (hash_iterate(iter, &key, &value)) @@ -358,48 +389,64 @@ net_set_nonblock(fd, TRUE); fd_close_on_exec(fd, TRUE); - auth = auth_process_new(0, fd, group, FALSE); + auth = auth_process_new(0, fd, group); auth->external = TRUE; return 0; } -static int unix_socket_create(const char *path, int mode, - uid_t uid, gid_t gid, int backlog) +static void auth_set_environment(struct auth_settings *set) { - mode_t old_umask; - int fd; + struct auth_socket_settings *as; + const char *str; + int i; - (void)unlink(path); - - old_umask = umask(0777 ^ mode); - fd = net_listen_unix(path, backlog); - umask(old_umask); + /* setup access environment */ + restrict_access_set_env(set->user, set->uid, set->gid, set->chroot, + 0, 0, NULL); - if (fd < 0) - i_fatal("Can't listen in UNIX socket %s: %m", path); - net_set_nonblock(fd, TRUE); - fd_close_on_exec(fd, TRUE); + /* set other environment */ + env_put("DOVECOT_MASTER=1"); + env_put(t_strconcat("AUTH_NAME=", set->name, NULL)); + env_put(t_strconcat("MECHANISMS=", set->mechanisms, NULL)); + env_put(t_strconcat("REALMS=", set->realms, NULL)); + env_put(t_strconcat("DEFAULT_REALM=", set->default_realm, NULL)); + env_put(t_strconcat("USERDB=", set->userdb, NULL)); + env_put(t_strconcat("PASSDB=", set->passdb, NULL)); + env_put(t_strconcat("USERNAME_CHARS=", set->username_chars, NULL)); + env_put(t_strconcat("USERNAME_TRANSLATION=", + set->username_translation, NULL)); + env_put(t_strconcat("ANONYMOUS_USERNAME=", + set->anonymous_username, NULL)); + env_put(t_strdup_printf("CACHE_SIZE=%u", set->cache_size)); + env_put(t_strdup_printf("CACHE_TTL=%u", set->cache_ttl)); - if (uid != (uid_t)-1 || gid != (gid_t)-1) { - /* set correct permissions */ - if (chown(path, uid, gid) < 0) { - i_fatal("login: chown(%s, %s, %s) failed: %m", - path, dec2str(uid), dec2str(gid)); - } + for (as = set->sockets, i = 1; as != NULL; as = as->next, i++) { + if (strcmp(as->type, "listen") != 0) + continue; + + str = t_strdup_printf("AUTH_%u", i); + socket_settings_env_put(str, &as->client); + socket_settings_env_put(t_strconcat(str, "_MASTER", NULL), + &as->master); } - return fd; + + if (set->verbose) + env_put("VERBOSE=1"); + if (set->debug) + env_put("VERBOSE_DEBUG=1"); + if (set->ssl_require_client_cert) + env_put("SSL_REQUIRE_CLIENT_CERT=1"); + + restrict_process_size(set->process_size, (unsigned int)-1); } -static int create_auth_process(struct auth_process_group *group, - int balancer_worker) +static int create_auth_process(struct auth_process_group *group) { struct auth_socket_settings *as; - const char *prefix, *str, *executable; + const char *prefix, *executable; struct log_io *log; pid_t pid; - int fd[2], log_fd, i, balancer; - - balancer = group->balancer_listen_fd != -1 && !balancer_worker; + int fd[2], log_fd, i; /* see if this is a connect socket */ as = group->set->sockets; @@ -435,7 +482,7 @@ net_set_nonblock(fd[0], TRUE); fd_close_on_exec(fd[0], TRUE); - auth_process_new(pid, fd[0], group, balancer); + auth_process_new(pid, fd[0], group); (void)close(fd[1]); (void)close(log_fd); return 0; @@ -460,76 +507,94 @@ child_process_init_env(); - if (!balancer_worker) { - i_assert(group->balancer_listen_fd != 3); - if (group->listen_fd != 3) { - if (dup2(group->listen_fd, 3) < 0) - i_fatal("dup2() failed: %m"); - } - fd_close_on_exec(3, FALSE); + if (group->listen_fd != 3) { + if (dup2(group->listen_fd, 3) < 0) + i_fatal("dup2() failed: %m"); } - - if (balancer) { - if (group->balancer_listen_fd != 4) { - if (dup2(group->balancer_listen_fd, 4) < 0) - i_fatal("dup2() failed: %m"); - } - fd_close_on_exec(4, FALSE); - } + fd_close_on_exec(3, FALSE); for (i = 0; i <= 2; i++) fd_close_on_exec(i, FALSE); - /* setup access environment */ - restrict_access_set_env(group->set->user, group->set->uid, - group->set->gid, group->set->chroot, - 0, 0, NULL); + auth_set_environment(group->set); - /* set other environment */ - env_put("DOVECOT_MASTER=1"); - env_put(t_strconcat("AUTH_NAME=", group->set->name, NULL)); - env_put(t_strconcat("MECHANISMS=", group->set->mechanisms, NULL)); - env_put(t_strconcat("REALMS=", group->set->realms, NULL)); - env_put(t_strconcat("DEFAULT_REALM=", group->set->default_realm, NULL)); - env_put(t_strconcat("USERDB=", group->set->userdb, NULL)); - env_put(t_strconcat("PASSDB=", group->set->passdb, NULL)); - env_put(t_strconcat("USERNAME_CHARS=", group->set->username_chars, NULL)); - env_put(t_strconcat("USERNAME_TRANSLATION=", - group->set->username_translation, NULL)); - env_put(t_strconcat("ANONYMOUS_USERNAME=", - group->set->anonymous_username, NULL)); - env_put(t_strdup_printf("CACHE_SIZE=%u", group->set->cache_size)); - env_put(t_strdup_printf("CACHE_TTL=%u", group->set->cache_ttl)); - - for (as = group->set->sockets, i = 1; as != NULL; as = as->next, i++) { - if (strcmp(as->type, "listen") != 0) - continue; - - str = t_strdup_printf("AUTH_%u", i); - socket_settings_env_put(str, &as->client); - socket_settings_env_put(t_strconcat(str, "_MASTER", NULL), - &as->master); - } - - if (group->set->verbose) - env_put("VERBOSE=1"); - if (group->set->debug) - env_put("VERBOSE_DEBUG=1"); - if (group->set->ssl_require_client_cert) - env_put("SSL_REQUIRE_CLIENT_CERT=1"); - - restrict_process_size(group->set->process_size, (unsigned int)-1); + env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s", + group->set->parent->defaults->base_dir, + dec2str(getpid()))); + env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u", + group->set->worker_max_count)); /* make sure we don't leak syslog fd, but do it last so that any errors above will be logged */ closelog(); executable = group->set->executable; - if (balancer || balancer_worker) { - /* we are running in balancer mode */ - executable = t_strconcat(executable, balancer_worker ? - " -bw" : " -b", NULL); + client_process_exec(executable, ""); + i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); + return -1; +} + +static int create_auth_worker(struct auth_process *process, int fd) +{ + struct log_io *log; + const char *prefix, *executable; + pid_t pid; + int log_fd, i; + + log_fd = log_create_pipe(&log, 0); + if (log_fd < 0) + pid = -1; + else { + pid = fork(); + if (pid < 0) + i_error("fork() failed: %m"); + } + + if (pid < 0) { + (void)close(log_fd); + return -1; } + + if (pid != 0) { + /* master */ + PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH_WORKER); + prefix = t_strdup_printf("auth-worker(%s): ", + process->group->set->name); + log_set_prefix(log, prefix); + (void)close(fd); + (void)close(log_fd); + return 0; + } + + prefix = t_strdup_printf("master-auth-worker(%s): ", + process->group->set->name); + log_set_prefix(log, prefix); + + /* set stdin and stdout to /dev/null, so anything written into it + gets ignored. */ + if (dup2(null_fd, 0) < 0) + i_fatal("dup2(stdin) failed: %m"); + if (dup2(null_fd, 1) < 0) + i_fatal("dup2(stdout) failed: %m"); + + if (dup2(log_fd, 2) < 0) + i_fatal("dup2(stderr) failed: %m"); + + if (dup2(fd, 4) < 0) + i_fatal("dup2(stdin) failed: %m"); + + for (i = 0; i <= 2; i++) + fd_close_on_exec(i, FALSE); + fd_close_on_exec(4, FALSE); + + child_process_init_env(); + auth_set_environment(process->group->set); + + /* make sure we don't leak syslog fd, but do it last so that + any errors above will be logged */ + closelog(); + + executable = t_strconcat(process->group->set->executable, " -w", NULL); client_process_exec(executable, ""); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); return -1; @@ -569,17 +634,11 @@ auth_set->name, NULL); group->listen_fd = unix_socket_create(path, 0660, master_uid, auth_set->parent->login_gid, 16); + if (group->listen_fd == -1) + i_fatal("Couldn't create auth process listener"); - if (auth_set->count <= 1) - group->balancer_listen_fd = -1; - else { - path = t_strconcat(auth_set->parent->defaults->base_dir, "/", - auth_set->name, "-balancer", NULL); - group->balancer_listen_fd = - unix_socket_create(path, 0600, (uid_t)-1, (gid_t)-1, - group->set->count); - group->need_balancer = TRUE; - } + net_set_nonblock(group->listen_fd, TRUE); + fd_close_on_exec(group->listen_fd, TRUE); } static void auth_process_group_destroy(struct auth_process_group *group) @@ -597,12 +656,6 @@ group->set->name, NULL); (void)unlink(path); - if (group->balancer_listen_fd != -1) { - path = t_strconcat(group->set->parent->defaults->base_dir, "/", - group->set->name, "-balancer", NULL); - (void)unlink(path); - } - if (close(group->listen_fd) < 0) i_error("close(%s) failed: %m", path); i_free(group); @@ -637,7 +690,6 @@ { struct auth_process_group *group; unsigned int count; - int balancer_worker; if (process_groups == NULL) { /* first time here, create the groups */ @@ -645,13 +697,9 @@ } for (group = process_groups; group != NULL; group = group->next) { - if (group->need_balancer) - (void)create_auth_process(group, FALSE); - - balancer_worker = group->balancer_listen_fd != -1; count = group->process_count; for (; count < group->set->count; count++) - (void)create_auth_process(group, balancer_worker); + (void)create_auth_process(group); } }
--- a/src/master/common.h Tue Mar 01 00:15:25 2005 +0200 +++ b/src/master/common.h Tue Mar 01 00:19:21 2005 +0200 @@ -10,6 +10,7 @@ enum process_type { PROCESS_TYPE_UNKNOWN, PROCESS_TYPE_AUTH, + PROCESS_TYPE_AUTH_WORKER, PROCESS_TYPE_LOGIN, PROCESS_TYPE_IMAP, PROCESS_TYPE_POP3,
--- a/src/master/main.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/master/main.c Tue Mar 01 00:19:21 2005 +0200 @@ -25,6 +25,7 @@ const char *process_names[PROCESS_TYPE_MAX] = { "unknown", "auth", + "auth-worker", "login", "imap", "pop3",
--- a/src/master/master-settings.c Tue Mar 01 00:15:25 2005 +0200 +++ b/src/master/master-settings.c Tue Mar 01 00:19:21 2005 +0200 @@ -155,6 +155,7 @@ DEF(SET_BOOL, ssl_require_client_cert), DEF(SET_INT, count), + DEF(SET_INT, worker_max_count), DEF(SET_INT, process_size), { 0, NULL, 0 } @@ -330,6 +331,7 @@ MEMBER(ssl_require_client_cert) FALSE, MEMBER(count) 1, + MEMBER(worker_max_count) 30, MEMBER(process_size) 256, /* .. */