changeset 1046:561da07883b6 HEAD

Async userdb and passdb interface.
author Timo Sirainen <tss@iki.fi>
date Mon, 27 Jan 2003 10:08:14 +0200
parents be7710b9a819
children c8adf40c8f42
files src/auth/login-connection.c src/auth/master-connection.c src/auth/mech-digest-md5.c src/auth/mech-plain.c src/auth/mech.c src/auth/mech.h src/auth/passdb-pam.c src/auth/passdb-passwd-file.c src/auth/passdb-passwd.c src/auth/passdb-shadow.c src/auth/passdb-vpopmail.c src/auth/passdb.h src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.h
diffstat 17 files changed, 235 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/login-connection.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/login-connection.c	Mon Jan 27 10:08:14 2003 +0200
@@ -209,6 +209,7 @@
 		}
 	}
 
+	//FIXME: hash_foreach(conn->auth_requests, auth_request_hash_destroy, NULL);
 	hash_destroy(conn->auth_requests);
 
 	i_stream_unref(conn->input);
--- a/src/auth/master-connection.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/master-connection.c	Mon Jan 27 10:08:14 2003 +0200
@@ -60,35 +60,12 @@
 	return reply;
 }
 
-static void master_handle_request(struct auth_master_request *request,
-				  int fd __attr_unused__)
+static void send_reply(struct auth_master_reply *reply, size_t reply_size,
+		       unsigned int tag)
 {
-	struct login_connection *login_conn;
-	struct auth_request *auth_request;
-	struct user_data *user_data;
-	struct auth_master_reply *reply;
-	size_t reply_size;
 	ssize_t ret;
 
-	login_conn = login_connection_lookup(request->login_pid);
-	auth_request = login_conn == NULL ? NULL :
-		hash_lookup(login_conn->auth_requests,
-			    POINTER_CAST(request->id));
-
-	reply_size = sizeof(*reply);
-	if (request == NULL)
-		reply = &failure_reply;
-	else {
-		user_data = userdb->lookup(auth_request->user,
-					   auth_request->realm);
-		if (user_data == NULL)
-			reply = &failure_reply;
-		else
-			reply = fill_reply(user_data, &reply_size);
-		mech_request_free(login_conn, auth_request, request->id);
-	}
-
-	reply->tag = request->tag;
+	reply->tag = tag;
 	for (;;) {
 		ret = o_stream_send(output, reply, reply_size);
 		if (ret < 0) {
@@ -110,6 +87,40 @@
 	}
 }
 
+static void userdb_callback(struct user_data *user, void *context)
+{
+	unsigned int tag = POINTER_CAST_TO(context, unsigned int);
+	struct auth_master_reply *reply;
+	size_t reply_size;
+
+	if (user == NULL)
+		send_reply(&failure_reply, sizeof(failure_reply), tag);
+	else {
+		reply = fill_reply(user, &reply_size);
+		send_reply(reply, reply_size, tag);
+	}
+}
+
+static void master_handle_request(struct auth_master_request *request,
+				  int fd __attr_unused__)
+{
+	struct login_connection *login_conn;
+	struct auth_request *auth_request;
+
+	login_conn = login_connection_lookup(request->login_pid);
+	auth_request = login_conn == NULL ? NULL :
+		hash_lookup(login_conn->auth_requests,
+			    POINTER_CAST(request->id));
+
+	if (request == NULL)
+		send_reply(&failure_reply, sizeof(failure_reply), request->tag);
+	else {
+		userdb->lookup(auth_request->user, auth_request->realm,
+			       userdb_callback, POINTER_CAST(request->tag));
+		mech_request_free(login_conn, auth_request, request->id);
+	}
+}
+
 static void master_input(void *context __attr_unused__)
 {
 	int ret;
--- a/src/auth/mech-digest-md5.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/mech-digest-md5.c	Mon Jan 27 10:08:14 2003 +0200
@@ -110,26 +110,22 @@
 	return str;
 }
 
-static int verify_auth(struct digest_auth_request *auth)
+static int verify_credentials(struct digest_auth_request *auth,
+			      const char *credentials)
 {
 	struct md5_context ctx;
 	unsigned char digest[16];
-	const char *a1_hex, *a2_hex, *response_hex, *data;
+	const char *a1_hex, *a2_hex, *response_hex;
 	buffer_t *digest_buf;
 	int i;
 
-	/* we should have taken care of this at startup */
-	i_assert(passdb->lookup_credentials != NULL);
-
 	/* get the MD5 password */
-	data = passdb->lookup_credentials(auth->username, auth->realm,
-					  PASSDB_CREDENTIALS_DIGEST_MD5);
-	if (data == NULL || strlen(data) != sizeof(digest)*2)
+	if (credentials == NULL || strlen(credentials) != sizeof(digest)*2)
 		return FALSE;
 
 	digest_buf = buffer_create_data(data_stack_pool,
 					digest, sizeof(digest));
-	if (hex_to_binary(data, digest_buf) <= 0)
+	if (hex_to_binary(credentials, digest_buf) <= 0)
 		return FALSE;
 
 	/*
@@ -487,6 +483,7 @@
 
 	t_push();
 
+	*error = NULL;
 	failed = FALSE;
 
 	copy = t_strdup_noconst(t_strndup(data, size));
@@ -518,19 +515,18 @@
 	if (auth->qop_value == NULL)
 		auth->qop_value = p_strdup(auth->pool, "auth");
 
-	if (!failed && !verify_auth(auth)) {
-		*error = NULL;
-		failed = TRUE;
-	}
-
 	t_pop();
 
-	/* error message is actually ignored here, we could send it to
-	   syslog or maybe to client, but it's not specified if that's
-	   allowed and how. */
 	return !failed;
 }
 
+static void credentials_callback(const char *result, void *context)
+{
+	struct digest_auth_request *auth = context;
+
+	mech_auth_finish(&auth->auth_request, verify_credentials(auth, result));
+}
+
 static int
 mech_digest_md5_auth_continue(struct login_connection *conn,
 			      struct auth_request *auth_request,
@@ -559,15 +555,13 @@
 
 	if (parse_digest_response(auth, (const char *) data,
 				  request->data_size, &error)) {
-		/* authentication ok */
-		auth->authenticated = TRUE;
-
-		reply.reply_idx = 0;
+		auth_request->conn = conn;
+		auth_request->id = request->id;
+		auth_request->callback = callback;
 
-		reply.result = AUTH_LOGIN_RESULT_CONTINUE;
-		reply.data_size = strlen(auth->rspauth);
-		callback(&reply, auth->rspauth, conn);
-		return TRUE;
+		passdb->lookup_credentials(auth->username, auth->realm,
+					   PASSDB_CREDENTIALS_DIGEST_MD5,
+					   credentials_callback, auth);
 	}
 
 	if (error == NULL)
--- a/src/auth/mech-plain.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/mech-plain.c	Mon Jan 27 10:08:14 2003 +0200
@@ -6,26 +6,31 @@
 #include "mech.h"
 #include "passdb.h"
 
+static void verify_callback(enum passdb_result result, void *context)
+{
+	struct auth_request *auth_request = context;
+
+	mech_auth_finish(auth_request, result == PASSDB_RESULT_OK);
+}
+
 static int
 mech_plain_auth_continue(struct login_connection *conn,
 			 struct auth_request *auth_request,
 			 struct auth_login_request_continue *request,
 			 const unsigned char *data, mech_callback_t *callback)
 {
-	struct auth_login_reply reply;
 	const char *authid, *authenid;
 	char *pass;
-	void *reply_data = NULL;
 	size_t i, count, len;
 
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	reply.result = AUTH_LOGIN_RESULT_FAILURE;
+	auth_request->conn = conn;
+	auth_request->id = request->id;
+	auth_request->callback = callback;
 
 	/* authorization ID \0 authentication ID \0 pass.
 	   we'll ignore authorization ID for now. */
 	authid = (const char *) data;
-	authenid = NULL; pass = NULL;
+	authenid = NULL; pass = "";
 
 	count = 0;
 	for (i = 0; i < request->data_size; i++) {
@@ -47,21 +52,12 @@
 	if (auth_request->realm != NULL)
                 auth_request->realm++;
 
-	if (pass != NULL) {
-		if (passdb->verify_plain(auth_request->user,
-					 auth_request->realm,
-					 pass) == PASSDB_RESULT_OK) {
-			reply_data = mech_auth_success(&reply, auth_request,
-						       NULL, 0);
-			reply.result = AUTH_LOGIN_RESULT_SUCCESS;
-		}
+	passdb->verify_plain(auth_request->user, auth_request->realm,
+			     pass, verify_callback, auth_request);
 
-		/* make sure it's cleared */
-		safe_memset(pass, 0, strlen(pass));
-	}
-
-	callback(&reply, reply_data, conn);
-	return reply.result == AUTH_LOGIN_RESULT_SUCCESS;
+	/* make sure it's cleared */
+	safe_memset(pass, 0, strlen(pass));
+	return TRUE;
 }
 
 static void
--- a/src/auth/mech.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/mech.c	Mon Jan 27 10:08:14 2003 +0200
@@ -162,6 +162,30 @@
 	return buffer_get_modifyable_data(buf, NULL);
 }
 
+void mech_auth_finish(struct auth_request *auth_request, int success)
+{
+	struct auth_login_reply reply;
+	void *reply_data;
+
+	memset(&reply, 0, sizeof(reply));
+	reply.id = auth_request->id;
+
+	if (success) {
+		reply_data = mech_auth_success(&reply, auth_request, NULL, 0);
+		reply.result = AUTH_LOGIN_RESULT_SUCCESS;
+	} else {
+		reply_data = NULL;
+		reply.result = AUTH_LOGIN_RESULT_FAILURE;
+	}
+
+	auth_request->callback(&reply, reply_data, auth_request->conn);
+
+	if (!success) {
+		mech_request_free(auth_request->conn, auth_request,
+				  auth_request->id);
+	}
+}
+
 extern struct mech_module mech_plain;
 extern struct mech_module mech_digest_md5;
 
--- a/src/auth/mech.h	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/mech.h	Mon Jan 27 10:08:14 2003 +0200
@@ -12,6 +12,10 @@
 	pool_t pool;
 	char *user, *realm;
 
+	struct login_connection *conn;
+	unsigned int id;
+	mech_callback_t *callback;
+
 	int (*auth_continue)(struct login_connection *conn,
 			     struct auth_request *auth_request,
 			     struct auth_login_request_continue *request,
@@ -49,6 +53,7 @@
 void *mech_auth_success(struct auth_login_reply *reply,
 			struct auth_request *auth_request,
 			const void *data, size_t data_size);
+void mech_auth_finish(struct auth_request *auth_request, int success);
 
 void mech_cyrus_sasl_init_lib(void);
 struct auth_request *mech_cyrus_sasl_new(struct login_connection *conn,
--- a/src/auth/passdb-pam.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb-pam.c	Mon Jan 27 10:08:14 2003 +0200
@@ -194,8 +194,9 @@
 	return PAM_SUCCESS;
 }
 
-static enum passdb_result
-pam_verify_plain(const char *user, const char *realm, const char *password)
+static void
+pam_verify_plain(const char *user, const char *realm, const char *password,
+		 verify_plain_callback_t *callback, void *context)
 {
 	pam_handle_t *pamh;
 	struct pam_userpass userpass;
@@ -217,19 +218,21 @@
 			i_info("PAM: pam_start(%s) failed: %s",
 			       user, pam_strerror(pamh, status));
 		}
-		return PASSDB_RESULT_INTERNAL_FAILURE;
+		callback(PASSDB_RESULT_INTERNAL_FAILURE, context);
+		return;
 	}
 
 	status = pam_auth(pamh, user);
 	if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) {
 		i_error("pam_end(%s) failed: %s",
 			user, pam_strerror(pamh, status2));
-		return PASSDB_RESULT_INTERNAL_FAILURE;
+		callback(PASSDB_RESULT_INTERNAL_FAILURE, context);
+		return;
 	}
 
 	/* FIXME: check for PASSDB_RESULT_UNKNOWN_USER somehow */
-	return status == PAM_SUCCESS ? PASSDB_RESULT_OK :
-		PASSDB_RESULT_PASSWORD_MISMATCH;
+	callback(status == PAM_SUCCESS ? PASSDB_RESULT_OK :
+		 PASSDB_RESULT_PASSWORD_MISMATCH, context);
 }
 
 static void pam_init(const char *args)
