changeset 10903:6e639833c3fc HEAD

auth: Initial support for per-protocol auth settings. Currently the list of services is hard-coded. This should be changed so that config lookup returns the service names.
author Timo Sirainen <tss@iki.fi>
date Sat, 13 Mar 2010 22:54:41 +0200
parents 2b56c8b1e5ad
children 612db456c090
files src/auth/auth-client-connection.c src/auth/auth-master-connection.c src/auth/auth-request-handler.c src/auth/auth-request-handler.h src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-settings.c src/auth/auth-settings.h src/auth/auth-worker-client.c src/auth/auth.c src/auth/auth.h src/auth/db-ldap.c src/auth/main.c src/auth/mech-anonymous.c src/auth/mech-digest-md5.c src/auth/mech-gssapi.c src/auth/mech-rpa.c src/auth/mech-winbind.c src/auth/passdb-cache.c src/auth/passdb-ldap.c src/auth/passdb-pam.c src/auth/passdb.c src/auth/passdb.h src/auth/userdb-ldap.c src/auth/userdb-prefetch.c src/auth/userdb.c src/auth/userdb.h
diffstat 27 files changed, 258 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-client-connection.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-client-connection.c	Sat Mar 13 22:54:41 2010 +0200
@@ -174,8 +174,7 @@
 				conn->auth->set->debug_passwords ? line :
 				auth_line_hide_pass(line));
 		}
-		return auth_request_handler_auth_begin(conn->auth,
-						       conn->request_handler,
+		return auth_request_handler_auth_begin(conn->request_handler,
 						       line + 5);
 	}
 	if (strncmp(line, "CONT\t", 5) == 0) {
--- a/src/auth/auth-master-connection.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-master-connection.c	Sat Mar 13 22:54:41 2010 +0200
@@ -122,7 +122,7 @@
 		return -1;
 	}
 
-	auth_request = auth_request_new_dummy(conn->auth);
+	auth_request = auth_request_new_dummy();
 	auth_request->id = (unsigned int)strtoul(list[0], NULL, 10);
 	auth_request->context = conn;
 	auth_master_connection_ref(conn);
@@ -151,6 +151,8 @@
 		auth_request_unref(&auth_request);
 		return -1;
 	}
+
+	auth_request_init(auth_request);
 	*request_r = auth_request;
 	return 1;
 }
--- a/src/auth/auth-request-handler.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-request-handler.c	Sat Mar 13 22:54:41 2010 +0200
@@ -302,8 +302,7 @@
 	}
 }
 
-bool auth_request_handler_auth_begin(struct auth *auth,
-				     struct auth_request_handler *handler,
+bool auth_request_handler_auth_begin(struct auth_request_handler *handler,
 				     const char *args)
 {
 	const struct mech_module *mech;
@@ -332,7 +331,7 @@
 		return FALSE;
 	}
 
-	request = auth_request_new(auth, mech, auth_callback, handler);
+	request = auth_request_new(mech, auth_callback, handler);
 	request->handler = handler;
 	request->connect_uid = handler->connect_uid;
 	request->client_pid = handler->client_pid;
@@ -375,12 +374,13 @@
 		auth_request_unref(&request);
 		return FALSE;
 	}
+	auth_request_init(request);
 
 	request->to_abort = timeout_add(AUTH_REQUEST_TIMEOUT * 1000,
 					auth_request_timeout, request);
 	hash_table_insert(handler->requests, POINTER_CAST(id), request);
 