--- a/src/auth/passdb-passwd-file.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb-passwd-file.c	Mon Jan 27 10:08:14 2003 +0200
@@ -15,44 +15,55 @@
 
 struct passwd_file *passdb_pwf = NULL;
 
-static enum passdb_result
+static void
 passwd_file_verify_plain(const char *user, const char *realm,
-			 const char *password)
+			 const char *password,
+			 verify_plain_callback_t *callback, void *context)
 {
 	struct passwd_user *pu;
 	unsigned char digest[16];
 	const char *str;
 
 	pu = passwd_file_lookup_user(passdb_pwf, user, realm);
-	if (pu == NULL)
-		return PASSDB_RESULT_USER_UNKNOWN;
+	if (pu == NULL) {
+		callback(PASSDB_RESULT_USER_UNKNOWN, context);
+		return;
+	}
 
 	switch (pu->password_type) {
 	case PASSWORD_NONE:
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 
 	case PASSWORD_DES:
-		if (strcmp(mycrypt(password, pu->password), pu->password) == 0)
-			return PASSDB_RESULT_OK;
+		if (strcmp(mycrypt(password, pu->password),
+			   pu->password) == 0) {
+			callback(PASSDB_RESULT_OK, context);
+			return;
+		}
 
 		if (verbose) {
 			i_info("passwd-file(%s): DES password mismatch",
 			       pu->user_realm);
 		}
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 
 	case PASSWORD_MD5:
 		md5_get_digest(password, strlen(password), digest);
 		str = binary_to_hex(digest, sizeof(digest));
 
-		if (strcmp(str, pu->password) == 0)
-			return PASSDB_RESULT_OK;
+		if (strcmp(str, pu->password) == 0) {
+			callback(PASSDB_RESULT_OK, context);
+			return;
+		}
 
 		if (verbose) {
 			i_info("passwd-file(%s): MD5 password mismatch",
 			       pu->user_realm);
 		}
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 
 	case PASSWORD_DIGEST_MD5:
 		/* user:realm:passwd */
@@ -63,51 +74,64 @@
 		md5_get_digest(str, strlen(str), digest);
 		str = binary_to_hex(digest, sizeof(digest));
 
-		if (strcmp(str, pu->password) == 0)
-			return PASSDB_RESULT_OK;
+		if (strcmp(str, pu->password) == 0) {
+			callback(PASSDB_RESULT_OK, context);
+			return;
+		}
 
 		if (verbose) {
 			i_info("passwd-file(%s): DIGEST-MD5 password mismatch",
 			       pu->user_realm);
 		}
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 	}
 
 	i_unreached();
 }
 
-static const char *
+static void
 passwd_file_lookup_credentials(const char *user, const char *realm,
-			       enum passdb_credentials credentials)
+			       enum passdb_credentials credentials,
+			       lookup_credentials_callback_t *callback,
+			       void *context)
 {
 	struct passwd_user *pu;
 
 	pu = passwd_file_lookup_user(passdb_pwf, user, realm);
-	if (pu == NULL)
-		return NULL;
+	if (pu == NULL) {
+		callback(NULL, context);
+		return;
+	}
 
 	if (pu->password_type == PASSWORD_NONE) {
 		if (verbose)
 			i_info("passwd-file(%s): No password", pu->user_realm);
-		return NULL;
+		callback(NULL, context);
+		return;
 	}
 
 	switch (credentials) {
 	case PASSDB_CREDENTIALS_DIGEST_MD5:
-		if (pu->password_type == PASSWORD_DIGEST_MD5)
-			return pu->password;
+		if (pu->password_type == PASSWORD_DIGEST_MD5) {
+			callback(pu->password, context);
+			return;
+		}
 
 		if (verbose) {
 			i_info("passwd-file(%s): No DIGEST-MD5 password",
 			       pu->user_realm);
 		}
-		return NULL;
+		callback(NULL, context);
+		return;
 	default:
 		if (verbose) {
 			i_info("passwd-file(%s): Unsupported credentials %u",
 			       pu->user_realm, (unsigned int)credentials);
 		}
-		return NULL;
+		callback(NULL, context);
+		return;
 	}
 }
 