-	if (request->auth->set->ssl_require_client_cert &&
+	if (request->set->ssl_require_client_cert &&
 	    !request->valid_client_cert) {
 		/* we fail without valid certificate */
                 auth_request_handler_auth_fail(handler, request,
@@ -579,7 +579,7 @@
 
 		/* FIXME: assumess that failure_delay is always the same. */
 		diff = ioloop_time - auth_request->last_access;
-		if (diff < (time_t)auth_request->auth->set->failure_delay &&
+		if (diff < (time_t)auth_request->set->failure_delay &&
 		    !flush_all)
 			break;
 
--- a/src/auth/auth-request-handler.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-request-handler.h	Sat Mar 13 22:54:41 2010 +0200
@@ -29,8 +29,7 @@
 			      unsigned int connect_uid,
 			      unsigned int client_pid);
 
-bool auth_request_handler_auth_begin(struct auth *auth,
-				     struct auth_request_handler *handler,
+bool auth_request_handler_auth_begin(struct auth_request_handler *handler,
 				     const char *args);
 bool auth_request_handler_auth_continue(struct auth_request_handler *handler,
 					const char *args);
--- a/src/auth/auth-request.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-request.c	Sat Mar 13 22:54:41 2010 +0200
@@ -30,20 +30,18 @@
 			   const char *subsystem);
 
 struct auth_request *
-auth_request_new(struct auth *auth, const struct mech_module *mech,
+auth_request_new(const struct mech_module *mech,
 		 mech_callback_t *callback, void *context)
 {
 	struct auth_request *request;
 
 	request = mech->auth_new();
 	request->state = AUTH_REQUEST_STATE_NEW;
-	request->passdb = auth->passdbs;
-	request->userdb = auth->userdbs;
 
 	request->refcount = 1;
 	request->last_access = ioloop_time;
 
-	request->auth = auth;
+	request->set = global_auth_settings;
 	request->mech = mech;
 	request->mech_name = mech == NULL ? NULL : mech->mech_name;
 	request->callback = callback;
@@ -51,7 +49,7 @@
 	return request;
 }
 
-struct auth_request *auth_request_new_dummy(struct auth *auth)
+struct auth_request *auth_request_new_dummy(void)
 {
 	struct auth_request *auth_request;
 	pool_t pool;
@@ -62,18 +60,26 @@
 
 	auth_request->refcount = 1;
 	auth_request->last_access = ioloop_time;
-
-	if (auth == NULL) {
-		auth = p_new(pool, struct auth, 1);
-		auth->set = global_auth_settings;
-	}
-	auth_request->auth = auth;
-	auth_request->passdb = auth->passdbs;
-	auth_request->userdb = auth->userdbs;
+	auth_request->set = global_auth_settings;
 
 	return auth_request;
 }
 
+void auth_request_init(struct auth_request *request)
+{
+	struct auth *auth;
+
+	auth = auth_request_get_auth(request);
+	request->set = auth->set;
+	request->passdb = auth->passdbs;
+	request->userdb = auth->userdbs;
+}
+
+struct auth *auth_request_get_auth(struct auth_request *request)
+{
+	return auth_find_service(request->service);
+}
+
 void auth_request_success(struct auth_request *request,
 			  const void *data, size_t data_size)
 {
@@ -181,7 +187,7 @@
 	else if (strcmp(key, "original_username") == 0)
 		request->original_username = p_strdup(request->pool, value);
 	else if (strcmp(key, "cert_username") == 0) {
-		if (request->auth->set->ssl_username_from_cert) {
+		if (request->set->ssl_username_from_cert) {
 			/* get username from SSL certificate. it overrides
 			   the username given by the auth mechanism. */
 			request->user = p_strdup(request->pool, value);
@@ -347,7 +353,7 @@
 
 	/* the authentication continues with passdb lookup for the
 	   requested_login_user. */
-	request->passdb = request->auth->passdbs;
+	request->passdb = auth_request_get_auth(request)->passdbs;
 	return FALSE;
 }
 
@@ -543,7 +549,7 @@
 			request->credentials_scheme,
                 	request->private_callback.lookup_credentials);
 	} else {
-		if (request->auth->set->debug_passwords &&
+		if (request->set->debug_passwords &&
 		    result == PASSDB_RESULT_OK) {
 			auth_request_log_debug(request, "password",
 				"Credentials: %s",
@@ -724,10 +730,10 @@
 		   request->client_pid != 0) {
 		/* this was an actual login attempt, the user should
 		   have been found. */
-		if (request->auth->userdbs->next == NULL) {
+		if (auth_request_get_auth(request)->userdbs->next == NULL) {
 			auth_request_log_error(request, "userdb",
 				"user not found from userdb %s",
-				request->auth->userdbs->userdb->iface->name);
+				request->userdb->userdb->iface->name);
 		} else {
 			auth_request_log_error(request, "userdb",
 				"user not found from any userdbs");
@@ -787,7 +793,7 @@
 auth_request_fix_username(struct auth_request *request, const char *username,
                           const char **error_r)
 {
-	const struct auth_settings *set = request->auth->set;
+	const struct auth_settings *set = request->set;
 	unsigned char *p;
 	char *user;
 
@@ -835,7 +841,7 @@
 bool auth_request_set_username(struct auth_request *request,
 			       const char *username, const char **error_r)
 {
-	const struct auth_settings *set = request->auth->set;
+	const struct auth_settings *set = request->set;
 	const char *p, *login_username = NULL;
 
 	if (*set->master_user_separator != '\0' && !request->userdb_lookup) {
@@ -905,7 +911,7 @@
 	}
 
         /* lookup request->user from masterdb first */
-        request->passdb = request->auth->masterdbs;
+        request->passdb = auth_request_get_auth(request)->masterdbs;
 
         request->requested_login_user =
                 auth_request_fix_username(request, username, error_r);
@@ -1326,7 +1332,7 @@
 					const char *subsystem)
 {
 	string_t *str;
-	const char *log_type = request->auth->set->verbose_passwords;
+	const char *log_type = request->set->verbose_passwords;
 
 	if (strcmp(log_type, "no") == 0) {
 		auth_request_log_info(request, subsystem, "Password mismatch");
@@ -1401,7 +1407,7 @@
 	i_assert(ret >= 0);
 	if (ret == 0) {
 		auth_request_log_password_mismatch(request, subsystem);
-		if (request->auth->set->debug_passwords) T_BEGIN {
+		if (request->set->debug_passwords) T_BEGIN {
 			log_password_failure(request, plain_password,
 					     crypted_password, scheme,
 					     request->original_username,
@@ -1532,7 +1538,7 @@
 {
 	va_list va;
 
-	if (!auth_request->auth->set->debug)
+	if (!auth_request->set->debug)
 		return;
 
 	va_start(va, format);
@@ -1548,7 +1554,7 @@
 {
 	va_list va;
 
-	if (!auth_request->auth->set->verbose)
+	if (!auth_request->set->verbose)
 		return;
 
 	va_start(va, format);
--- a/src/auth/auth-request.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-request.h	Sat Mar 13 22:54:41 2010 +0200
@@ -56,7 +56,7 @@
 
 	const struct mech_module *mech;
 	struct auth_request_handler *handler;
-	struct auth *auth;
+	const struct auth_settings *set;
         struct auth_passdb *passdb;
         struct auth_userdb *userdb;
 
@@ -111,9 +111,12 @@
 };
 
 struct auth_request *
-auth_request_new(struct auth *auth, const struct mech_module *mech,
+auth_request_new(const struct mech_module *mech,
 		 mech_callback_t *callback, void *context);
-struct auth_request *auth_request_new_dummy(struct auth *auth);
+struct auth_request *auth_request_new_dummy(void);
+void auth_request_init(struct auth_request *request);
+struct auth *auth_request_get_auth(struct auth_request *request);
+
 void auth_request_ref(struct auth_request *request);
 void auth_request_unref(struct auth_request **request);
 
--- a/src/auth/auth-settings.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-settings.c	Sat Mar 13 22:54:41 2010 +0200
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "array.h"
 #include "settings-parser.h"
+#include "master-service.h"
 #include "master-service-settings.h"
 #include "service-settings.h"
 #include "auth-settings.h"
@@ -305,19 +306,25 @@
 
 struct auth_settings *global_auth_settings;
 
-struct auth_settings *
-auth_settings_read(struct master_service *service)
+struct auth_settings *auth_settings_read(const char *service)
 {
 	static const struct setting_parser_info *set_roots[] = {
 		&auth_setting_parser_info,
 		NULL
 	};
+ 	struct master_service_settings_input input;
+	struct master_service_settings_output output;
 	const char *error;
 	void **sets;
 
-	if (master_service_settings_read_simple(service, set_roots, &error) < 0)
+	memset(&input, 0, sizeof(input));
+	input.roots = set_roots;
+	input.module = "auth";
+	input.service = service;
+	if (master_service_settings_read(master_service, &input,
+					 &output, &error) < 0)
 		i_fatal("Error reading configuration: %s", error);
 
-	sets = master_service_settings_get_others(service);
+	sets = master_service_settings_get_others(master_service);
 	return sets[0];
 }
--- a/src/auth/auth-settings.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-settings.h	Sat Mar 13 22:54:41 2010 +0200
@@ -52,6 +52,6 @@
 
 extern struct auth_settings *global_auth_settings;
 
-struct auth_settings *auth_settings_read(struct master_service *service);
+struct auth_settings *auth_settings_read(const char *service);
 
 #endif
--- a/src/auth/auth-worker-client.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth-worker-client.c	Sat Mar 13 22:54:41 2010 +0200
@@ -57,7 +57,7 @@
 	struct auth_request *auth_request;
 	const char *key, *value, *const *tmp;
 
-	auth_request = auth_request_new_dummy(client->auth);
+	auth_request = auth_request_new_dummy();
 
 	client->refcount++;
 	auth_request->context = client;
@@ -76,6 +76,7 @@
 		}
 	}
 
+	auth_request_init(auth_request);
 	return auth_request;
 }
 
@@ -170,7 +171,7 @@
 
 	if (passdb == NULL) {
 		/* could be a masterdb */
-		passdb = auth_request->auth->masterdbs;
+		passdb = auth_request_get_auth(auth_request)->masterdbs;
 		while (passdb != NULL && passdb->passdb->id != passdb_id)
 			passdb = passdb->next;
 
--- a/src/auth/auth.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth.c	Sat Mar 13 22:54:41 2010 +0200
@@ -20,6 +20,8 @@
 	.args = ""
 };
 
+static ARRAY_DEFINE(auths, struct auth *);
+
 static void
 auth_passdb_preinit(struct auth *auth, const struct auth_passdb_settings *set)
 {
@@ -51,7 +53,8 @@
 }
 
 struct auth *
-auth_preinit(struct auth_settings *set, const struct mechanisms_register *reg)
+auth_preinit(const struct auth_settings *set, const char *service,
+	     const struct mechanisms_register *reg)
 {
 	struct auth_passdb_settings *const *passdbs;
 	struct auth_userdb_settings *const *userdbs;
@@ -62,6 +65,7 @@
 	pool = pool_alloconly_create("auth", 2048);
 	auth = p_new(pool, struct auth, 1);
 	auth->pool = pool;
+	auth->service = p_strdup(pool, service);
 	auth->set = set;
 	auth->reg = reg;
 
@@ -227,3 +231,63 @@
 
 	pool_unref(&auth->pool);
 }
+
+struct auth *auth_find_service(const char *name)
+{
+	struct auth *const *a;
+	unsigned int i, count;
+
+	a = array_get(&auths, &count);
+	if (name != NULL) {
+		for (i = 1; i < count; i++) {
+			if (strcmp(a[i]->service, name) == 0)
+				return a[i];
+		}
+	}
+	return a[0];
+}
+
+void auths_preinit(const struct auth_settings *set,
+		   const struct mechanisms_register *reg)
+{
+	static const char *services[] = {
+		"imap", "pop3", "lda", "lmtp", "managesieve"
+	};
+	const struct auth_settings *service_set;
+	struct auth *auth;
+	unsigned int i;
+
+	i_array_init(&auths, 8);
+
+	auth = auth_preinit(set, NULL, reg);
+	array_append(&auths, &auth, 1);
+
+	/* FIXME: this is ugly.. the service names should be coming from
+	   the first config lookup */
+	for (i = 0; i < N_ELEMENTS(services); i++) {
+		service_set = auth_settings_read(services[i]);
+		auth = auth_preinit(service_set, services[i], reg);
+		array_append(&auths, &auth, 1);
+	}
+}
+
+void auths_init(void)
+{
+	struct auth *const *auth;
+
+	array_foreach(&auths, auth)
+		auth_init(*auth);
+}
+
+void auths_deinit(void)
+{
+	struct auth **auth;
+	unsigned int i, count;
+
+	/* deinit in reverse order, because modules have been allocated by
+	   the first auth pool that used them */
+	auth = array_get_modifiable(&auths, &count);
+	for (i = count; i > 0; i--)
+		auth_deinit(&auth[i-1]);
+	array_free(&auths);
+}
--- a/src/auth/auth.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/auth.h	Sat Mar 13 22:54:41 2010 +0200
@@ -21,6 +21,7 @@
 
 struct auth {
 	pool_t pool;
+	const char *service;
 	const struct auth_settings *set;
 
 	const struct mechanisms_register *reg;
@@ -29,9 +30,19 @@
 	struct auth_userdb *userdbs;
 };
 
+extern struct auth_penalty *auth_penalty;
+
 struct auth *
-auth_preinit(struct auth_settings *set, const struct mechanisms_register *reg);
+auth_preinit(const struct auth_settings *set, const char *service,
+	     const struct mechanisms_register *mech_reg);
 void auth_init(struct auth *auth);
 void auth_deinit(struct auth **auth);
 
+struct auth *auth_find_service(const char *name);
+
+void auths_preinit(const struct auth_settings *set,
+		   const struct mechanisms_register *reg);
+void auths_init(void);
+void auths_deinit(void);
+
 #endif
--- a/src/auth/db-ldap.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/db-ldap.c	Sat Mar 13 22:54:41 2010 +0200
@@ -1085,7 +1085,7 @@
 		ctx->static_attrs = t_strsplit(str_c(str), ",");
 	}
 
-	if (auth_request->auth->set->debug)
+	if (auth_request->set->debug)
 		ctx->debug = t_str_new(256);
 
 	ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber);
@@ -1157,7 +1157,7 @@
 	if (ctx->debug != NULL) {
 		if (!first)
 			str_append_c(ctx->debug, '/');
-		if (ctx->auth_request->auth->set->debug_passwords ||
+		if (ctx->auth_request->set->debug_passwords ||
 		    strcmp(ctx->name, "password") != 0)
 			str_append(ctx->debug, ctx->value);
 		else
--- a/src/auth/main.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/main.c	Sat Mar 13 22:54:41 2010 +0200
@@ -38,7 +38,6 @@
 struct auth_penalty *auth_penalty;
 
 static struct module *modules = NULL;
-static struct auth *auth;
 static struct mechanisms_register *mech_reg;
 static ARRAY_DEFINE(listen_fd_types, enum auth_socket_type);
 
@@ -66,10 +65,10 @@
 	modules = module_dir_load(AUTH_MODULE_DIR, NULL, &mod_set);
 	module_dir_init(modules);
 
+	auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
 	mech_init(global_auth_settings);
 	mech_reg = mech_register_init(global_auth_settings);
-	auth = auth_preinit(global_auth_settings, mech_reg);
-	auth_penalty = auth_penalty_init(AUTH_PENALTY_ANVIL_PATH);
+	auths_preinit(global_auth_settings, mech_reg);
 
 	/* Password lookups etc. may require roots, allow it. */
 	restrict_access_by_env(NULL, FALSE);
@@ -89,7 +88,7 @@
 	child_wait_init();
 	password_schemes_init();
 	auth_worker_server_init();
-	auth_init(auth);
+	auths_init();
 	auth_request_handler_init();
 	auth_master_connections_init();
 	auth_client_connections_init();
@@ -112,8 +111,8 @@
 	auth_master_connections_deinit();
         auth_worker_server_deinit();
 
-	mech_deinit(auth->set);
-	auth_deinit(&auth);
+	mech_deinit(global_auth_settings);
+	auths_deinit();
 	mech_register_deinit(&mech_reg);
 	auth_penalty_deinit(&auth_penalty);
 
@@ -137,13 +136,15 @@
 		(void)close(conn->fd);
 		return;
 	}
-	(void)auth_worker_client_create(auth, conn->fd);
+
+	(void)auth_worker_client_create(auth_find_service(NULL), conn->fd);
 }
 
 static void client_connected(const struct master_service_connection *conn)
 {
 	enum auth_socket_type *type;
 	const char *name, *suffix;
+	struct auth *auth;
 
 	type = array_idx_modifiable(&listen_fd_types, conn->listen_fd);
 	if (*type == AUTH_SOCKET_UNKNOWN) {
@@ -166,6 +167,7 @@
 		}
 	}
 
+	auth = auth_find_service(NULL);
 	switch (*type) {
 	case AUTH_SOCKET_MASTER:
 		(void)auth_master_connection_create(auth, conn->fd, FALSE);
@@ -198,7 +200,7 @@
 		}
 	}
 
-	global_auth_settings = auth_settings_read(master_service);
+	global_auth_settings = auth_settings_read(NULL);
 	main_preinit();
 
 	master_service_init_finish(master_service);
--- a/src/auth/mech-anonymous.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/mech-anonymous.c	Sat Mar 13 22:54:41 2010 +0200
@@ -7,9 +7,9 @@
 mech_anonymous_auth_continue(struct auth_request *request,
 			     const unsigned char *data, size_t data_size)
 {
-	i_assert(*request->auth->set->anonymous_username != '\0');
+	i_assert(*request->set->anonymous_username != '\0');
 
-	if (request->auth->set->verbose) {
+	if (request->set->verbose) {
 		/* temporarily set the user to the one that was given,
 		   so that the log message goes right */
 		request->user =
@@ -18,7 +18,7 @@
 	}
 
 	request->user = p_strdup(request->pool,
-				 request->auth->set->anonymous_username);
+				 request->set->anonymous_username);
 
 	auth_request_success(request, NULL, 0);
 }
--- a/src/auth/mech-digest-md5.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/mech-digest-md5.c	Sat Mar 13 22:54:41 2010 +0200
@@ -56,7 +56,7 @@
 
 static string_t *get_digest_challenge(struct digest_auth_request *request)
 {
-	struct auth *auth = request->auth_request.auth;
+	const struct auth_settings *set = request->auth_request.set;
 	buffer_t buf;
 	string_t *str;
 	const char *const *tmp;
@@ -84,12 +84,12 @@
 	request->nonce = p_strdup(request->pool, buf.data);
 
 	str = t_str_new(256);
-	if (*auth->set->realms_arr == NULL) {
+	if (*set->realms_arr == NULL) {
 		/* If no realms are given, at least Cyrus SASL client defaults
 		   to destination host name */
 		str_append(str, "realm=\"\",");
 	} else {
-		for (tmp = auth->set->realms_arr; *tmp != NULL; tmp++)
+		for (tmp = set->realms_arr; *tmp != NULL; tmp++)
 			str_printfa(str, "realm=\"%s\",", *tmp);
 	}
 
--- a/src/auth/mech-gssapi.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/mech-gssapi.c	Sat Mar 13 22:54:41 2010 +0200
@@ -143,7 +143,7 @@
 		mech_gssapi_initialize(request->auth);
 	}
 
-	if (strcmp(request->auth->set->gssapi_hostname, "$ALL") == 0) {
+	if (strcmp(request->set->gssapi_hostname, "$ALL") == 0) {
 		auth_request_log_debug(request, "gssapi",
 				       "Using all keytab entries");
 		*ret_r = GSS_C_NO_CREDENTIAL;
@@ -161,7 +161,7 @@
 	principal_name = t_str_new(128);
 	str_append(principal_name, service_name);
 	str_append_c(principal_name, '@');
-	str_append(principal_name, request->auth->set->gssapi_hostname);
+	str_append(principal_name, request->set->gssapi_hostname);
 
 	auth_request_log_debug(request, "gssapi",
 		"Obtaining credentials for %s", str_c(principal_name));
--- a/src/auth/mech-rpa.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/mech-rpa.c	Sat Mar 13 22:54:41 2010 +0200
@@ -329,7 +329,7 @@
 static const unsigned char *
 mech_rpa_build_token2(struct rpa_auth_request *request, size_t *size)
 {
-	struct auth *auth = request->auth_request.auth;
+	const struct auth_settings *set = request->auth_request.set;
 	unsigned int realms_len, length;
 	string_t *realms;
 	buffer_t *buf;
@@ -337,13 +337,13 @@
 	const char *const *tmp;
 
 	realms = t_str_new(64);
-	for (tmp = auth->set->realms_arr; *tmp != NULL; tmp++) {
+	for (tmp = set->realms_arr; *tmp != NULL; tmp++) {
 		rpa_add_realm(realms, *tmp, request->auth_request.service);
 	}
 
 	if (str_len(realms) == 0) {
-		rpa_add_realm(realms, *auth->set->default_realm != '\0' ?
-			      auth->set->default_realm : my_hostname,
+		rpa_add_realm(realms, *set->default_realm != '\0' ?
+			      set->default_realm : my_hostname,
 			      request->auth_request.service);
 	}
 
--- a/src/auth/mech-winbind.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/mech-winbind.c	Sat Mar 13 22:54:41 2010 +0200
@@ -96,7 +96,8 @@
 }
 
 static void
-winbind_helper_connect(struct auth *auth, struct winbind_helper *winbind)
+winbind_helper_connect(const struct auth_settings *set,
+		       struct winbind_helper *winbind)
 {
 	int infd[2], outfd[2];
 	pid_t pid;
@@ -132,7 +133,7 @@
 		    dup2(infd[1], STDOUT_FILENO) < 0)
 			i_fatal("dup2() failed: %m");
 
-		args[0] = auth->set->winbind_helper_path;
+		args[0] = set->winbind_helper_path;
 		args[1] = winbind->param;
 		args[2] = NULL;
 		execv(args[0], (void *)args);
@@ -284,7 +285,7 @@
 	struct winbind_auth_request *request =
 		(struct winbind_auth_request *)auth_request;
 
-	winbind_helper_connect(auth_request->auth, request->winbind);
+	winbind_helper_connect(auth_request->set, request->winbind);
 	mech_generic_auth_initial(auth_request, data, data_size);
 }
 
--- a/src/auth/passdb-cache.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/passdb-cache.c	Sat Mar 13 22:54:41 2010 +0200
@@ -14,7 +14,7 @@
 {
 	const char *p;
 
-	if (!request->auth->set->debug_passwords &&
+	if (!request->set->debug_passwords &&
 	    *value != '\0' && *value != '\t') {
 		/* hide the password */
 		p = strchr(value, '\t');
--- a/src/auth/passdb-ldap.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/passdb-ldap.c	Sat Mar 13 22:54:41 2010 +0200
@@ -145,7 +145,7 @@
 			passdb_result = PASSDB_RESULT_OK;
 		else if (ret == LDAP_INVALID_CREDENTIALS) {
 			str = "invalid credentials";
-			if (auth_request->auth->set->debug_passwords) {
+			if (auth_request->set->debug_passwords) {
 				str = t_strconcat(str, " (given password: ",
 						  auth_request->mech_password,
 						  ")", NULL);
--- a/src/auth/passdb-pam.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/passdb-pam.c	Sat Mar 13 22:54:41 2010 +0200
@@ -174,7 +174,7 @@
 			auth_request_log_error(request, "pam", "%s", str);
 		} else if (status == PAM_AUTH_ERR) {
 			str = t_strconcat(str, " (password mismatch?)", NULL);
-			if (request->auth->set->debug_passwords) {
+			if (request->set->debug_passwords) {
 				str = t_strconcat(str, " (given password: ",
 						  request->mech_password,
 						  ")", NULL);
--- a/src/auth/passdb.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/passdb.c	Sat Mar 13 22:54:41 2010 +0200
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 
 static ARRAY_DEFINE(passdb_interfaces, struct passdb_module_interface *);
+static ARRAY_DEFINE(passdb_modules, struct passdb_module *);
 
 static struct passdb_module_interface *passdb_interface_find(const char *name)
 {
@@ -94,7 +95,7 @@
 			const char *error = t_strdup_printf(
 				"Requested %s scheme, but we have only %s",
 				wanted_scheme, input_scheme);
-			if (auth_request->auth->set->debug_passwords) {
+			if (auth_request->set->debug_passwords) {
 				error = t_strdup_printf("%s (input: %s)",
 							error, input);
 			}
@@ -112,7 +113,7 @@
 			username = t_strconcat(username, "@",
 					       auth_request->realm, NULL);
 		}
-		if (auth_request->auth->set->debug_passwords) {
+		if (auth_request->set->debug_passwords) {
 			auth_request_log_debug(auth_request, "password",
 				"Generating %s from user '%s', password '%s'",
 				wanted_scheme, username, plaintext);
@@ -154,12 +155,30 @@
 	callback(result, credentials, size, auth_request);
 }
 
+static struct passdb_module *
+passdb_find(const char *driver, const char *args, unsigned int *idx_r)
+{
+	struct passdb_module *const *passdbs;
+	unsigned int i, count;
+
+	passdbs = array_get(&passdb_modules, &count);
+	for (i = 0; i < count; i++) {
+		if (strcmp(passdbs[i]->iface.name, driver) == 0 &&
+		    strcmp(passdbs[i]->args, args) == 0) {
+			*idx_r = i;
+			return passdbs[i];
+		}
+	}
+	return NULL;
+}
+
 struct passdb_module *
 passdb_preinit(pool_t pool, const char *driver, const char *args)
 {
 	static unsigned int auth_passdb_id = 0;
 	struct passdb_module_interface *iface;
 	struct passdb_module *passdb;
+	unsigned int idx;
 
 	iface = passdb_interface_find(driver);
 	if (iface == NULL)
@@ -168,10 +187,13 @@
 		i_fatal("Support not compiled in for passdb driver '%s'",
 			driver);
 	}
-
 	if (iface->preinit == NULL && iface->init == NULL && *args != '\0')
 		i_fatal("passdb %s: No args are supported: %s", driver, args);
 
+	passdb = passdb_find(driver, args, &idx);
+	if (passdb != NULL)
+		return passdb;
+
 	if (iface->preinit == NULL)
 		passdb = p_new(pool, struct passdb_module, 1);
 	else
@@ -179,15 +201,15 @@
 	passdb->id = ++auth_passdb_id;
 	passdb->iface = *iface;
 	passdb->args = p_strdup(pool, args);
+	array_append(&passdb_modules, &passdb, 1);
 	return passdb;
 }
 
 void passdb_init(struct passdb_module *passdb)
 {
-	if (passdb->iface.init != NULL && !passdb->initialized) {
-		passdb->initialized = TRUE;
+	if (passdb->iface.init != NULL && passdb->init_refcount == 0)
 		passdb->iface.init(passdb);
-	}
+	passdb->init_refcount++;
 
 	i_assert(passdb->default_pass_scheme != NULL ||
 		 passdb->cache_key == NULL);
@@ -195,7 +217,16 @@
 
 void passdb_deinit(struct passdb_module *passdb)
 {
-	i_assert(passdb->initialized);
+	unsigned int idx;
+
+	i_assert(passdb->init_refcount > 0);
+
+	if (--passdb->init_refcount > 0)
+		return;
+
+	if (passdb_find(passdb->iface.name, passdb->args, &idx) == NULL)
+		i_unreached();
+	array_delete(&passdb_modules, idx, 1);
 
 	if (passdb->iface.deinit != NULL)
 		passdb->iface.deinit(passdb);
@@ -215,6 +246,7 @@
 void passdbs_init(void)
 {
 	i_array_init(&passdb_interfaces, 16);
+	i_array_init(&passdb_modules, 16);
 	passdb_register_module(&passdb_passwd);
 	passdb_register_module(&passdb_bsdauth);
 	passdb_register_module(&passdb_passwd_file);
@@ -229,5 +261,6 @@
 
 void passdbs_deinit(void)
 {
+	array_free(&passdb_modules);
 	array_free(&passdb_interfaces);
 }
--- a/src/auth/passdb.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/passdb.h	Sat Mar 13 22:54:41 2010 +0200
@@ -62,10 +62,10 @@
         /* id is used by blocking passdb to identify the passdb */
 	unsigned int id;
 
-	struct passdb_module_interface iface;
+	/* number of time init() has been called */
+	int init_refcount;
 
-	/* init() has been called */
-	unsigned int initialized:1;
+	struct passdb_module_interface iface;
 };
 
 /* Try to get credentials in wanted scheme (request->credentials_scheme) from
--- a/src/auth/userdb-ldap.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/userdb-ldap.c	Sat Mar 13 22:54:41 2010 +0200
@@ -193,7 +193,7 @@
 	request = &ctx->request;
 	request->ctx = ctx;
 
-	request->request.request.auth_request = auth_request_new_dummy(NULL);
+	request->request.request.auth_request = auth_request_new_dummy();
 	request->request.base = conn->set.base;
 	request->request.filter = conn->set.iterate_filter;
 	request->request.attributes = conn->iterate_attr_names;
--- a/src/auth/userdb-prefetch.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/userdb-prefetch.c	Sat Mar 13 22:54:41 2010 +0200
@@ -16,7 +16,7 @@
 	/* auth_request_set_field() should have already placed the userdb_*
 	   values to userdb_reply. */
 	if (auth_request->userdb_reply == NULL) {
-		if (auth_request->auth->userdbs->next == NULL) {
+		if (auth_request_get_auth(auth_request)->userdbs->next == NULL) {
 			/* no other userdbs */
 			if (auth_request->userdb_lookup) {
 				auth_request_log_error(auth_request, "prefetch",
@@ -26,7 +26,7 @@
 					"passdb didn't return userdb entries");
 			}
 		} else if (!auth_request->userdb_lookup ||
-			   auth_request->auth->set->debug) {
+			   auth_request->set->debug) {
 			/* more userdbs, they may know the user */
 			auth_request_log_debug(auth_request, "prefetch",
 				"passdb didn't return userdb entries, "
--- a/src/auth/userdb.c	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/userdb.c	Sat Mar 13 22:54:41 2010 +0200
@@ -10,6 +10,7 @@
 #include <grp.h>
 
 static ARRAY_DEFINE(userdb_interfaces, struct userdb_module_interface *);
+static ARRAY_DEFINE(userdb_modules, struct userdb_module *);
 
 static struct userdb_module_interface *userdb_interface_find(const char *name)
 {
@@ -106,12 +107,30 @@
 	return gr->gr_gid;
 }
 
+static struct userdb_module *
+userdb_find(const char *driver, const char *args, unsigned int *idx_r)
+{
+	struct userdb_module *const *userdbs;
+	unsigned int i, count;
+
+	userdbs = array_get(&userdb_modules, &count);
+	for (i = 0; i < count; i++) {
+		if (strcmp(userdbs[i]->iface->name, driver) == 0 &&
+		    strcmp(userdbs[i]->args, args) == 0) {
+			*idx_r = i;
+			return userdbs[i];
+		}
+	}
+	return NULL;
+}
+
 struct userdb_module *
 userdb_preinit(pool_t pool, const char *driver, const char *args)
 {
 	static unsigned int auth_userdb_id = 0;
 	struct userdb_module_interface *iface;
 	struct userdb_module *userdb;
+	unsigned int idx;
 
 	iface = userdb_interface_find(driver);
 	if (iface == NULL)
@@ -120,10 +139,13 @@
 		i_fatal("Support not compiled in for userdb driver '%s'",
 			driver);
 	}
-
 	if (iface->preinit == NULL && iface->init == NULL && *args != '\0')
 		i_fatal("userdb %s: No args are supported: %s", driver, args);
 
+	userdb = userdb_find(driver, args, &idx);
+	if (userdb != NULL)
+		return userdb;
+
 	if (iface->preinit == NULL)
 		userdb = p_new(pool, struct userdb_module, 1);
 	else
@@ -131,20 +153,30 @@
 	userdb->id = ++auth_userdb_id;
 	userdb->iface = iface;
 	userdb->args = p_strdup(pool, args);
+	array_append(&userdb_modules, &userdb, 1);
 	return userdb;
 }
 
 void userdb_init(struct userdb_module *userdb)
 {
-	if (userdb->iface->init != NULL && !userdb->initialized) {
-		userdb->initialized = TRUE;
+	if (userdb->iface->init != NULL && userdb->init_refcount == 0)
 		userdb->iface->init(userdb);
-	}
+	userdb->init_refcount++;
 }
 
 void userdb_deinit(struct userdb_module *userdb)
 {
-	i_assert(userdb->initialized);
+	unsigned int idx;
+
+	i_assert(userdb->init_refcount > 0);
+
+	if (--userdb->init_refcount > 0)
+		return;
+
+	if (userdb_find(userdb->iface->name, userdb->args, &idx) == NULL)
+		i_unreached();
+	array_delete(&userdb_modules, idx, 1);
+
 	if (userdb->iface->deinit != NULL)
 		userdb->iface->deinit(userdb);
 }
@@ -162,6 +194,7 @@
 void userdbs_init(void)
 {
 	i_array_init(&userdb_interfaces, 16);
+	i_array_init(&userdb_modules, 16);
 	userdb_register_module(&userdb_passwd);
 	userdb_register_module(&userdb_passwd_file);
 	userdb_register_module(&userdb_prefetch);
@@ -175,5 +208,6 @@
 
 void userdbs_deinit(void)
 {
+	array_free(&userdb_modules);
 	array_free(&userdb_interfaces);
 }
--- a/src/auth/userdb.h	Sat Mar 13 22:23:58 2010 +0200
+++ b/src/auth/userdb.h	Sat Mar 13 22:54:41 2010 +0200
@@ -29,10 +29,10 @@
         /* id is used by blocking userdb to identify the userdb */
 	unsigned int id;
 
-	const struct userdb_module_interface *iface;
+	/* number of time init() has been called */
+	int init_refcount;
 
-	/* init() has been called */
-	unsigned int initialized:1;
+	const struct userdb_module_interface *iface;
 };
 
 struct userdb_iterate_context {