--- a/src/auth/passdb-passwd.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb-passwd.c	Mon Jan 27 10:08:14 2003 +0200
@@ -12,8 +12,9 @@
 
 #include <pwd.h>
 
-static enum passdb_result
-passwd_verify_plain(const char *user, const char *realm, const char *password)
+static void
+passwd_verify_plain(const char *user, const char *realm, const char *password,
+		    verify_plain_callback_t *callback, void *context)
 {
 	struct passwd *pw;
 	int result;
@@ -26,7 +27,8 @@
 			i_error("getpwnam(%s) failed: %m", user);
 		else if (verbose)
 			i_info("passwd(%s): unknown user", user);
-		return PASSDB_RESULT_USER_UNKNOWN;
+		callback(PASSDB_RESULT_USER_UNKNOWN, context);
+		return;
 	}
 
 	if (!IS_VALID_PASSWD(pw->pw_passwd)) {
@@ -34,7 +36,8 @@
 			i_info("passwd(%s): invalid password field '%s'",
 			       user, pw->pw_passwd);
 		}
-		return PASSDB_RESULT_USER_DISABLED;
+		callback(PASSDB_RESULT_USER_DISABLED, context);
+		return;
 	}
 
 	/* check if the password is valid */
@@ -46,10 +49,11 @@
 	if (!result) {
 		if (verbose)
 			i_info("passwd(%s): password mismatch", user);
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 	}
 
-	return PASSDB_RESULT_OK;
+	callback(PASSDB_RESULT_OK, context);
 }
 
 static void passwd_deinit(void)
--- a/src/auth/passdb-shadow.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb-shadow.c	Mon Jan 27 10:08:14 2003 +0200
@@ -12,8 +12,9 @@
 
 #include <shadow.h>
 
-static enum passdb_result
-shadow_verify_plain(const char *user, const char *realm, const char *password)
+static void
+shadow_verify_plain(const char *user, const char *realm, const char *password,
+		    verify_plain_callback_t *callback, void *context)
 {
 	struct spwd *spw;
 	int result;
@@ -26,7 +27,8 @@
 			i_error("getspnam(%s) failed: %m", user);
 		else if (verbose)
 			i_info("shadow(%s): unknown user", user);
-		return PASSDB_RESULT_USER_UNKNOWN;
+		callback(PASSDB_RESULT_USER_UNKNOWN, context);
+		return;
 	}
 
 	if (!IS_VALID_PASSWD(spw->sp_pwdp)) {
@@ -34,7 +36,8 @@
 			i_info("shadow(%s): invalid password field '%s'",
 			       user, spw->sp_pwdp);
 		}
-		return PASSDB_RESULT_USER_DISABLED;
+		callback(PASSDB_RESULT_USER_DISABLED, context);
+		return;
 	}
 
 	/* check if the password is valid */
@@ -46,10 +49,11 @@
 	if (!result) {
 		if (verbose)
 			i_info("shadow(%s): password mismatch", user);
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 	}
 
-	return PASSDB_RESULT_OK;
+	callback(PASSDB_RESULT_OK, context);
 }
 
 static void shadow_deinit(void)
--- a/src/auth/passdb-vpopmail.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb-vpopmail.c	Mon Jan 27 10:08:14 2003 +0200
@@ -14,21 +14,25 @@
 
 #include "userdb-vpopmail.h"
 
-static enum passdb_result
-vpopmail_verify_plain(const char *user, const char *realm, const char *password)
+static void
+vpopmail_verify_plain(const char *user, const char *realm, const char *password,
+		      verify_plain_callback_t *callback, void *context)
 {
 	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
 	struct vqpasswd *vpw;
 	int result;
 
 	vpw = vpopmail_lookup_vqp(user, realm, vpop_user, vpop_domain);
-	if (vpw == NULL)
-		return PASSDB_RESULT_USER_UNKNOWN;
+	if (vpw == NULL) {
+		callback(PASSDB_RESULT_USER_UNKNOWN, context);
+		return;
+	}
 
 	if ((vpw->pw_gid & NO_IMAP) != 0) {
 		if (verbose)
 			i_info("vpopmail(%s): IMAP disabled", user);
-		return PASSDB_RESULT_USER_DISABLED;
+		callback(PASSDB_RESULT_USER_DISABLED, context);
+		return;
 	}
 
 	/* verify password */
@@ -38,10 +42,11 @@
 	if (!result) {
 		if (verbose)
 			i_info("vpopmail(%s): password mismatch", user);
-		return PASSDB_RESULT_PASSWORD_MISMATCH;
+		callback(PASSDB_RESULT_PASSWORD_MISMATCH, context);
+		return;
 	}
 
-	return PASSDB_RESULT_OK;
+	callback(PASSDB_RESULT_OK, context);
 }
 
 static void vpopmail_deinit(void)
--- a/src/auth/passdb.h	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/passdb.h	Mon Jan 27 10:08:14 2003 +0200
@@ -18,18 +18,24 @@
 	PASSDB_RESULT_OK = 1,
 };
 
+typedef void verify_plain_callback_t(enum passdb_result result, void *context);
+typedef void lookup_credentials_callback_t(const char *result, void *context);
+
 struct passdb_module {
 	void (*init)(const char *args);
 	void (*deinit)(void);
 
 	/* Check if plaintext password matches */
-	enum passdb_result (*verify_plain)(const char *user, const char *realm,
-					   const char *password);
+	void (*verify_plain)(const char *user, const char *realm,
+			     const char *password,
+			     verify_plain_callback_t *callback, void *context);
 
 	/* Return authentication credentials. Type is authentication mechanism
 	   specific value that is requested. */
-	const char *(*lookup_credentials)(const char *user, const char *realm,
-					  enum passdb_credentials credentials);
+	void (*lookup_credentials)(const char *user, const char *realm,
+				   enum passdb_credentials credentials,
+				   lookup_credentials_callback_t *callback,
+				   void *context);
 };
 
 const char *passdb_credentials_to_str(enum passdb_credentials credentials);
--- a/src/auth/userdb-passwd-file.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/userdb-passwd-file.c	Mon Jan 27 10:08:14 2003 +0200
@@ -11,15 +11,18 @@
 
 struct passwd_file *userdb_pwf = NULL;
 
-static struct user_data *passwd_file_lookup(const char *user, const char *realm)
+static void passwd_file_lookup(const char *user, const char *realm,
+			       userdb_callback_t *callback, void *context)
 {
 	struct user_data *data;
 	struct passwd_user *pu;
 	pool_t pool;
 
 	pu = passwd_file_lookup_user(userdb_pwf, user, realm);
-	if (pu == NULL)
-		return NULL;
+	if (pu == NULL) {
+		callback(NULL, context);
+		return;
+	}
 
 	pool = pool_alloconly_create("user_data", 512);
 	data = p_new(pool, struct user_data, 1);
@@ -34,7 +37,8 @@
 	data->mail = p_strdup(data->pool, pu->mail);
 
 	data->chroot = pu->chroot;
-	return data;
+
+	callback(data, context);
 }
 
 static void passwd_file_init(const char *args)
--- a/src/auth/userdb-passwd.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/userdb-passwd.c	Mon Jan 27 10:08:14 2003 +0200
@@ -10,7 +10,8 @@
 
 #include <pwd.h>
 
-static struct user_data *passwd_lookup(const char *user, const char *realm)
+static void passwd_lookup(const char *user, const char *realm,
+			  userdb_callback_t *callback, void *context)
 {
 	struct user_data *data;
 	struct passwd *pw;
@@ -24,7 +25,8 @@
 			i_error("getpwnam(%s) failed: %m", user);
 		else if (verbose)
 			i_info("passwd(%s): unknown user", user);
-		return NULL;
+		callback(NULL, context);
+		return;
 	}
 
 	pool = pool_alloconly_create("user_data", 512);
@@ -38,7 +40,7 @@
 	data->virtual_user = data->system_user;
 	data->home = p_strdup(data->pool, pw->pw_dir);
 
-	return data;
+	callback(data, context);
 }
 
 struct userdb_module userdb_passwd = {
--- a/src/auth/userdb-static.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/userdb-static.c	Mon Jan 27 10:08:14 2003 +0200
@@ -16,7 +16,8 @@
 static gid_t static_gid;
 static char *static_home_template;
 
-static struct user_data *static_lookup(const char *user, const char *realm)
+static void static_lookup(const char *user, const char *realm,
+			  userdb_callback_t *callback, void *context)
 {
 	struct user_data *data;
 	pool_t pool;
@@ -39,7 +40,7 @@
 	var_expand(str, static_home_template, user, NULL);
 	data->home = p_strdup(data->pool, str_c(str));
 
-	return data;
+	callback(data, context);
 }
 
 static void static_init(const char *args)
--- a/src/auth/userdb-vpopmail.c	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/userdb-vpopmail.c	Mon Jan 27 10:08:14 2003 +0200
@@ -50,7 +50,8 @@
 
 #ifdef USERDB_VPOPMAIL
 
-static struct user_data *vpopmail_lookup(const char *user, const char *realm)
+static void vpopmail_lookup(const char *user, const char *realm,
+			    userdb_callback_t *callback, void *context)
 {
 	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
 	struct vqpasswd *vpw;
@@ -63,8 +64,10 @@
 		user = t_strconcat(user, "@", realm, NULL);
 
 	vpw = vpopmail_lookup_vqp(user, realm, vpop_user, vpop_domain);
-	if (vpw == NULL)
-		return NULL;
+	if (vpw == NULL) {
+		callback(NULL, context);
+		return;
+	}
 
 	/* we have to get uid/gid separately, because the gid field in
 	   struct vqpasswd isn't really gid at all but just some flags... */
@@ -73,7 +76,8 @@
 			i_info("vpopmail(%s): vget_assign(%s) failed",
 			       user, vpop_domain);
 		}
-		return NULL;
+		callback(NULL, context);
+		return;
 	}
 
 	if (vpw->pw_dir == NULL || vpw->pw_dir[0] == '\0') {
@@ -86,13 +90,16 @@
 		if (make_user_dir(vpop_user, vpop_domain, uid, gid) == NULL) {
 			i_error("vpopmail(%s): make_user_dir(%s, %s) failed",
 				user, vpop_user, vpop_domain);
-			return NULL;
+			callback(NULL, context);
+			return;
 		}
 
 		/* get the user again so pw_dir is visible */
 		vpw = vauth_getpw(vpop_user, vpop_domain);
-		if (vpw == NULL)
-			return NULL;
+		if (vpw == NULL) {
+			callback(NULL, context);
+			return;
+		}
 	}
 
 	pool = pool_alloconly_create("user_data", 1024);
@@ -105,7 +112,7 @@
 	data->virtual_user = p_strdup(data->pool, vpw->pw_name);
 	data->home = p_strdup(data->pool, vpw->pw_dir);
 
-	return data;
+	callback(data, context);
 }
 
 struct userdb_module userdb_vpopmail = {
--- a/src/auth/userdb.h	Mon Jan 27 09:01:20 2003 +0200
+++ b/src/auth/userdb.h	Mon Jan 27 10:08:14 2003 +0200
@@ -15,11 +15,14 @@
 	int chroot; /* chroot to home directory */
 };
 
+typedef void userdb_callback_t(struct user_data *user, void *context);
+
 struct userdb_module {
 	void (*init)(const char *args);
 	void (*deinit)(void);
 
-	struct user_data *(*lookup)(const char *user, const char *realm);
+	void (*lookup)(const char *user, const char *realm,
+		       userdb_callback_t *callback, void *context);
 };
 
 extern struct userdb_module *userdb;