changeset 1035:fe49ece0f3ea HEAD

We have now separate "userdb" and "passdb". They aren't tied to each others in any way, so it's possible to use whatever user database with whatever password database. Added "static" userdb, which uses same uid/gid for everyone and generates home directory from given template. This could be useful with PAM, although insecure since everyone uses same uid. Not too well tested, and userdb/passdb API still needs to be changed to asynchronous for sql/ldap/etc lookups.
author Timo Sirainen <tss@iki.fi>
date Mon, 27 Jan 2003 03:33:40 +0200
parents c077254cbe66
children f782b3319553
files dovecot-example.conf src/auth/Makefile.am src/auth/auth-cyrus-sasl2.c src/auth/auth-digest-md5.c src/auth/auth-interface.h src/auth/auth-login-interface.h src/auth/auth-master-interface.h src/auth/auth-plain.c src/auth/auth.c src/auth/auth.h src/auth/common.h src/auth/cookie.c src/auth/cookie.h src/auth/login-connection.c src/auth/login-connection.h src/auth/main.c src/auth/master-connection.c src/auth/master-connection.h src/auth/master.c src/auth/master.h src/auth/mech-cyrus-sasl2.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.c src/auth/passdb.h src/auth/passwd-file.c src/auth/passwd-file.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-vpopmail.h src/auth/userdb.c src/auth/userdb.h src/auth/userinfo-pam.c src/auth/userinfo-passwd-file.c src/auth/userinfo-passwd.c src/auth/userinfo-passwd.h src/auth/userinfo-shadow.c src/auth/userinfo-vpopmail.c src/auth/userinfo.c src/auth/userinfo.h src/imap/main.c src/login/auth-connection.c src/login/auth-connection.h src/login/client-authenticate.c src/login/client.h src/login/common.h src/login/master.c src/login/master.h src/master/Makefile.am src/master/auth-process.c src/master/auth-process.h src/master/common.h src/master/imap-process.c src/master/imap-process.h src/master/login-process.c src/master/login-process.h src/master/main.c src/master/master-interface.h src/master/master-login-interface.h src/master/settings.c src/master/settings.h
diffstat 70 files changed, 3506 insertions(+), 3317 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Mon Jan 27 03:27:51 2003 +0200
+++ b/dovecot-example.conf	Mon Jan 27 03:33:40 2003 +0200
@@ -271,9 +271,9 @@
 # Authentication process name.
 auth = default
 
-# Authentication methods this process allows separated with a space.
-# Currently supported: plain digest-md5
-auth_methods = plain
+# Space separated list of wanted authentication mechanisms:
+#   plain digest-md5
+auth_mechanisms = plain
 
 # Space separated list of realms with authentication methods that need them.
 # This is usually empty or the host name of the server (eg.
@@ -284,14 +284,20 @@
 #    need to add more realms, add them to end of the list.
 #auth_realms =
 
-# Where the user information and passwords are stored into:
+# Where user database is kept:
+#   passwd: /etc/passwd or similiar, using getpwnam()
+#   passwd-file <path>: passwd-like file with specified location
+#   static uid=<uid> gid=<gid> home=<dir template>: static settings
+#   vpopmail: vpopmail library
+auth_userdb = passwd
+
+# Where password database is kept:
 #   passwd: /etc/passwd or similiar, using getpwnam()
 #   shadow: /etc/shadow or similiar, using getspnam()
 #   pam: PAM authentication
-#   passwd-file /etc/passwd.imap: /etc/passwd-like file. Supports digest-md5
-#                                 style passwords
+#   passwd-file <path>: passwd-like file with specified location
 #   vpopmail: vpopmail authentication
-auth_userinfo = pam
+auth_passdb = pam
 
 # Executable location
 #auth_executable = /usr/libexec/dovecot/imap-auth
@@ -322,7 +328,8 @@
 #auth = digest_md5
 #auth_methods = digest-md5
 #auth_realms = 
-#auth_userinfo = passwd-file /etc/passwd.imap
+#auth_userdb = passwd-file /etc/passwd.imap
+#auth_passdb = passwd-file /etc/passwd.imap
 #auth_user = imapauth
 #auth_chroot = /var/run/dovecot/auth
 
--- a/src/auth/Makefile.am	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/auth/Makefile.am	Mon Jan 27 03:33:40 2003 +0200
@@ -8,35 +8,42 @@
 
 imap_auth_LDADD = \
 	../lib/liblib.a \
-	$(USERINFO_LIBS) \
+	$(PASSDB_LIBS) \
 	$(SASL_LIBS) \
 	$(VPOPMAIL_LIBS)
 
 imap_auth_SOURCES = \
-	auth.c \
-	auth-cyrus-sasl2.c \
-	auth-plain.c \
-	auth-digest-md5.c \
-	cookie.c \
 	login-connection.c \
 	main.c \
-	master.c \
+	master-connection.c \
+	mech.c \
+	mech-cyrus-sasl2.c \
+	mech-plain.c \
+	mech-digest-md5.c \
 	mycrypt.c \
-	userinfo.c \
-	userinfo-passwd.c \
-	userinfo-shadow.c \
-	userinfo-pam.c \
-	userinfo-passwd-file.c \
-	userinfo-vpopmail.c
+	passdb.c \
+	passdb-passwd.c \
+	passdb-passwd-file.c \
+	passdb-pam.c \
+	passdb-shadow.c \
+	passdb-vpopmail.c \
+	passwd-file.c \
+	userdb.c \
+	userdb-passwd.c \
+	userdb-passwd-file.c \
+	userdb-static.c \
+	userdb-vpopmail.c
 
 noinst_HEADERS = \
-	auth.h \
+	auth-login-interface.h \
+	auth-master-interface.h \
 	auth-mech-desc.h \
-	auth-interface.h \
 	common.h \
-	cookie.h \
 	login-connection.h \
-	master.h \
+	master-connection.h \
+	mech.h \
 	mycrypt.h \
-	userinfo.h \
-	userinfo-passwd.h
+	passdb.h \
+	passwd-file.h \
+	userdb.h \
+	userdb-vpopmail.h
--- a/src/auth/auth-cyrus-sasl2.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
-/* Copyright (C) 2003 Timo Sirainen */
-
-#include "common.h"
-#include "auth.h"
-#include "cookie.h"
-
-#ifdef USE_CYRUS_SASL2
-
-#include <stdlib.h>
-#include <sasl/sasl.h>
-
-#include "auth-mech-desc.h"
-
-struct auth_context {
-	sasl_conn_t *conn;
-	int success;
-};
-
-static const char *auth_mech_to_str(enum auth_mech mech)
-{
-	int i;
-
-	for (i = 0; i < AUTH_MECH_COUNT; i++) {
-		if (auth_mech_desc[i].mech == mech)
-			return auth_mech_desc[i].name;
-	}
-
-	return NULL;
-}
-
-static void auth_sasl_continue(struct cookie_data *cookie,
-			       struct auth_continued_request_data *request,
-			       const unsigned char *data,
-			       auth_callback_t callback, void *context)
-{
-	struct auth_context *ctx = cookie->context;
-	struct auth_reply_data reply;
-	const char *serverout;
-	unsigned int serveroutlen;
-	int ret;
-
-	ret = sasl_server_step(ctx->conn, data, request->data_size,
-			       &serverout, &serveroutlen);
-
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-
-	if (ret == SASL_CONTINUE) {
-		reply.result = AUTH_RESULT_CONTINUE;
-	} else if (ret == SASL_OK) {
-		/* success */
-		reply.result = AUTH_RESULT_SUCCESS;
-		ctx->success = TRUE;
-	} else {
-		/* failure */
-		reply.result = AUTH_RESULT_FAILURE;
-		cookie_remove(cookie->cookie);
-	}
-
-	reply.data_size = serveroutlen;
-        callback(&reply, serverout, context);
-}
-
-static int auth_sasl_fill_reply(struct cookie_data *cookie,
-				struct auth_cookie_reply_data *reply)
-{
-	struct auth_context *ctx = cookie->context;
-	const char *canon_user;
-        const struct propval *prop;
-	int ret;
-
-	if (!ctx->success)
-		return FALSE;
-
-	/* get our username */
-	ret = sasl_getprop(ctx->conn, SASL_USERNAME,
-			   (const void **) &canon_user);
-	if (ret != SASL_OK) {
-		i_warning("sasl_getprop() failed: %s",
-			  sasl_errstring(ret, NULL, NULL));
-		return FALSE;
-	}
-
-	memset(reply, 0, sizeof(*reply));
-	reply->success = TRUE;
-
-	if (strocpy(reply->virtual_user, canon_user,
-		    sizeof(reply->virtual_user)) < 0)
-		i_panic("virtual_user overflow");
-
-	/* get other properties */
-	prop = prop_get(sasl_auxprop_getctx(ctx->conn));
-	for (; prop != NULL && prop->name != NULL; prop++) {
-		if (prop->nvalues == 0 || prop->values[0] == NULL)
-			continue;
-
-		if (strcasecmp(prop->name, SASL_AUX_UIDNUM) == 0)
-			reply->uid = atoi(prop->values[0]);
-		else if (strcasecmp(prop->name, SASL_AUX_GIDNUM) == 0)
-			reply->gid = atoi(prop->values[0]);
-		else if (strcasecmp(prop->name, SASL_AUX_HOMEDIR) == 0) {
-			if (strocpy(reply->home, prop->values[0],
-				    sizeof(reply->home)) < 0)
-				i_panic("home overflow");
-		} else if (strcasecmp(prop->name, SASL_AUX_UNIXMBX) == 0) {
-			if (strocpy(reply->mail, prop->values[0],
-				    sizeof(reply->mail)) < 0)
-				i_panic("mail overflow");
-		}
-	}
-
-	return TRUE;
-}
-
-static void auth_sasl_free(struct cookie_data *cookie)
-{
-	struct auth_context *ctx = cookie->context;
-
-	sasl_dispose(&ctx->conn);
-	i_free(ctx);
-	i_free(cookie);
-}
-
-void auth_cyrus_sasl_init(unsigned int login_pid,
-			  struct auth_init_request_data *request,
-			  auth_callback_t callback, void *context)
-{
-	static const char *propnames[] = {
-		SASL_AUX_UIDNUM,
-		SASL_AUX_GIDNUM,
-		SASL_AUX_HOMEDIR,
-		SASL_AUX_UNIXMBX,
-		NULL
-	};
-	struct cookie_data *cookie;
-	struct auth_reply_data reply;
-	struct auth_context *ctx;
-	const char *mech, *serverout;
-	unsigned int serveroutlen;
-	sasl_security_properties_t sec_props;
-	sasl_conn_t *conn;
-	int ret;
-
-	mech = auth_mech_to_str(request->mech);
-	if (mech == NULL)
-		i_fatal("Login asked for unknown mechanism %d", request->mech);
-
-	/* create new SASL connection */
-	ret = sasl_server_new("imap", NULL, NULL, NULL, NULL, NULL, 0, &conn);
-	if (ret != SASL_OK) {
-		i_fatal("sasl_server_new() failed: %s",
-			sasl_errstring(ret, NULL, NULL));
-	}
-
-	/* don't allow SASL security layer */
-	memset(&sec_props, 0, sizeof(sec_props));
-	sec_props.min_ssf = 0;
-	sec_props.max_ssf = 1;
-
-	if (sasl_setprop(conn, SASL_SEC_PROPS, &sec_props) != SASL_OK) {
-		i_fatal("sasl_setprop(SASL_SEC_PROPS) failed: %s",
-			sasl_errstring(ret, NULL, NULL));
-	}
-
-	ret = sasl_auxprop_request(conn, propnames);
-	if (ret != SASL_OK) {
-		i_fatal("sasl_auxprop_request() failed: %s",
-			sasl_errstring(ret, NULL, NULL));
-	}
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-
-	/* start the exchange */
-	ret = sasl_server_start(conn, mech, NULL, 0, &serverout, &serveroutlen);
-	if (ret != SASL_CONTINUE) {
-		reply.result = AUTH_RESULT_FAILURE;
-		serverout = NULL;
-		serveroutlen = 0;
-		sasl_dispose(&conn);
-	} else {
-		cookie = i_new(struct cookie_data, 1);
-		cookie->login_pid = login_pid;
-		cookie->auth_fill_reply = auth_sasl_fill_reply;
-		cookie->auth_continue = auth_sasl_continue;
-		cookie->free = auth_sasl_free;
-		ctx = cookie->context = i_new(struct auth_context, 1);
-		ctx->conn = conn;
-
-		cookie_add(cookie);
-
-		reply.result = AUTH_RESULT_CONTINUE;
-		memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-	}
-
-	reply.data_size = serveroutlen;
-	callback(&reply, serverout, context);
-}
-
-static int sasl_log(void *context __attr_unused__,
-		    int level, const char *message)
-{
-	switch (level) {
-	case SASL_LOG_ERR:
-		i_error("SASL authentication error: %s", message);
-		break;
-	case SASL_LOG_WARN:
-		i_warning("SASL authentication warning: %s", message);
-		break;
-	case SASL_LOG_NOTE:
-		/*i_info("SASL authentication info: %s", message);*/
-		break;
-	case SASL_LOG_FAIL:
-		/*i_info("SASL authentication failure: %s", message);*/
-		break;
-	}
-
-	return SASL_OK;
-}
-
-static const struct sasl_callback sasl_callbacks[] = {
-	{ SASL_CB_LOG, &sasl_log, NULL },
-	{ SASL_CB_LIST_END, NULL, NULL }
-};
-
-void auth_cyrus_sasl_init_lib(void)
-{
-	int ret;
-
-	ret = sasl_server_init(sasl_callbacks, "imap-auth");
-	if (ret != SASL_OK) {
-		i_fatal("sasl_server_init() failed: %s",
-			sasl_errstring(ret, NULL, NULL));
-	}
-}
-
-#endif
--- a/src/auth/auth-digest-md5.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,639 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-/* Digest-MD5 SASL authentication, see rfc-2831 */
-
-#include "common.h"
-#include "base64.h"
-#include "buffer.h"
-#include "hex-binary.h"
-#include "md5.h"
-#include "randgen.h"
-#include "str.h"
-
-#include "auth.h"
-#include "cookie.h"
-#include "userinfo.h"
-
-#include <stdlib.h>
-
-#define SERVICE_TYPE "imap"
-
-/* Linear whitespace */
-#define IS_LWS(c) ((c) == ' ' || (c) == '\t')
-
-enum qop_option {
-	QOP_AUTH	= 0x01,	/* authenticate */
-	QOP_AUTH_INT	= 0x02, /* + integrity protection, not supported yet */
-	QOP_AUTH_CONF	= 0x04, /* + encryption, not supported yet */
-
-	QOP_COUNT	= 3
-};
-
-static const char *qop_names[] = { "auth", "auth-int", "auth-conf" };
-
-struct auth_data {
-	pool_t pool;
-	unsigned int authenticated:1;
-
-	/* requested: */
-	char *nonce;
-	enum qop_option qop;
-
-	/* received: */
-	char *realm; /* may be NULL */
-	char *username;
-	char *cnonce;
-	char *nonce_count;
-	char *qop_value;
-	char *digest_uri; /* may be NULL */
-	unsigned char response[32];
-	unsigned long maxbuf;
-	unsigned int nonce_found:1;
-
-	/* final reply: */
-	char *rspauth;
-        struct auth_cookie_reply_data cookie_reply;
-};
-
-static const char *get_digest_challenge(struct auth_data *auth)
-{
-	string_t *qoplist, *realms;
-	buffer_t *buf;
-	const char *const *tmp;
-	unsigned char nonce[16];
-	int i;
-
-	/*
-	   realm="hostname" (multiple allowed)
-	   nonce="randomized data, at least 64bit"
-	   qop="auth,auth-int,auth-conf"
-	   maxbuf=number (with auth-int, auth-conf, defaults to 64k)
-	   charset="utf-8" (iso-8859-1 if it doesn't exist)
-	   algorithm="md5-sess"
-	   cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf)
-	*/
-
-	/* get 128bit of random data as nonce */
-	random_fill(nonce, sizeof(nonce));
-
-	t_push();
-	buf = buffer_create_static(data_stack_pool,
-				   MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1);
-
-	base64_encode(nonce, sizeof(nonce), buf);
-	buffer_append_c(buf, '\0');
-	auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL));
-	t_pop();
-
-	/* get list of allowed QoPs */
-	qoplist = t_str_new(32);
-	for (i = 0; i < QOP_COUNT; i++) {
-		if (auth->qop & (1 << i)) {
-			if (str_len(qoplist) > 0)
-				str_append_c(qoplist, ',');
-			str_append(qoplist, qop_names[i]);
-		}
-	}
-
-	realms = t_str_new(128);
-	for (tmp = auth_realms; *tmp != NULL; tmp++) {
-		str_printfa(realms, "realm=\"%s\"", *tmp);
-		str_append_c(realms, ',');
-	}
-
-	return t_strconcat(str_c(realms),
-			   "nonce=\"", auth->nonce, "\",",
-			   "qop=\"", str_c(qoplist), "\",",
-			   "charset=\"utf-8\",",
-			   "algorithm=\"md5-sess\"",
-			   NULL);
-}
-
-static int verify_auth(struct auth_data *auth)
-{
-	struct md5_context ctx;
-	unsigned char digest[16];
-	const char *a1_hex, *a2_hex, *response_hex;
-	int i;
-
-	/* we should have taken care of this at startup */
-	i_assert(userinfo->lookup_digest_md5 != NULL);
-
-	/* get the MD5 password */
-	if (!userinfo->lookup_digest_md5(auth->username, auth->realm != NULL ?
-					 auth->realm : "", digest,
-					 &auth->cookie_reply))
-		return FALSE;
-
-	/*
-	   response =
-	     HEX( KD ( HEX(H(A1)),
-		     { nonce-value, ":" nc-value, ":",
-		       cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
-
-	   and since we don't support authzid yet:
-
-	   A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
-		":", nonce-value, ":", cnonce-value }
-
-	   If the "qop" directive's value is "auth", then A2 is:
-	
-	      A2       = { "AUTHENTICATE:", digest-uri-value }
-	
-	   If the "qop" value is "auth-int" or "auth-conf" then A2 is:
-	
-	      A2       = { "AUTHENTICATE:", digest-uri-value,
-		       ":00000000000000000000000000000000" }
-	*/
-
-	/* A1 */
-	md5_init(&ctx);
-	md5_update(&ctx, digest, 16);
-	md5_update(&ctx, ":", 1);
-	md5_update(&ctx, auth->nonce, strlen(auth->nonce));
-	md5_update(&ctx, ":", 1);
-	md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
-	md5_final(&ctx, digest);
-	a1_hex = binary_to_hex(digest, 16);
-
-	/* do it twice, first verify the user's response, the second is
-	   sent for client as a reply */
-	for (i = 0; i < 2; i++) {
-		/* A2 */
-		md5_init(&ctx);
-		if (i == 0)
-			md5_update(&ctx, "AUTHENTICATE:", 13);
-		else
-			md5_update(&ctx, ":", 1);
-
-		if (auth->digest_uri != NULL) {
-			md5_update(&ctx, auth->digest_uri,
-				   strlen(auth->digest_uri));
-		}
-		if (auth->qop == QOP_AUTH_INT || auth->qop == QOP_AUTH_CONF) {
-			md5_update(&ctx, ":00000000000000000000000000000000",
-				   33);
-		}
-		md5_final(&ctx, digest);
-		a2_hex = binary_to_hex(digest, 16);
-
-		/* response */
-		md5_init(&ctx);
-		md5_update(&ctx, a1_hex, 32);
-		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->nonce, strlen(auth->nonce));
-		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->nonce_count, strlen(auth->nonce_count));
-		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
-		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->qop_value, strlen(auth->qop_value));
-		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, a2_hex, 32);
-		md5_final(&ctx, digest);
-		response_hex = binary_to_hex(digest, 16);
-
-		if (i == 0) {
-			/* verify response */
-			if (memcmp(response_hex, auth->response, 32) != 0) {
-				if (verbose) {
-					i_info("digest-md5(%s): "
-					       "password mismatch",
-					       auth->username);
-				}
-				return FALSE;
-			}
-		} else {
-			auth->rspauth = p_strconcat(auth->pool, "rspauth=",
-						    response_hex, NULL);
-		}
-	}
-
-	return TRUE;
-}
-
-static int verify_realm(const char *realm)
-{
-	const char *const *tmp;
-
-	for (tmp = auth_realms; *tmp != NULL; tmp++) {
-		if (strcasecmp(realm, *tmp) == 0)
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
-static int parse_next(char **data, char **key, char **value)
-{
-	/* @UNSAFE */
-	char *p, *dest;
-
-	p = *data;
-	while (IS_LWS(*p)) p++;
-
-	/* get key */
-	*key = p;
-	while (*p != '\0' && *p != '=' && *p != ',')
-		p++;
-
-	if (*p != '=') {
-		*data = p;
-		return FALSE;
-	}
-
-	*value = p+1;
-
-	/* skip trailing whitespace in key */
-	while (IS_LWS(p[-1]))
-		p--;
-	*p = '\0';
-
-	/* get value */
-	p = *value;
-	while (IS_LWS(*p)) p++;
-
-	if (*p != '"') {
-		while (*p != '\0' && *p != ',')
-			p++;
-
-		*data = p+1;
-		while (IS_LWS(p[-1]))
-			p--;
-		*p = '\0';
-	} else {
-		/* quoted string */
-		*value = dest = ++p;
-		while (*p != '\0' && *p != '"') {
-			if (*p == '\\' && p[1] != '\0')
-				p++;
-			*dest++ = *p++;
-		}
-
-		*data = *p == '"' ? p+1 : p;
-		*dest = '\0';
-	}
-
-	return TRUE;
-}
-
-/* remove leading and trailing whitespace */
-static const char *trim(const char *str)
-{
-	const char *ret;
-
-	while (IS_LWS(*str)) str++;
-	ret = str;
-
-	while (*str != '\0') str++;
-	if (str > ret) {
-		while (IS_LWS(str[-1])) str--;
-		ret = t_strdup_until(ret, str);
-	}
-
-	return ret;
-}
-
-static int auth_handle_response(struct auth_data *auth, char *key, char *value,
-				const char **error)
-{
-	int i;
-
-	str_lcase(key);
-
-	if (strcmp(key, "realm") == 0) {
-		if (!verify_realm(value)) {
-			*error = "Invalid realm";
-			return FALSE;
-		}
-		if (auth->realm == NULL)
-			auth->realm = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "username") == 0) {
-		if (auth->username != NULL) {
-			*error = "username must not exist more than once";
-			return FALSE;
-		}
-
-		if (*value == '\0') {
-			*error = "empty username";
-			return FALSE;
-		}
-
-		auth->username = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "nonce") == 0) {
-		/* nonce must be same */
-		if (strcmp(value, auth->nonce) != 0) {
-			*error = "Invalid nonce";
-			return FALSE;
-		}
-
-		auth->nonce_found = TRUE;
-		return TRUE;
-	}
-
-	if (strcmp(key, "cnonce") == 0) {
-		if (auth->cnonce != NULL) {
-			*error = "cnonce must not exist more than once";
-			return FALSE;
-		}
-
-		if (*value == '\0') {
-			*error = "cnonce can't contain empty value";
-			return FALSE;
-		}
-
-		auth->cnonce = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "nonce-count") == 0) {
-		if (auth->nonce_count != NULL) {
-			*error = "nonce-count must not exist more than once";
-			return FALSE;
-		}
-
-		if (atoi(value) != 1) {
-			*error = "re-auth not supported currently";
-			return FALSE;
-		}
-
-		auth->nonce_count = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "qop") == 0) {
-		for (i = 0; i < QOP_COUNT; i++) {
-			if (strcasecmp(qop_names[i], value) == 0)
-				break;
-		}
-
-		if (i == QOP_COUNT) {
-			*error = "Unknown QoP value";
-			return FALSE;
-		}
-
-		auth->qop &= (1 << i);
-		if (auth->qop == 0) {
-			*error = "Nonallowed QoP requested";
-			return FALSE;
-		} 
-
-		auth->qop_value = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "digest-uri") == 0) {
-		/* type / host / serv-name */
-		const char *const *uri = t_strsplit(value, "/");
-
-		if (uri[0] == NULL || uri[1] == NULL) {
-			*error = "Invalid digest-uri";
-			return FALSE;
-		}
-
-		if (strcasecmp(trim(uri[0]), SERVICE_TYPE) != 0) {
-			*error = "Unexpected service type in digest-uri";
-			return FALSE;
-		}
-
-		/* FIXME: RFC recommends that we verify the host/serv-type.
-		   But isn't the realm enough already? That'd be just extra
-		   configuration.. Maybe optionally list valid hosts in
-		   config file? */
-		auth->digest_uri = p_strdup(auth->pool, value);
-		return TRUE;
-	}
-
-	if (strcmp(key, "maxbuf") == 0) {
-		if (auth->maxbuf != 0) {
-			*error = "maxbuf must not exist more than once";
-			return FALSE;
-		}
-
-		auth->maxbuf = strtoul(value, NULL, 10);
-		if (auth->maxbuf == 0) {
-			*error = "Invalid maxbuf value";
-			return FALSE;
-		}
-		return TRUE;
-	}
-
-	if (strcmp(key, "charset") == 0) {
-		if (strcasecmp(value, "utf-8") != 0) {
-			*error = "Only utf-8 charset is allowed";
-			return FALSE;
-		}
-
-		return TRUE;
-	}
-
-	if (strcmp(key, "response") == 0) {
-		if (strlen(value) != 32) {
-			*error = "Invalid response value";
-			return FALSE;
-		}
-
-		memcpy(auth->response, value, 32);
-		return TRUE;
-	}
-
-	if (strcmp(key, "cipher") == 0) {
-		/* not supported, ignore */
-		return TRUE;
-	}
-
-	if (strcmp(key, "authzid") == 0) {
-		/* not supported, abort */
-		return FALSE;
-	}
-
-	/* unknown key, ignore */
-	return TRUE;
-}
-
-static int parse_digest_response(struct auth_data *auth, const char *data,
-				 size_t size, const char **error)
-{
-	char *copy, *key, *value;
-	int failed;
-
-	/*
-	   realm="realm"
-	   username="username"
-	   nonce="randomized data"
-	   cnonce="??"
-	   nc=00000001
-	   qop="auth|auth-int|auth-conf"
-	   digest-uri="serv-type/host[/serv-name]"
-	   response=32 HEX digits
-	   maxbuf=number (with auth-int, auth-conf, defaults to 64k)
-	   charset="utf-8" (iso-8859-1 if it doesn't exist)
-	   cipher="cipher-value"
-	   authzid="authzid-value"
-	*/
-
-	t_push();
-
-	failed = FALSE;
-
-	copy = t_strdup_noconst(t_strndup(data, size));
-	while (*copy != '\0') {
-		if (parse_next(&copy, &key, &value)) {
-			if (!auth_handle_response(auth, key, value, error)) {
-				failed = TRUE;
-				break;
-			}
-		}
-
-		if (*copy == ',')
-			copy++;
-	}
-
-	if (!auth->nonce_found) {
-		*error = "Missing nonce parameter";
-		failed = TRUE;
-	} else if (auth->cnonce == NULL) {
-		*error = "Missing cnonce parameter";
-		failed = TRUE;
-	} else if (auth->username == NULL) {
-		*error = "Missing username parameter";
-		failed = TRUE;
-	}
-
-	if (auth->nonce_count == NULL)
-		auth->nonce_count = p_strdup(auth->pool, "00000001");
-	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
-auth_digest_md5_continue(struct cookie_data *cookie,
-			 struct auth_continued_request_data *request,
-			 const unsigned char *data,
-			 auth_callback_t callback, void *context)
-{
-	struct auth_data *auth = cookie->context;
-	struct auth_reply_data reply;
-	const char *error;
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-
-	if (auth->authenticated) {
-		/* authentication is done, we were just waiting the last
-		   word from client */
-		if (strocpy(reply.virtual_user, auth->cookie_reply.virtual_user,
-			    sizeof(reply.virtual_user)) < 0)
-			i_panic("virtual_user overflow");
-
-		auth->cookie_reply.success = TRUE;
-		reply.result = AUTH_RESULT_SUCCESS;
-		callback(&reply, NULL, context);
-		return;
-	}
-
-	if (parse_digest_response(auth, (const char *) data,
-					 request->data_size, &error)) {
-		/* authentication ok */
-		reply.result = AUTH_RESULT_CONTINUE;
-
-		reply.data_size = strlen(auth->rspauth);
-		callback(&reply, auth->rspauth, context);
-		auth->authenticated = TRUE;
-		return;
-	}
-
-	if (error == NULL)
-                error = "Authentication failed";
-	else if (verbose)
-		i_info("digest-md5: %s", error);
-
-	/* failed */
-	reply.result = AUTH_RESULT_FAILURE;
-	callback(&reply, error, context);
-	cookie_remove(cookie->cookie);
-}
-
-static int auth_digest_md5_fill_reply(struct cookie_data *cookie,
-				      struct auth_cookie_reply_data *reply)
-{
-	struct auth_data *auth = cookie->context;
-
-	if (!auth->authenticated)
-		return FALSE;
-
-	memcpy(reply, &auth->cookie_reply,
-	       sizeof(struct auth_cookie_reply_data));
-	return TRUE;
-}
-
-static void auth_digest_md5_free(struct cookie_data *cookie)
-{
-	pool_unref(((struct auth_data *) cookie->context)->pool);
-}
-
-static void auth_digest_md5_init(unsigned int login_pid,
-				 struct auth_init_request_data *request,
-				 auth_callback_t callback, void *context)
-{
-	struct cookie_data *cookie;
-	struct auth_reply_data reply;
-	struct auth_data *auth;
-	pool_t pool;
-	const char *challenge;
-
-	pool = pool_alloconly_create("Digest-MD5", 2048);
-	auth = p_new(pool, struct auth_data, 1);
-	auth->pool = pool;
-
-	auth->qop = QOP_AUTH;
-
-	cookie = p_new(pool, struct cookie_data, 1);
-	cookie->login_pid = login_pid;
-	cookie->auth_fill_reply = auth_digest_md5_fill_reply;
-	cookie->auth_continue = auth_digest_md5_continue;
-	cookie->free = auth_digest_md5_free;
-	cookie->context = auth;
-
-	cookie_add(cookie);
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	reply.result = AUTH_RESULT_CONTINUE;
-	memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-
-	/* send the initial challenge */
-	t_push();
-
-	challenge = get_digest_challenge(auth);
-	reply.data_size = strlen(challenge);
-	callback(&reply, challenge, context);
-
-	t_pop();
-}
-
-struct auth_module auth_digest_md5 = {
-	AUTH_MECH_DIGEST_MD5,
-	auth_digest_md5_init
-};
--- a/src/auth/auth-interface.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-#ifndef __AUTH_INTERFACE_H
-#define __AUTH_INTERFACE_H
-
-#define AUTH_COOKIE_SIZE		16
-
-#define AUTH_MAX_REQUEST_DATA_SIZE	4096
-#define AUTH_MAX_REPLY_DATA_SIZE	4096
-
-#define AUTH_MAX_USER_LEN		64
-#define AUTH_MAX_VUSER_LEN		256
-#define AUTH_MAX_HOME_LEN		256
-#define AUTH_MAX_MAIL_LEN		256
-
-enum auth_request_type {
-	AUTH_REQUEST_NONE, /* must not be requested */
-	AUTH_REQUEST_INIT,
-        AUTH_REQUEST_CONTINUE
-};
-
-enum auth_result {
-	AUTH_RESULT_INTERNAL_FAILURE, /* never sent by imap-auth */
-
-	AUTH_RESULT_CONTINUE,
-	AUTH_RESULT_SUCCESS,
-	AUTH_RESULT_FAILURE
-};
-
-enum auth_mech {
-	AUTH_MECH_PLAIN		= 0x01,
-	AUTH_MECH_DIGEST_MD5	= 0x02,
-
-	AUTH_MECH_COUNT		= 2
-};
-
-/* Initialization reply, sent after client is connected */
-struct auth_init_data {
-	unsigned int auth_process; /* unique auth process identifier */
-	enum auth_mech auth_mechanisms; /* valid authentication mechanisms */
-};
-
-/* Initialization handshake from client. */
-struct client_auth_init_data {
-	unsigned int pid; /* unique identifier for client process */
-};
-
-/* New authentication request */
-struct auth_init_request_data {
-	enum auth_request_type type; /* AUTH_REQUEST_INIT */
-
-	enum auth_mech mech;
-	unsigned int id; /* AuthReplyData.id will contain this value */
-};
-
-/* Continued authentication request */
-struct auth_continued_request_data {
-	enum auth_request_type type; /* AUTH_REQUEST_CONTINUE */
-
-	unsigned char cookie[AUTH_COOKIE_SIZE];
-	unsigned int id; /* AuthReplyData.id will contain this value */
-
-	size_t data_size;
-	/* unsigned char data[]; */
-};
-
-/* Reply to authentication */
-struct auth_reply_data {
-	unsigned int id;
-	unsigned char cookie[AUTH_COOKIE_SIZE];
-	enum auth_result result;
-
-	char virtual_user[AUTH_MAX_VUSER_LEN]; /* set at SUCCESS */
-
-	size_t data_size;
-	/* unsigned char data[]; */
-};
-
-/* Request data associated to cookie */
-struct auth_cookie_request_data {
-	unsigned int id;
-	unsigned int login_pid;
-	unsigned char cookie[AUTH_COOKIE_SIZE];
-};
-
-/* Reply to cookie request */
-struct auth_cookie_reply_data {
-	unsigned int id;
-	int success; /* FALSE if cookie wasn't found */
-
-	char system_user[AUTH_MAX_USER_LEN]; /* system user, if available */
-	char virtual_user[AUTH_MAX_VUSER_LEN]; /* for logging etc. */
-	uid_t uid;
-	gid_t gid;
-
-	char home[AUTH_MAX_HOME_LEN];
-	char mail[AUTH_MAX_MAIL_LEN];
-
-	int chroot; /* chroot to home directory */
-};
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-login-interface.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,69 @@
+#ifndef __AUTH_LOGIN_INTERFACE_H
+#define __AUTH_LOGIN_INTERFACE_H
+
+/* max. size for auth_login_request_continue.data[] */
+#define AUTH_LOGIN_MAX_REQUEST_DATA_SIZE 4096
+
+enum auth_mech {
+	AUTH_MECH_PLAIN		= 0x01,
+	AUTH_MECH_DIGEST_MD5	= 0x02,
+
+	AUTH_MECH_COUNT
+};
+
+enum auth_login_request_type {
+	AUTH_LOGIN_REQUEST_NEW = 1,
+        AUTH_LOGIN_REQUEST_CONTINUE
+};
+
+enum auth_login_result {
+	AUTH_LOGIN_RESULT_CONTINUE = 1,
+	AUTH_LOGIN_RESULT_SUCCESS,
+	AUTH_LOGIN_RESULT_FAILURE
+};
+
+/* Incoming handshake */
+struct auth_login_handshake_input {
+	unsigned int pid; /* unique identifier for client process */
+};
+
+/* Outgoing handshake */
+struct auth_login_handshake_output {
+	unsigned int pid; /* unique auth process identifier */
+	enum auth_mech auth_mechanisms; /* valid authentication mechanisms */
+};
+
+/* New authentication request */
+struct auth_login_request_new {
+	enum auth_login_request_type type; /* AUTH_LOGIN_REQUEST_NEW */
+	unsigned int id; /* unique ID for the request */
+
+        enum auth_mech mech;
+};
+
+/* Continue authentication request */
+struct auth_login_request_continue {
+	enum auth_login_request_type type; /* AUTH_LOGIN_REQUEST_CONTINUE */
+	unsigned int id;
+
+	size_t data_size;
+	/* unsigned char data[]; */
+};
+
+/* Reply to authentication */
+struct auth_login_reply {
+	unsigned int id;
+
+	enum auth_login_result result;
+
+	/* variable width data, indexes into data[].
+	   Ignore if it points outside data_size. */
+	size_t username_idx; /* NUL-terminated */
+	size_t realm_idx; /* NUL-terminated */
+	size_t reply_idx; /* last, non-NUL terminated */
+
+	size_t data_size;
+	/* unsigned char data[]; */
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-master-interface.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,33 @@
+#ifndef __AUTH_MASTER_INTERFACE_H
+#define __AUTH_MASTER_INTERFACE_H
+
+#define AUTH_MASTER_MAX_REPLY_DATA_SIZE 4096
+
+struct auth_master_request {
+	unsigned int tag;
+
+	unsigned int id;
+	unsigned int login_pid;
+};
+
+struct auth_master_reply {
+	unsigned int tag;
+
+	unsigned int success:1;
+	unsigned int chroot:1; /* chroot to home directory */
+
+	uid_t uid;
+	gid_t gid;
+
+	/* variable width fields are packed into data[]. These variables
+	   contain indexes to the data, they're all NUL-terminated.
+	   Ignore if it points outside data_size. */
+	size_t system_user_idx;
+	size_t virtual_user_idx;
+	size_t home_idx, mail_idx;
+
+	size_t data_size;
+	/* unsigned char data[]; */
+};
+
+#endif
--- a/src/auth/auth-plain.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "safe-memset.h"
-#include "auth.h"
-#include "cookie.h"
-#include "userinfo.h"
-
-static void auth_plain_continue(struct cookie_data *cookie,
-				struct auth_continued_request_data *request,
-				const unsigned char *data,
-				auth_callback_t callback, void *context)
-{
-	struct auth_cookie_reply_data *cookie_reply = cookie->context;
-	struct auth_reply_data reply;
-	const char *authid, *authenid;
-	char *pass;
-	size_t i, count, len;
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	reply.result = AUTH_RESULT_FAILURE;
-	memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-
-	/* authorization ID \0 authentication ID \0 pass.
-	   we'll ignore authorization ID for now. */
-	authid = (const char *) data;
-	authenid = NULL; pass = NULL;
-
-	count = 0;
-	for (i = 0; i < request->data_size; i++) {
-		if (data[i] == '\0') {
-			if (++count == 1)
-				authenid = data + i+1;
-			else {
-				i++;
-				len = request->data_size - i;
-				pass = t_malloc(len+1);
-				memcpy(pass, data + i, len);
-				pass[len] = '\0';
-				break;
-			}
-		}
-	}
-
-	if (pass != NULL) {
-		if (userinfo->verify_plain(authenid, pass, cookie_reply)) {
-			cookie_reply->success = TRUE;
-			reply.result = AUTH_RESULT_SUCCESS;
-
-			if (strocpy(reply.virtual_user,
-				    cookie_reply->virtual_user,
-				    sizeof(reply.virtual_user)) < 0)
-				i_panic("virtual_user overflow");
-		}
-
-		if (*pass != '\0') {
-			/* make sure it's cleared */
-			safe_memset(pass, 0, strlen(pass));
-		}
-	}
-
-        callback(&reply, NULL, context);
-
-	if (!cookie_reply->success) {
-		/* failed, we don't need the cookie anymore */
-		cookie_remove(cookie->cookie);
-	}
-}
-
-static int auth_plain_fill_reply(struct cookie_data *cookie,
-				 struct auth_cookie_reply_data *reply)
-{
-	struct auth_cookie_reply_data *cookie_reply;
-
-	cookie_reply = cookie->context;
-	if (!cookie_reply->success)
-		return FALSE;
-
-	memcpy(reply, cookie_reply, sizeof(struct auth_cookie_reply_data));
-	return TRUE;
-}
-
-static void auth_plain_free(struct cookie_data *cookie)
-{
-	i_free(cookie->context);
-	i_free(cookie);
-}
-
-static void auth_plain_init(unsigned int login_pid,
-			    struct auth_init_request_data *request,
-			    auth_callback_t callback, void *context)
-{
-	struct cookie_data *cookie;
-	struct auth_reply_data reply;
-
-	cookie = i_new(struct cookie_data, 1);
-	cookie->login_pid = login_pid;
-	cookie->auth_fill_reply = auth_plain_fill_reply;
-	cookie->auth_continue = auth_plain_continue;
-	cookie->free = auth_plain_free;
-	cookie->context = i_new(struct auth_cookie_reply_data, 1);
-
-	cookie_add(cookie);
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	reply.result = AUTH_RESULT_CONTINUE;
-	memcpy(reply.cookie, cookie->cookie, AUTH_COOKIE_SIZE);
-
-	callback(&reply, NULL, context);
-}
-
-struct auth_module auth_plain = {
-	AUTH_MECH_PLAIN,
-	auth_plain_init
-};
--- a/src/auth/auth.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "auth.h"
-#include "cookie.h"
-
-#include <stdlib.h>
-
-struct auth_module_list {
-	struct auth_module_list *next;
-
-	struct auth_module module;
-};
-
-enum auth_mech auth_mechanisms;
-const char *const *auth_realms;
-
-static int set_use_cyrus_sasl;
-static struct auth_module_list *auth_modules;
-static struct auth_reply_data failure_reply;
-
-void auth_register_module(struct auth_module *module)
-{
-	struct auth_module_list *list;
-
-	i_assert((auth_mechanisms & module->mech) == 0);
-
-	auth_mechanisms |= module->mech;
-
-	list = i_new(struct auth_module_list, 1);
-	memcpy(&list->module, module, sizeof(struct auth_module));
-
-	list->next = auth_modules;
-	auth_modules = list;
-}
-
-void auth_unregister_module(struct auth_module *module)
-{
-	struct auth_module_list **pos, *list;
-
-	if ((auth_mechanisms & module->mech) == 0)
-		return; /* not registered */
-
-        auth_mechanisms &= ~module->mech;
-
-	for (pos = &auth_modules; *pos != NULL; pos = &(*pos)->next) {
-		if ((*pos)->module.mech == module->mech) {
-			list = *pos;
-			*pos = (*pos)->next;
-			i_free(list);
-			break;
-		}
-	}
-}
-
-void auth_init_request(unsigned int login_pid,
-		       struct auth_init_request_data *request,
-		       auth_callback_t callback, void *context)
-{
-	struct auth_module_list *list;
-
-	if ((auth_mechanisms & request->mech) == 0) {
-		/* unsupported mechanism */
-		i_error("BUG: imap-login requested unsupported "
-			"auth mechanism %d", request->mech);
-		failure_reply.id = request->id;
-		callback(&failure_reply, NULL, context);
-		return;
-	}
-
-#ifdef USE_CYRUS_SASL2
-	if (set_use_cyrus_sasl) {
-		auth_cyrus_sasl_init(login_pid, request, callback, context);
-		return;
-	}
-#endif
-
-	for (list = auth_modules; list != NULL; list = list->next) {
-		if (list->module.mech == request->mech) {
-			list->module.init(login_pid, request,
-					  callback, context);
-			return;
-		}
-	}
-	i_unreached();
-}
-
-void auth_continue_request(unsigned int login_pid,
-			   struct auth_continued_request_data *request,
-			   const unsigned char *data,
-			   auth_callback_t callback, void *context)
-{
-	struct cookie_data *cookie_data;
-
-	cookie_data = cookie_lookup(request->cookie);
-	if (cookie_data == NULL) {
-		/* timeouted cookie */
-		failure_reply.id = request->id;
-		callback(&failure_reply, NULL, context);
-	} else if (cookie_data->login_pid != login_pid) {
-		i_error("BUG: imap-login requested cookie it didn't own");
-	} else {
-		cookie_data->auth_continue(cookie_data, request,
-					   data, callback, context);
-	}
-}
-
-extern struct auth_module auth_plain;
-extern struct auth_module auth_digest_md5;
-
-void auth_init(void)
-{
-	const char *const *mechanisms;
-	const char *env;
-
-        auth_modules = NULL;
-	auth_mechanisms = 0;
-
-	memset(&failure_reply, 0, sizeof(failure_reply));
-	failure_reply.result = AUTH_RESULT_FAILURE;
-
-	/* register wanted mechanisms */
-	env = getenv("MECHANISMS");
-	if (env == NULL || *env == '\0')
-		i_fatal("MECHANISMS environment is unset");
-
-	mechanisms = t_strsplit(env, " ");
-	while (*mechanisms != NULL) {
-		if (strcasecmp(*mechanisms, "PLAIN") == 0)
-			auth_register_module(&auth_plain);
-		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
-			auth_register_module(&auth_digest_md5);
-		else {
-			i_fatal("Unknown authentication mechanism '%s'",
-				*mechanisms);
-		}
-
-		mechanisms++;
-	}
-
-	/* get our realm - note that we allocate from data stack so
-	   this function should never be called inside I/O loop or anywhere
-	   else where t_pop() is called */
-	env = getenv("REALMS");
-	if (env == NULL)
-		env = "";
-	auth_realms = t_strsplit(env, " ");
-
-	set_use_cyrus_sasl = getenv("USE_CYRUS_SASL") != NULL;
-
-#ifdef USE_CYRUS_SASL2
-	if (set_use_cyrus_sasl)
-		auth_cyrus_sasl_init_lib();
-#endif
-}
-
-void auth_deinit(void)
-{
-	auth_unregister_module(&auth_plain);
-	auth_unregister_module(&auth_digest_md5);
-}
--- a/src/auth/auth.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#ifndef __AUTH_H
-#define __AUTH_H
-
-#include "auth-interface.h"
-
-typedef void (*auth_callback_t)(struct auth_reply_data *reply,
-				const void *data, void *context);
-
-struct auth_module {
-	enum auth_mech mech;
-
-	void (*init)(unsigned int login_pid,
-		     struct auth_init_request_data *request,
-		     auth_callback_t callback, void *context);
-};
-
-extern enum auth_mech auth_mechanisms;
-extern const char *const *auth_realms;
-
-void auth_register_module(struct auth_module *module);
-void auth_unregister_module(struct auth_module *module);
-
-void auth_init_request(unsigned int login_pid,
-		       struct auth_init_request_data *request,
-		       auth_callback_t callback, void *context);
-void auth_continue_request(unsigned int login_pid,
-			   struct auth_continued_request_data *request,
-			   const unsigned char *data,
-			   auth_callback_t callback, void *context);
-
-void auth_cyrus_sasl_init_lib(void);
-void auth_cyrus_sasl_init(unsigned int login_pid,
-			  struct auth_init_request_data *request,
-			  auth_callback_t callback, void *context);
-
-void auth_init(void);
-void auth_deinit(void);
-
-#endif
--- a/src/auth/common.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/auth/common.h	Mon Jan 27 03:33:40 2003 +0200
@@ -2,7 +2,6 @@
 #define __COMMON_H
 
 #include "lib.h"
-#include "auth.h"
 
 #define MASTER_SOCKET_FD 0
 #define LOGIN_LISTEN_FD 3
--- a/src/auth/cookie.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "hash.h"
-#include "cookie.h"
-#include "randgen.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-
-/* 30 seconds should be more than enough */
-#define COOKIE_TIMEOUT 30
-
-struct cookie_list {
-	struct cookie_list *next;
-	time_t created;
-
-	struct cookie_data *data;
-};
-
-static struct hash_table *cookies;
-static struct cookie_list *oldest_cookie, **next_cookie;
-
-static struct timeout *to;
-
-/* a char* hash function from ASU -- from glib */
-static unsigned int cookie_hash(const void *p)
-{
-        const unsigned char *s = p;
-	unsigned int i, g, h = 0;
-
-	for (i = 0; i < AUTH_COOKIE_SIZE; i++) {
-		h = (h << 4) + s[i];
-		if ((g = h & 0xf0000000UL)) {
-			h = h ^ (g >> 24);
-			h = h ^ g;
-		}
-	}
-
-	return h;
-}
-
-static int cookie_cmp(const void *p1, const void *p2)
-{
-	int i, ret;
-
-	for (i = 0; i < AUTH_COOKIE_SIZE; i++) {
-		ret = ((const unsigned char *) p1)[i] -
-			((const unsigned char *) p2)[i];
-		if (ret != 0)
-			return ret;
-	}
-
-	return 0;
-}
-
-void cookie_add(struct cookie_data *data)
-{
-	struct cookie_list *list;
-
-	do {
-		random_fill(data->cookie, AUTH_COOKIE_SIZE);
-	} while (hash_lookup(cookies, data->cookie));
-
-	/* add to linked list */
-	list = i_new(struct cookie_list, 1);
-	list->created = ioloop_time;
-	list->data = data;
-
-	*next_cookie = list;
-	next_cookie = &list->next;
-
-	/* add to hash */
-	hash_insert(cookies, data->cookie, data);
-}
-
-static void cookie_destroy(unsigned char cookie[AUTH_COOKIE_SIZE],
-			   int free_data)
-{
-	struct cookie_list **pos, *list;
-
-	hash_remove(cookies, cookie);
-
-	/* FIXME: slow */
-	list = NULL;
-	for (pos = &oldest_cookie; *pos != NULL; pos = &(*pos)->next) {
-		if (cookie_cmp((*pos)->data->cookie, cookie) == 0) {
-			list = *pos;
-			*pos = list->next;
-			break;
-		}
-	}
-	i_assert(list != NULL);
-
-	if (list->next == NULL)
-		next_cookie = pos;
-
-	if (free_data)
-		list->data->free(list->data);
-	i_free(list);
-}
-
-struct cookie_data *cookie_lookup(unsigned char cookie[AUTH_COOKIE_SIZE])
-{
-	return hash_lookup(cookies, cookie);
-}
-
-void cookie_remove(unsigned char cookie[AUTH_COOKIE_SIZE])
-{
-	cookie_destroy(cookie, TRUE);
-}
-
-struct cookie_data *
-cookie_lookup_and_remove(unsigned int login_pid,
-			 unsigned char cookie[AUTH_COOKIE_SIZE])
-{
-	struct cookie_data *data;
-
-	data = hash_lookup(cookies, cookie);
-	if (data != NULL) {
-		if (data->login_pid != login_pid)
-			data = NULL;
-		else
-			cookie_destroy(cookie, FALSE);
-	}
-	return data;
-}
-
-void cookies_remove_login_pid(unsigned int login_pid)
-{
-	struct cookie_list *list, *next;
-
-	/* FIXME: slow */
-	for (list = oldest_cookie; list != NULL; list = next) {
-		next = list->next;
-
-		if (list->data->login_pid == login_pid)
-			cookie_destroy(list->data->cookie, TRUE);
-	}
-}
-
-static void cookie_timeout(void *context __attr_unused__,
-			   struct timeout *timeout __attr_unused__)
-{
-	time_t remove_time;
-
-        remove_time = ioloop_time - COOKIE_TIMEOUT;
-	while (oldest_cookie != NULL && oldest_cookie->created < remove_time)
-		cookie_destroy(oldest_cookie->data->cookie, TRUE);
-}
-
-void cookies_init(void)
-{
-	oldest_cookie = NULL;
-	next_cookie = &oldest_cookie;
-
-	cookies = hash_create(default_pool, default_pool, 100,
-			      cookie_hash, cookie_cmp);
-	to = timeout_add(10000, cookie_timeout, NULL);
-}
-
-void cookies_deinit(void)
-{
-	while (oldest_cookie != NULL)
-		cookie_destroy(oldest_cookie->data->cookie, TRUE);
-	hash_destroy(cookies);
-
-	timeout_remove(to);
-}
--- a/src/auth/cookie.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#ifndef __COOKIE_H
-#define __COOKIE_H
-
-#include "auth-interface.h"
-
-struct cookie_data {
-	unsigned int login_pid;
-	unsigned char cookie[AUTH_COOKIE_SIZE];
-
-	/* continue authentication */
-	void (*auth_continue)(struct cookie_data *cookie,
-			      struct auth_continued_request_data *request,
-			      const unsigned char *data,
-			      auth_callback_t callback, void *context);
-
-	/* fills reply from cookie, returns TRUE if successful */
-	int (*auth_fill_reply)(struct cookie_data *cookie,
-			       struct auth_cookie_reply_data *reply);
-
-	/* Free all data related to cookie */
-	void (*free)(struct cookie_data *cookie);
-
-	void *context;
-};
-
-/* data->cookie is filled */
-void cookie_add(struct cookie_data *data);
-/* Looks up the cookie */
-struct cookie_data *cookie_lookup(unsigned char cookie[AUTH_COOKIE_SIZE]);
-/* Removes and frees the cookie */
-void cookie_remove(unsigned char cookie[AUTH_COOKIE_SIZE]);
-/* Looks up the cookie and removes it, you have to free the returned data. */
-struct cookie_data *
-cookie_lookup_and_remove(unsigned int login_pid,
-			 unsigned char cookie[AUTH_COOKIE_SIZE]);
-
-/* Remove all cookies created by a login process. */
-void cookies_remove_login_pid(unsigned int login_pid);
-
-void cookies_init(void);
-void cookies_deinit(void);
-
-#endif
--- a/src/auth/login-connection.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/auth/login-connection.c	Mon Jan 27 03:33:40 2003 +0200
@@ -5,50 +5,47 @@
 #include "istream.h"
 #include "ostream.h"
 #include "network.h"
+#include "hash.h"
 #include "safe-memset.h"
-#include "cookie.h"
+#include "mech.h"
 #include "login-connection.h"
 
 #include <stdlib.h>
 #include <syslog.h>
 
 #define MAX_INBUF_SIZE \
-	(sizeof(struct auth_continued_request_data) + \
-	 AUTH_MAX_REQUEST_DATA_SIZE)
-#define MAX_OUTBUF_SIZE \
-	(10 * (sizeof(struct auth_reply_data) + AUTH_MAX_REPLY_DATA_SIZE))
-
-struct login_connection {
-	struct login_connection *next;
+	(sizeof(struct auth_login_request_continue) + \
+	 AUTH_LOGIN_MAX_REQUEST_DATA_SIZE)
+#define MAX_OUTBUF_SIZE (1024*50)
 
-	int fd;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-
-	unsigned int pid;
-	enum auth_request_type type;
-};
-
-static struct auth_init_data auth_init_data;
+static struct auth_login_handshake_output handshake_output;
 static struct login_connection *connections;
 
-static void request_callback(struct auth_reply_data *reply,
-			     const void *data, void *context)
+static void request_callback(struct auth_login_reply *reply,
+			     const void *data, struct login_connection *conn)
 {
-	struct login_connection *conn = context;
-
-	i_assert(reply->data_size <= AUTH_MAX_REPLY_DATA_SIZE);
+	ssize_t ret;
 
-	if (o_stream_send(conn->output, reply, sizeof(*reply)) < 0)
-		login_connection_destroy(conn);
-	else if (reply->data_size > 0) {
-		if (o_stream_send(conn->output, data, reply->data_size) < 0)
-			login_connection_destroy(conn);
+	ret = o_stream_send(conn->output, reply, sizeof(*reply));
+	if ((size_t)ret == sizeof(*reply)) {
+		if (reply->data_size == 0) {
+			/* all sent */
+			return;
+		}
+
+		ret = o_stream_send(conn->output, data, reply->data_size);
+		if ((size_t)ret == reply->data_size) {
+			/* all sent */
+			return;
+		}
 	}
+
+	if (ret >= 0)
+		i_warning("Transmit buffer full for login process, killing it");
+	login_connection_destroy(conn);
 }
 
-static struct login_connection *login_find_pid(unsigned int pid)
+struct login_connection *login_connection_lookup(unsigned int pid)
 {
 	struct login_connection *conn;
 
@@ -62,12 +59,12 @@
 
 static void login_input_handshake(struct login_connection *conn)
 {
-        struct client_auth_init_data rec;
+        struct auth_login_handshake_input rec;
         unsigned char *data;
 	size_t size;
 
 	data = i_stream_get_modifyable_data(conn->input, &size);
-	if (size < sizeof(struct client_auth_init_data))
+	if (size < sizeof(struct auth_login_handshake_input))
 		return;
 
 	/* Don't just cast because of alignment issues. */
@@ -77,7 +74,7 @@
 	if (rec.pid == 0) {
 		i_error("BUG: imap-login said it's PID 0");
 		login_connection_destroy(conn);
-	} else if (login_find_pid(rec.pid) != NULL) {
+	} else if (login_connection_lookup(rec.pid) != NULL) {
 		/* well, it might have just reconnected very fast .. although
 		   there's not much reason for it. */
 		i_error("BUG: imap-login gave a PID of existing connection");
@@ -93,22 +90,20 @@
 
 static void login_input_request(struct login_connection *conn)
 {
+        enum auth_login_request_type type;
         unsigned char *data;
 	size_t size;
 
 	data = i_stream_get_modifyable_data(conn->input, &size);
-	if (size < sizeof(enum auth_request_type))
+	if (size < sizeof(type))
 		return;
 
 	/* note that we can't directly cast the received data pointer into
 	   structures, as it may not be aligned properly. */
-	if (conn->type == AUTH_REQUEST_NONE) {
-		/* get the request type */
-		memcpy(&conn->type, data, sizeof(enum auth_request_type));
-	}
+	memcpy(&type, data, sizeof(type));
 
-	if (conn->type == AUTH_REQUEST_INIT) {
-		struct auth_init_request_data request;
+	if (type == AUTH_LOGIN_REQUEST_NEW) {
+		struct auth_login_request_new request;
 
 		if (size < sizeof(request))
 			return;
@@ -117,10 +112,9 @@
 		i_stream_skip(conn->input, sizeof(request));
 
 		/* we have a full init request */
-		auth_init_request(conn->pid, &request, request_callback, conn);
-		conn->type = AUTH_REQUEST_NONE;
-	} else if (conn->type == AUTH_REQUEST_CONTINUE) {
-                struct auth_continued_request_data request;
+		mech_request_new(conn, &request, request_callback);
+	} else if (type == AUTH_LOGIN_REQUEST_CONTINUE) {
+                struct auth_login_request_continue request;
 
 		if (size < sizeof(request))
 			return;
@@ -132,17 +126,14 @@
 		i_stream_skip(conn->input, sizeof(request) + request.data_size);
 
 		/* we have a full continued request */
-		auth_continue_request(conn->pid, &request,
-				      data + sizeof(request),
-				      request_callback, conn);
-		conn->type = AUTH_REQUEST_NONE;
+		mech_request_continue(conn, &request, data + sizeof(request),
+				      request_callback);
 
 		/* clear any sensitive data from memory */
 		safe_memset(data + sizeof(request), 0, request.data_size);
 	} else {
 		/* unknown request */
-		i_error("BUG: imap-login sent us unknown request %u",
-			conn->type);
+		i_error("BUG: imap-login sent us unknown request %u", type);
 		login_connection_destroy(conn);
 	}
 }
@@ -188,13 +179,16 @@
 	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
 					    IO_PRIORITY_DEFAULT, FALSE);
 	conn->io = io_add(fd, IO_READ, login_input, conn);
-	conn->type = AUTH_REQUEST_NONE;
+
+	conn->pool = pool_alloconly_create("auth_request hash", 10240);
+	conn->auth_requests = hash_create(default_pool, conn->pool,
+					  0, NULL, NULL);
 
 	conn->next = connections;
 	connections = conn;
 
-	if (o_stream_send(conn->output, &auth_init_data,
-			  sizeof(auth_init_data)) < 0) {
+	if (o_stream_send(conn->output, &handshake_output,
+			  sizeof(handshake_output)) < 0) {
 		login_connection_destroy(conn);
 		conn = NULL;
 	}
@@ -216,13 +210,14 @@
 		}
 	}
 
-	cookies_remove_login_pid(conn->pid);
+	hash_destroy(conn->auth_requests);
 
 	i_stream_unref(conn->input);
 	o_stream_unref(conn->output);
 
 	io_remove(conn->io);
 	net_disconnect(conn->fd);
+	pool_unref(conn->pool);
 	i_free(conn);
 }
 
@@ -234,9 +229,9 @@
 	if (env == NULL)
 		i_fatal("AUTH_PROCESS environment is unset");
 
-	memset(&auth_init_data, 0, sizeof(auth_init_data));
-	auth_init_data.auth_process = atoi(env);
-	auth_init_data.auth_mechanisms = auth_mechanisms;
+	memset(&handshake_output, 0, sizeof(handshake_output));
+	handshake_output.pid = atoi(env);
+	handshake_output.auth_mechanisms = auth_mechanisms;
 
 	connections = NULL;
 }
--- a/src/auth/login-connection.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/auth/login-connection.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,9 +1,27 @@
 #ifndef __LOGIN_CONNECTION_H
 #define __LOGIN_CONNECTION_H
 
+#include "auth-login-interface.h"
+
+struct login_connection {
+	struct login_connection *next;
+
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	pool_t pool;
+	struct hash_table *auth_requests;
+
+	unsigned int pid;
+};
+
 struct login_connection *login_connection_create(int fd);
 void login_connection_destroy(struct login_connection *conn);
 
+struct login_connection *login_connection_lookup(unsigned int pid);
+
 void login_connections_init(void);
 void login_connections_deinit(void);
 
--- a/src/auth/main.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/auth/main.c	Mon Jan 27 03:33:40 2003 +0200
@@ -7,11 +7,11 @@
 #include "restrict-access.h"
 #include "fd-close-on-exec.h"
 #include "randgen.h"
-#include "auth.h"
-#include "cookie.h"
+#include "mech.h"
+#include "userdb.h"
+#include "passdb.h"
+#include "master-connection.h"
 #include "login-connection.h"
-#include "userinfo.h"
-#include "master.h"
 
 #include <stdlib.h>
 #include <syslog.h>
@@ -73,16 +73,17 @@
 
 	verbose = getenv("VERBOSE") != NULL;
 
-	auth_init();
-	cookies_init();
+	mech_init();
+	userdb_init();
+	passdb_init();
+
 	login_connections_init();
-	userinfo_init();
 
 	io_listen = io_add_priority(LOGIN_LISTEN_FD, IO_PRIORITY_LOW,
 				    IO_READ, auth_accept, NULL);
 
 	/* initialize master last - it sends the "we're ok" notification */
-	master_init();
+	master_connection_init();
 }
 
 static void main_deinit(void)
@@ -92,12 +93,13 @@
 
 	io_remove(io_listen);
 
-	userinfo_deinit();
-	master_deinit();
 	login_connections_deinit();
-	cookies_deinit();
-	auth_deinit();
 
+	passdb_deinit();
+	userdb_deinit();
+	mech_deinit();
+
+	master_connection_deinit();
 	random_deinit();
 
 	closelog();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/master-connection.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "buffer.h"
+#include "hash.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "network.h"
+#include "mech.h"
+#include "userdb.h"
+#include "login-connection.h"
+#include "master-connection.h"
+#include "auth-master-interface.h"
+
+#define MAX_OUTBUF_SIZE (1024*50)
+
+static struct auth_master_reply failure_reply;
+
+static struct ostream *output;
+static struct io *io_master;
+
+static unsigned int master_pos;
+static char master_buf[sizeof(struct auth_master_request)];
+
+static size_t reply_add(buffer_t *buf, const char *str)
+{
+	size_t index;
+
+	if (str == NULL || *str == '\0')
+		return (size_t)-1;
+
+	index = buffer_get_used_size(buf) - sizeof(struct auth_master_reply);
+	buffer_append(buf, str, strlen(str)+1);
+	return index;
+}
+
+static struct auth_master_reply *
+fill_reply(const struct user_data *user, size_t *reply_size)
+{
+	struct auth_master_reply *reply;
+	buffer_t *buf;
+
+	buf = buffer_create_dynamic(data_stack_pool,
+				    sizeof(*reply) + 256, (size_t)-1);
+	reply = buffer_append_space(buf, sizeof(*reply));
+
+	reply->success = TRUE;
+
+	reply->chroot = user->chroot;
+	reply->uid = user->uid;
+	reply->gid = user->gid;
+
+	reply->system_user_idx = reply_add(buf, user->system_user);
+	reply->virtual_user_idx = reply_add(buf, user->virtual_user);
+	reply->home_idx = reply_add(buf, user->home);
+	reply->mail_idx = reply_add(buf, user->mail);
+
+	*reply_size = buffer_get_used_size(buf);
+	reply->data_size = *reply_size - sizeof(*reply);
+	return reply;
+}
+
+static void master_handle_request(struct auth_master_request *request,
+				  int fd __attr_unused__)
+{
+	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;
+	for (;;) {
+		ret = o_stream_send(output, reply, reply_size);
+		if (ret < 0) {
+			/* master died, kill ourself too */
+			io_loop_stop(ioloop);
+			break;
+		}
+
+		if ((size_t)ret == reply_size)
+			break;
+
+		/* buffer full, we have to block */
+		i_warning("Master transmit buffer full, blocking..");
+		if (o_stream_flush(output) < 0) {
+			/* transmit error, probably master died */
+			io_loop_stop(ioloop);
+			break;
+		}
+	}
+}
+
+static void master_input(void *context __attr_unused__, int fd,
+			 struct io *io __attr_unused__)
+{
+	int ret;
+
+	ret = net_receive(fd, master_buf + master_pos,
+			  sizeof(master_buf) - master_pos);
+	if (ret < 0) {
+		/* master died, kill ourself too */
+		io_loop_stop(ioloop);
+		return;
+	}
+
+	master_pos += ret;
+	if (master_pos < sizeof(master_buf))
+		return;
+
+	/* reply is now read */
+	master_handle_request((struct auth_master_request *) master_buf,
+			      fd);
+	master_pos = 0;
+}
+
+void master_connection_init(void)
+{
+	memset(&failure_reply, 0, sizeof(failure_reply));
+
+	master_pos = 0;
+	output = o_stream_create_file(MASTER_SOCKET_FD, default_pool,
+				      MAX_OUTBUF_SIZE, IO_PRIORITY_DEFAULT,
+				      FALSE);
+	io_master = io_add(MASTER_SOCKET_FD, IO_READ, master_input, NULL);
+
+	/* just a note to master that we're ok. if we die before,
+	   master should shutdown itself. */
+	o_stream_send(output, "O", 1);
+}
+
+void master_connection_deinit(void)
+{
+	o_stream_unref(output);
+	io_remove(io_master);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/master-connection.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,7 @@
+#ifndef __MASTER_CONNECTION_H
+#define __MASTER_CONNECTION_H
+
+void master_connection_init(void);
+void master_connection_deinit(void);
+
+#endif
--- a/src/auth/master.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "ostream.h"
-#include "network.h"
-#include "cookie.h"
-#include "master.h"
-
-#define MAX_OUTBUF_SIZE (10 * sizeof(struct auth_cookie_reply_data))
-
-static struct auth_cookie_reply_data failure_reply;
-
-static struct ostream *output;
-static struct io *io_master;
-
-static unsigned int master_pos;
-static char master_buf[sizeof(struct auth_cookie_request_data)];
-
-static void master_handle_request(struct auth_cookie_request_data *request,
-				  int fd __attr_unused__)
-{
-	struct cookie_data *cookie;
-        struct auth_cookie_reply_data *reply, temp_reply;
-
-	cookie = cookie_lookup_and_remove(request->login_pid, request->cookie);
-	if (cookie == NULL)
-		reply = &failure_reply;
-	else {
-		if (cookie->auth_fill_reply(cookie, &temp_reply))
-			reply = &temp_reply;
-		else
-			reply = &failure_reply;
-		cookie->free(cookie);
-	}
-
-	reply->id = request->id;
-	switch (o_stream_send(output, reply, sizeof(*reply))) {
-	case -2:
-		i_fatal("Master transmit buffer full, aborting");
-	case -1:
-		/* master died, kill ourself too */
-		io_loop_stop(ioloop);
-		break;
-	}
-}
-
-static void master_input(void *context __attr_unused__, int fd,
-			 struct io *io __attr_unused__)
-{
-	int ret;
-
-	ret = net_receive(fd, master_buf + master_pos,
-			  sizeof(master_buf) - master_pos);
-	if (ret < 0) {
-		/* master died, kill ourself too */
-		io_loop_stop(ioloop);
-		return;
-	}
-
-	master_pos += ret;
-	if (master_pos < sizeof(master_buf))
-		return;
-
-	/* reply is now read */
-	master_handle_request((struct auth_cookie_request_data *) master_buf,
-			      fd);
-	master_pos = 0;
-}
-
-void master_init(void)
-{
-	memset(&failure_reply, 0, sizeof(failure_reply));
-
-	master_pos = 0;
-	output = o_stream_create_file(MASTER_SOCKET_FD, default_pool,
-				      MAX_OUTBUF_SIZE, IO_PRIORITY_DEFAULT,
-				      FALSE);
-	io_master = io_add(MASTER_SOCKET_FD, IO_READ, master_input, NULL);
-
-	/* just a note to master that we're ok. if we die before,
-	   master should shutdown itself. */
-	o_stream_send(output, "O", 1);
-}
-
-void master_deinit(void)
-{
-	o_stream_unref(output);
-	io_remove(io_master);
-}
--- a/src/auth/master.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#ifndef __MASTER_H
-#define __MASTER_H
-
-void master_init(void);
-void master_deinit(void);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-cyrus-sasl2.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,250 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "common.h"
+#include "mech.h"
+
+#ifdef USE_CYRUS_SASL2
+
+#include <stdlib.h>
+#include <sasl/sasl.h>
+
+#include "auth-mech-desc.h"
+
+struct cyrus_auth_request {
+	struct auth_request auth_request;
+
+	sasl_conn_t *conn;
+	int success;
+};
+
+static const char *auth_mech_to_str(enum auth_mech mech)
+{
+	int i;
+
+	for (i = 0; i < AUTH_MECH_COUNT; i++) {
+		if (auth_mech_desc[i].mech == mech)
+			return auth_mech_desc[i].name;
+	}
+
+	return NULL;
+}
+
+static int
+cyrus_sasl_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 cyrus_auth_request *cyrus_request =
+		(struct cyrus_auth_request *)auth_request;
+	struct auth_login_reply reply;
+	const char *serverout;
+	unsigned int serveroutlen;
+	int ret;
+
+	ret = sasl_server_step(cyrus_request->conn, data, request->data_size,
+			       &serverout, &serveroutlen);
+
+	mech_init_login_reply(&reply);
+	reply.id = request->id;
+
+	if (ret == SASL_CONTINUE) {
+		reply.result = AUTH_LOGIN_RESULT_CONTINUE;
+		reply.data_size = serveroutlen;
+	} else if (ret == SASL_OK) {
+		/* success */
+		reply.result = AUTH_LOGIN_RESULT_SUCCESS;
+		cyrus_request->success = TRUE;
+
+		serverout = mech_auth_success(&reply, auth_request,
+					      serverout, serveroutlen);
+	} else {
+		/* failure */
+		reply.result = AUTH_LOGIN_RESULT_FAILURE;
+	}
+
+	callback(&reply, serverout, conn);
+	return reply.result != AUTH_LOGIN_RESULT_FAILURE;
+}
+
+#if 0
+static int auth_sasl_fill_reply(struct cookie_data *cookie,
+				struct auth_cookie_reply_data *reply)
+{
+	struct auth_context *ctx = cookie->context;
+	const char *canon_user;
+        const struct propval *prop;
+	int ret;
+
+	if (!ctx->success)
+		return FALSE;
+
+	/* get our username */
+	ret = sasl_getprop(ctx->conn, SASL_USERNAME,
+			   (const void **) &canon_user);
+	if (ret != SASL_OK) {
+		i_warning("sasl_getprop() failed: %s",
+			  sasl_errstring(ret, NULL, NULL));
+		return FALSE;
+	}
+
+	memset(reply, 0, sizeof(*reply));
+	reply->success = TRUE;
+
+	if (strocpy(reply->virtual_user, canon_user,
+		    sizeof(reply->virtual_user)) < 0)
+		i_panic("virtual_user overflow");
+
+	/* get other properties */
+	prop = prop_get(sasl_auxprop_getctx(ctx->conn));
+	for (; prop != NULL && prop->name != NULL; prop++) {
+		if (prop->nvalues == 0 || prop->values[0] == NULL)
+			continue;
+
+		if (strcasecmp(prop->name, SASL_AUX_UIDNUM) == 0)
+			reply->uid = atoi(prop->values[0]);
+		else if (strcasecmp(prop->name, SASL_AUX_GIDNUM) == 0)
+			reply->gid = atoi(prop->values[0]);
+		else if (strcasecmp(prop->name, SASL_AUX_HOMEDIR) == 0) {
+			if (strocpy(reply->home, prop->values[0],
+				    sizeof(reply->home)) < 0)
+				i_panic("home overflow");
+		} else if (strcasecmp(prop->name, SASL_AUX_UNIXMBX) == 0) {
+			if (strocpy(reply->mail, prop->values[0],
+				    sizeof(reply->mail)) < 0)
+				i_panic("mail overflow");
+		}
+	}
+
+	return TRUE;
+}
+#endif
+
+static void cyrus_sasl_auth_free(struct auth_request *auth_request)
+{
+	struct cyrus_auth_request *cyrus_request =
+		(struct cyrus_auth_request *)auth_request;
+
+	sasl_dispose(&cyrus_request->conn);
+	pool_unref(auth_request->pool);
+}
+
+struct auth_request *mech_cyrus_sasl_new(struct login_connection *conn,
+					 struct auth_login_request_new *request,
+					 mech_callback_t *callback)
+{
+	static const char *propnames[] = {
+		SASL_AUX_UIDNUM,
+		SASL_AUX_GIDNUM,
+		SASL_AUX_HOMEDIR,
+		SASL_AUX_UNIXMBX,
+		NULL
+	};
+	struct cyrus_auth_request *cyrus_request;
+	struct auth_login_reply reply;
+	const char *mech, *serverout;
+	unsigned int serveroutlen;
+	sasl_security_properties_t sec_props;
+	sasl_conn_t *sasl_conn;
+	pool_t pool;
+	int ret;
+
+	mech = auth_mech_to_str(request->mech);
+	if (mech == NULL)
+		i_fatal("Login asked for unknown mechanism %d", request->mech);
+
+	/* create new SASL connection */
+	ret = sasl_server_new("imap", NULL, NULL, NULL, NULL, NULL, 0,
+			      &sasl_conn);
+	if (ret != SASL_OK) {
+		i_fatal("sasl_server_new() failed: %s",
+			sasl_errstring(ret, NULL, NULL));
+	}
+
+	/* don't allow SASL security layer */
+	memset(&sec_props, 0, sizeof(sec_props));
+	sec_props.min_ssf = 0;
+	sec_props.max_ssf = 1;
+
+	if (sasl_setprop(sasl_conn, SASL_SEC_PROPS, &sec_props) != SASL_OK) {
+		i_fatal("sasl_setprop(SASL_SEC_PROPS) failed: %s",
+			sasl_errstring(ret, NULL, NULL));
+	}
+
+	ret = sasl_auxprop_request(sasl_conn, propnames);
+	if (ret != SASL_OK) {
+		i_fatal("sasl_auxprop_request() failed: %s",
+			sasl_errstring(ret, NULL, NULL));
+	}
+
+	/* initialize reply */
+	mech_init_login_reply(&reply);
+	reply.id = request->id;
+	reply.reply_idx = 0;
+
+	/* start the exchange */
+	ret = sasl_server_start(sasl_conn, mech, NULL, 0,
+				&serverout, &serveroutlen);
+	if (ret != SASL_CONTINUE) {
+		reply.result = AUTH_LOGIN_RESULT_FAILURE;
+		sasl_dispose(&sasl_conn);
+
+		callback(&reply, NULL, conn);
+		return NULL;
+	}
+
+	pool = pool_alloconly_create("cyrus_sasl_auth_request", 256);
+	cyrus_request = p_new(pool, struct cyrus_auth_request, 1);
+
+	cyrus_request->auth_request.pool = pool;
+	cyrus_request->auth_request.auth_continue =
+		cyrus_sasl_auth_continue;
+	cyrus_request->auth_request.auth_free =
+		cyrus_sasl_auth_free;
+
+	reply.result = AUTH_LOGIN_RESULT_CONTINUE;
+
+	reply.data_size = serveroutlen;
+	callback(&reply, serverout, conn);
+
+	return &cyrus_request->auth_request;
+}
+
+static int sasl_log(void *context __attr_unused__,
+		    int level, const char *message)
+{
+	switch (level) {
+	case SASL_LOG_ERR:
+		i_error("SASL authentication error: %s", message);
+		break;
+	case SASL_LOG_WARN:
+		i_warning("SASL authentication warning: %s", message);
+		break;
+	case SASL_LOG_NOTE:
+		/*i_info("SASL authentication info: %s", message);*/
+		break;
+	case SASL_LOG_FAIL:
+		/*i_info("SASL authentication failure: %s", message);*/
+		break;
+	}
+
+	return SASL_OK;
+}
+
+static const struct sasl_callback sasl_callbacks[] = {
+	{ SASL_CB_LOG, &sasl_log, NULL },
+	{ SASL_CB_LIST_END, NULL, NULL }
+};
+
+void mech_cyrus_sasl_init_lib(void)
+{
+	int ret;
+
+	ret = sasl_server_init(sasl_callbacks, "imap-auth");
+	if (ret != SASL_OK) {
+		i_fatal("sasl_server_init() failed: %s",
+			sasl_errstring(ret, NULL, NULL));
+	}
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-digest-md5.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,625 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+/* Digest-MD5 SASL authentication, see RFC-2831 */
+
+#include "common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "hex-binary.h"
+#include "md5.h"
+#include "randgen.h"
+#include "str.h"
+#include "mech.h"
+#include "passdb.h"
+
+#include <stdlib.h>
+
+#define SERVICE_TYPE "imap"
+
+/* Linear whitespace */
+#define IS_LWS(c) ((c) == ' ' || (c) == '\t')
+
+enum qop_option {
+	QOP_AUTH	= 0x01,	/* authenticate */
+	QOP_AUTH_INT	= 0x02, /* + integrity protection, not supported yet */
+	QOP_AUTH_CONF	= 0x04, /* + encryption, not supported yet */
+
+	QOP_COUNT	= 3
+};
+
+static const char *qop_names[] = { "auth", "auth-int", "auth-conf" };
+
+struct digest_auth_request {
+	struct auth_request auth_request;
+
+	pool_t pool;
+	unsigned int authenticated:1;
+
+	/* requested: */
+	char *nonce;
+	enum qop_option qop;
+
+	/* received: */
+	char *realm; /* may be NULL */
+	char *username;
+	char *cnonce;
+	char *nonce_count;
+	char *qop_value;
+	char *digest_uri; /* may be NULL */
+	unsigned char response[32];
+	unsigned long maxbuf;
+	unsigned int nonce_found:1;
+
+	/* final reply: */
+	char *rspauth;
+};
+
+static string_t *get_digest_challenge(struct digest_auth_request *auth)
+{
+	buffer_t *buf;
+	string_t *str;
+	const char *const *tmp;
+	unsigned char nonce[16];
+	int i, first_qop;
+
+	/*
+	   realm="hostname" (multiple allowed)
+	   nonce="randomized data, at least 64bit"
+	   qop="auth,auth-int,auth-conf"
+	   maxbuf=number (with auth-int, auth-conf, defaults to 64k)
+	   charset="utf-8" (iso-8859-1 if it doesn't exist)
+	   algorithm="md5-sess"
+	   cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf)
+	*/
+
+	/* get 128bit of random data as nonce */
+	random_fill(nonce, sizeof(nonce));
+
+	t_push();
+	buf = buffer_create_static(data_stack_pool,
+				   MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1);
+
+	base64_encode(nonce, sizeof(nonce), buf);
+	buffer_append_c(buf, '\0');
+	auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL));
+	t_pop();
+
+	str = t_str_new(256);
+
+	for (tmp = auth_realms; *tmp != NULL; tmp++) {
+		str_printfa(str, "realm=\"%s\"", *tmp);
+		str_append_c(str, ',');
+	}
+
+	str_printfa(str, "nonce=\"%s\",", auth->nonce);
+
+	str_append(str, "qop=\""); first_qop = TRUE;
+	for (i = 0; i < QOP_COUNT; i++) {
+		if (auth->qop & (1 << i)) {
+			if (first_qop)
+				first_qop = FALSE;
+			else
+				str_append_c(str, ',');
+			str_append(str, qop_names[i]);
+		}
+	}
+	str_append(str, "\",");
+
+	str_append(str, "charset=\"utf-8\","
+		   "algorithm=\"md5-sess\"");
+	return str;
+}
+
+static int verify_auth(struct digest_auth_request *auth)
+{
+	struct md5_context ctx;
+	unsigned char digest[16];
+	const char *a1_hex, *a2_hex, *response_hex, *data;
+	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)
+		return FALSE;
+
+	digest_buf = buffer_create_data(data_stack_pool,
+					digest, sizeof(digest));
+	if (hex_to_binary(data, digest_buf) <= 0)
+		return FALSE;
+
+	/*
+	   response =
+	     HEX( KD ( HEX(H(A1)),
+		     { nonce-value, ":" nc-value, ":",
+		       cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
+
+	   and since we don't support authzid yet:
+
+	   A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
+		":", nonce-value, ":", cnonce-value }
+
+	   If the "qop" directive's value is "auth", then A2 is:
+	
+	      A2       = { "AUTHENTICATE:", digest-uri-value }
+	
+	   If the "qop" value is "auth-int" or "auth-conf" then A2 is:
+	
+	      A2       = { "AUTHENTICATE:", digest-uri-value,
+		       ":00000000000000000000000000000000" }
+	*/
+
+	/* A1 */
+	md5_init(&ctx);
+	md5_update(&ctx, digest, 16);
+	md5_update(&ctx, ":", 1);
+	md5_update(&ctx, auth->nonce, strlen(auth->nonce));
+	md5_update(&ctx, ":", 1);
+	md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
+	md5_final(&ctx, digest);
+	a1_hex = binary_to_hex(digest, 16);
+
+	/* do it twice, first verify the user's response, the second is
+	   sent for client as a reply */
+	for (i = 0; i < 2; i++) {
+		/* A2 */
+		md5_init(&ctx);
+		if (i == 0)
+			md5_update(&ctx, "AUTHENTICATE:", 13);
+		else
+			md5_update(&ctx, ":", 1);
+
+		if (auth->digest_uri != NULL) {
+			md5_update(&ctx, auth->digest_uri,
+				   strlen(auth->digest_uri));
+		}
+		if (auth->qop == QOP_AUTH_INT || auth->qop == QOP_AUTH_CONF) {
+			md5_update(&ctx, ":00000000000000000000000000000000",
+				   33);
+		}
+		md5_final(&ctx, digest);
+		a2_hex = binary_to_hex(digest, 16);
+
+		/* response */
+		md5_init(&ctx);
+		md5_update(&ctx, a1_hex, 32);
+		md5_update(&ctx, ":", 1);
+		md5_update(&ctx, auth->nonce, strlen(auth->nonce));
+		md5_update(&ctx, ":", 1);
+		md5_update(&ctx, auth->nonce_count, strlen(auth->nonce_count));
+		md5_update(&ctx, ":", 1);
+		md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
+		md5_update(&ctx, ":", 1);
+		md5_update(&ctx, auth->qop_value, strlen(auth->qop_value));
+		md5_update(&ctx, ":", 1);
+		md5_update(&ctx, a2_hex, 32);
+		md5_final(&ctx, digest);
+		response_hex = binary_to_hex(digest, 16);
+
+		if (i == 0) {
+			/* verify response */
+			if (memcmp(response_hex, auth->response, 32) != 0) {
+				if (verbose) {
+					i_info("digest-md5(%s): "
+					       "password mismatch",
+					       auth->username);
+				}
+				return FALSE;
+			}
+		} else {
+			auth->rspauth = p_strconcat(auth->pool, "rspauth=",
+						    response_hex, NULL);
+		}
+	}
+
+	return TRUE;
+}
+
+static int verify_realm(const char *realm)
+{
+	const char *const *tmp;
+
+	for (tmp = auth_realms; *tmp != NULL; tmp++) {
+		if (strcasecmp(realm, *tmp) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static int parse_next(char **data, char **key, char **value)
+{
+	/* @UNSAFE */
+	char *p, *dest;
+
+	p = *data;
+	while (IS_LWS(*p)) p++;
+
+	/* get key */
+	*key = p;
+	while (*p != '\0' && *p != '=' && *p != ',')
+		p++;
+
+	if (*p != '=') {
+		*data = p;
+		return FALSE;
+	}
+
+	*value = p+1;
+
+	/* skip trailing whitespace in key */
+	while (IS_LWS(p[-1]))
+		p--;
+	*p = '\0';
+
+	/* get value */
+	p = *value;
+	while (IS_LWS(*p)) p++;
+
+	if (*p != '"') {
+		while (*p != '\0' && *p != ',')
+			p++;
+
+		*data = p+1;
+		while (IS_LWS(p[-1]))
+			p--;
+		*p = '\0';
+	} else {
+		/* quoted string */
+		*value = dest = ++p;
+		while (*p != '\0' && *p != '"') {
+			if (*p == '\\' && p[1] != '\0')
+				p++;
+			*dest++ = *p++;
+		}
+
+		*data = *p == '"' ? p+1 : p;
+		*dest = '\0';
+	}
+
+	return TRUE;
+}
+
+/* remove leading and trailing whitespace */
+static const char *trim(const char *str)
+{
+	const char *ret;
+
+	while (IS_LWS(*str)) str++;
+	ret = str;
+
+	while (*str != '\0') str++;
+	if (str > ret) {
+		while (IS_LWS(str[-1])) str--;
+		ret = t_strdup_until(ret, str);
+	}
+
+	return ret;
+}
+
+static int auth_handle_response(struct digest_auth_request *auth, char *key, char *value,
+				const char **error)
+{
+	int i;
+
+	str_lcase(key);
+
+	if (strcmp(key, "realm") == 0) {
+		if (!verify_realm(value)) {
+			*error = "Invalid realm";
+			return FALSE;
+		}
+		if (auth->realm == NULL)
+			auth->realm = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "username") == 0) {
+		if (auth->username != NULL) {
+			*error = "username must not exist more than once";
+			return FALSE;
+		}
+
+		if (*value == '\0') {
+			*error = "empty username";
+			return FALSE;
+		}
+
+		auth->username = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "nonce") == 0) {
+		/* nonce must be same */
+		if (strcmp(value, auth->nonce) != 0) {
+			*error = "Invalid nonce";
+			return FALSE;
+		}
+
+		auth->nonce_found = TRUE;
+		return TRUE;
+	}
+
+	if (strcmp(key, "cnonce") == 0) {
+		if (auth->cnonce != NULL) {
+			*error = "cnonce must not exist more than once";
+			return FALSE;
+		}
+
+		if (*value == '\0') {
+			*error = "cnonce can't contain empty value";
+			return FALSE;
+		}
+
+		auth->cnonce = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "nonce-count") == 0) {
+		if (auth->nonce_count != NULL) {
+			*error = "nonce-count must not exist more than once";
+			return FALSE;
+		}
+
+		if (atoi(value) != 1) {
+			*error = "re-auth not supported currently";
+			return FALSE;
+		}
+
+		auth->nonce_count = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "qop") == 0) {
+		for (i = 0; i < QOP_COUNT; i++) {
+			if (strcasecmp(qop_names[i], value) == 0)
+				break;
+		}
+
+		if (i == QOP_COUNT) {
+			*error = "Unknown QoP value";
+			return FALSE;
+		}
+
+		auth->qop &= (1 << i);
+		if (auth->qop == 0) {
+			*error = "Nonallowed QoP requested";
+			return FALSE;
+		} 
+
+		auth->qop_value = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "digest-uri") == 0) {
+		/* type / host / serv-name */
+		const char *const *uri = t_strsplit(value, "/");
+
+		if (uri[0] == NULL || uri[1] == NULL) {
+			*error = "Invalid digest-uri";
+			return FALSE;
+		}
+
+		if (strcasecmp(trim(uri[0]), SERVICE_TYPE) != 0) {
+			*error = "Unexpected service type in digest-uri";
+			return FALSE;
+		}
+
+		/* FIXME: RFC recommends that we verify the host/serv-type.
+		   But isn't the realm enough already? That'd be just extra
+		   configuration.. Maybe optionally list valid hosts in
+		   config file? */
+		auth->digest_uri = p_strdup(auth->pool, value);
+		return TRUE;
+	}
+
+	if (strcmp(key, "maxbuf") == 0) {
+		if (auth->maxbuf != 0) {
+			*error = "maxbuf must not exist more than once";
+			return FALSE;
+		}
+
+		auth->maxbuf = strtoul(value, NULL, 10);
+		if (auth->maxbuf == 0) {
+			*error = "Invalid maxbuf value";
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	if (strcmp(key, "charset") == 0) {
+		if (strcasecmp(value, "utf-8") != 0) {
+			*error = "Only utf-8 charset is allowed";
+			return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	if (strcmp(key, "response") == 0) {
+		if (strlen(value) != 32) {
+			*error = "Invalid response value";
+			return FALSE;
+		}
+
+		memcpy(auth->response, value, 32);
+		return TRUE;
+	}
+
+	if (strcmp(key, "cipher") == 0) {
+		/* not supported, ignore */
+		return TRUE;
+	}
+
+	if (strcmp(key, "authzid") == 0) {
+		/* not supported, abort */
+		return FALSE;
+	}
+
+	/* unknown key, ignore */
+	return TRUE;
+}
+
+static int parse_digest_response(struct digest_auth_request *auth, const char *data,
+				 size_t size, const char **error)
+{
+	char *copy, *key, *value;
+	int failed;
+
+	/*
+	   realm="realm"
+	   username="username"
+	   nonce="randomized data"
+	   cnonce="??"
+	   nc=00000001
+	   qop="auth|auth-int|auth-conf"
+	   digest-uri="serv-type/host[/serv-name]"
+	   response=32 HEX digits
+	   maxbuf=number (with auth-int, auth-conf, defaults to 64k)
+	   charset="utf-8" (iso-8859-1 if it doesn't exist)
+	   cipher="cipher-value"
+	   authzid="authzid-value"
+	*/
+
+	t_push();
+
+	failed = FALSE;
+
+	copy = t_strdup_noconst(t_strndup(data, size));
+	while (*copy != '\0') {
+		if (parse_next(&copy, &key, &value)) {
+			if (!auth_handle_response(auth, key, value, error)) {
+				failed = TRUE;
+				break;
+			}
+		}
+
+		if (*copy == ',')
+			copy++;
+	}
+
+	if (!auth->nonce_found) {
+		*error = "Missing nonce parameter";
+		failed = TRUE;
+	} else if (auth->cnonce == NULL) {
+		*error = "Missing cnonce parameter";
+		failed = TRUE;
+	} else if (auth->username == NULL) {
+		*error = "Missing username parameter";
+		failed = TRUE;
+	}
+
+	if (auth->nonce_count == NULL)
+		auth->nonce_count = p_strdup(auth->pool, "00000001");
+	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 int
+mech_digest_md5_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 digest_auth_request *auth =
+		(struct digest_auth_request *)auth_request;
+	struct auth_login_reply reply;
+	const char *error;
+
+	/* initialize reply */
+	mech_init_login_reply(&reply);
+	reply.id = request->id;
+
+	if (auth->authenticated) {
+		/* authentication is done, we were just waiting the last
+		   word from client */
+		void *data;
+
+		data = mech_auth_success(&reply, auth_request, NULL, 0);
+		callback(&reply, data, conn);
+		return TRUE;
+	}
+
+	if (parse_digest_response(auth, (const char *) data,
+				  request->data_size, &error)) {
+		/* authentication ok */
+		auth->authenticated = TRUE;
+
+		reply.reply_idx = 0;
+
+		reply.result = AUTH_LOGIN_RESULT_CONTINUE;
+		reply.data_size = strlen(auth->rspauth);
+		callback(&reply, auth->rspauth, conn);
+		return TRUE;
+	}
+
+	if (error == NULL)
+                error = "Authentication failed";
+	else if (verbose)
+		i_info("digest-md5: %s", error);
+
+	/* failed */
+	reply.result = AUTH_LOGIN_RESULT_FAILURE;
+	reply.data_size = strlen(error)+1;
+	callback(&reply, error, conn);
+	return FALSE;
+}
+
+static void mech_digest_md5_auth_free(struct auth_request *auth_request)
+{
+	pool_unref(auth_request->pool);
+}
+
+static struct auth_request *
+mech_digest_md5_auth_new(struct login_connection *conn,
+			 unsigned int id, mech_callback_t *callback)
+{
+	struct auth_login_reply reply;
+	struct digest_auth_request *auth;
+	pool_t pool;
+	string_t *challenge;
+
+	pool = pool_alloconly_create("digest_md5_auth_request", 2048);
+	auth = p_new(pool, struct digest_auth_request, 1);
+	auth->pool = pool;
+
+	auth->auth_request.pool = pool;
+	auth->auth_request.auth_continue = mech_digest_md5_auth_continue;
+	auth->auth_request.auth_free = mech_digest_md5_auth_free;
+	auth->qop = QOP_AUTH;
+
+	/* initialize reply */
+	mech_init_login_reply(&reply);
+	reply.id = id;
+	reply.result = AUTH_LOGIN_RESULT_CONTINUE;
+
+	/* send the initial challenge */
+	reply.reply_idx = 0;
+	challenge = get_digest_challenge(auth);
+	reply.data_size = str_len(challenge);
+	callback(&reply, str_data(challenge), conn);
+
+	return &auth->auth_request;
+}
+
+struct mech_module mech_digest_md5 = {
+	AUTH_MECH_DIGEST_MD5,
+	mech_digest_md5_auth_new
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-plain.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "hash.h"
+#include "safe-memset.h"
+#include "mech.h"
+#include "passdb.h"
+
+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;
+
+	/* authorization ID \0 authentication ID \0 pass.
+	   we'll ignore authorization ID for now. */
+	authid = (const char *) data;
+	authenid = NULL; pass = NULL;
+
+	count = 0;
+	for (i = 0; i < request->data_size; i++) {
+		if (data[i] == '\0') {
+			if (++count == 1)
+				authenid = data + i+1;
+			else {
+				i++;
+				len = request->data_size - i;
+				pass = p_strndup(data_stack_pool, data+i, len);
+				break;
+			}
+		}
+	}
+
+	/* split and save user/realm */
+	auth_request->user = p_strdup(auth_request->pool, authenid);
+	auth_request->realm = strchr(auth_request->user, '@');
+	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;
+		}
+
+		/* make sure it's cleared */
+		safe_memset(pass, 0, strlen(pass));
+	}
+
+	callback(&reply, reply_data, conn);
+	return reply.result == AUTH_LOGIN_RESULT_SUCCESS;
+}
+
+static void
+mech_plain_auth_free(struct auth_request *auth_request)
+{
+	pool_unref(auth_request->pool);
+}
+
+static struct auth_request *
+mech_plain_auth_new(struct login_connection *conn, unsigned int id,
+		    mech_callback_t *callback)
+{
+        struct auth_request *auth_request;
+	struct auth_login_reply reply;
+	pool_t pool;
+
+	pool = pool_alloconly_create("plain_auth_request", 256);
+	auth_request = p_new(pool, struct auth_request, 1);
+	auth_request->pool = pool;
+	auth_request->auth_continue = mech_plain_auth_continue;
+        auth_request->auth_free = mech_plain_auth_free;
+
+	/* initialize reply */
+	memset(&reply, 0, sizeof(reply));
+	reply.id = id;
+	reply.result = AUTH_LOGIN_RESULT_CONTINUE;
+
+	callback(&reply, NULL, conn);
+	return auth_request;
+}
+
+struct mech_module mech_plain = {
+	AUTH_MECH_PLAIN,
+	mech_plain_auth_new
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,221 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "buffer.h"
+#include "hash.h"
+#include "mech.h"
+#include "login-connection.h"
+
+#include <stdlib.h>
+
+struct mech_module_list {
+	struct mech_module_list *next;
+
+	struct mech_module module;
+};
+
+enum auth_mech auth_mechanisms;
+const char *const *auth_realms;
+
+static int set_use_cyrus_sasl;
+static struct mech_module_list *mech_modules;
+static struct auth_login_reply failure_reply;
+
+void mech_register_module(struct mech_module *module)
+{
+	struct mech_module_list *list;
+
+	i_assert((auth_mechanisms & module->mech) == 0);
+
+	auth_mechanisms |= module->mech;
+
+	list = i_new(struct mech_module_list, 1);
+	list->module = *module;
+
+	list->next = mech_modules;
+	mech_modules = list;
+}
+
+void mech_unregister_module(struct mech_module *module)
+{
+	struct mech_module_list **pos, *list;
+
+	if ((auth_mechanisms & module->mech) == 0)
+		return; /* not registered */
+
+        auth_mechanisms &= ~module->mech;
+
+	for (pos = &mech_modules; *pos != NULL; pos = &(*pos)->next) {
+		if ((*pos)->module.mech == module->mech) {
+			list = *pos;
+			*pos = (*pos)->next;
+			i_free(list);
+			break;
+		}
+	}
+}
+
+void mech_request_new(struct login_connection *conn,
+		      struct auth_login_request_new *request,
+		      mech_callback_t *callback)
+{
+	struct mech_module_list *list;
+	struct auth_request *auth_request;
+
+	if ((auth_mechanisms & request->mech) == 0) {
+		/* unsupported mechanism */
+		i_error("BUG: imap-login requested unsupported "
+			"auth mechanism %d", request->mech);
+		failure_reply.id = request->id;
+		callback(&failure_reply, NULL, conn);
+		return;
+	}
+
+#ifdef USE_CYRUS_SASL2
+	if (set_use_cyrus_sasl) {
+		auth_request = mech_cyrus_sasl_new(conn, request, callback);
+	} else
+#endif
+	{
+		auth_request = NULL;
+
+		for (list = mech_modules; list != NULL; list = list->next) {
+			if (list->module.mech == request->mech) {
+				auth_request =
+					list->module.auth_new(conn, request->id,
+							      callback);
+				break;
+			}
+		}
+	}
+
+	if (auth_request != NULL) {
+		hash_insert(conn->auth_requests, POINTER_CAST(request->id),
+			    auth_request);
+	}
+}
+
+void mech_request_continue(struct login_connection *conn,
+			   struct auth_login_request_continue *request,
+			   const unsigned char *data,
+			   mech_callback_t *callback)
+{
+	struct auth_request *auth_request;
+
+	auth_request = hash_lookup(conn->auth_requests,
+				   POINTER_CAST(request->id));
+	if (auth_request == NULL) {
+		/* timeouted */
+		failure_reply.id = request->id;
+		callback(&failure_reply, NULL, conn);
+	} else {
+		if (!auth_request->auth_continue(conn, auth_request,
+						 request, data, callback))
+                        mech_request_free(conn, auth_request, request->id);
+	}
+}
+
+void mech_request_free(struct login_connection *conn,
+		       struct auth_request *auth_request, unsigned int id)
+{
+	auth_request->auth_free(auth_request);
+	hash_remove(conn->auth_requests, POINTER_CAST(id));
+}
+
+void mech_init_login_reply(struct auth_login_reply *reply)
+{
+	memset(reply, 0, sizeof(*reply));
+
+	reply->username_idx = (unsigned int)-1;
+	reply->realm_idx = (unsigned int)-1;
+	reply->reply_idx = (unsigned int)-1;
+}
+
+void *mech_auth_success(struct auth_login_reply *reply,
+			struct auth_request *auth_request,
+			const void *data, size_t data_size)
+{
+	buffer_t *buf;
+
+	buf = buffer_create_dynamic(data_stack_pool, 256, (size_t)-1);
+
+	reply->username_idx = 0;
+	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);
+
+	if (auth_request->realm == NULL)
+		reply->realm_idx = (size_t)-1;
+	else {
+		reply->realm_idx = buffer_get_used_size(buf);
+		buffer_append(buf, auth_request->realm,
+			      strlen(auth_request->realm)+1);
+	}
+
+	if (data_size == 0)
+		reply->reply_idx = (size_t)-1;
+	else {
+		reply->reply_idx = buffer_get_used_size(buf);
+		buffer_append(buf, data, data_size);
+	}
+
+	reply->result = AUTH_LOGIN_RESULT_SUCCESS;
+	reply->data_size = buffer_get_used_size(buf);
+	return buffer_get_modifyable_data(buf, NULL);
+}
+
+extern struct mech_module mech_plain;
+extern struct mech_module mech_digest_md5;
+
+void mech_init(void)
+{
+	const char *const *mechanisms;
+	const char *env;
+
+        mech_modules = NULL;
+	auth_mechanisms = 0;
+
+	memset(&failure_reply, 0, sizeof(failure_reply));
+	failure_reply.result = AUTH_LOGIN_RESULT_FAILURE;
+
+	/* register wanted mechanisms */
+	env = getenv("MECHANISMS");
+	if (env == NULL || *env == '\0')
+		i_fatal("MECHANISMS environment is unset");
+
+	mechanisms = t_strsplit(env, " ");
+	while (*mechanisms != NULL) {
+		if (strcasecmp(*mechanisms, "PLAIN") == 0)
+			mech_register_module(&mech_plain);
+		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
+			mech_register_module(&mech_digest_md5);
+		else {
+			i_fatal("Unknown authentication mechanism '%s'",
+				*mechanisms);
+		}
+
+		mechanisms++;
+	}
+
+	if (auth_mechanisms == 0)
+		i_fatal("No authentication mechanisms configured");
+
+	/* get our realm - note that we allocate from data stack so
+	   this function should never be called inside I/O loop or anywhere
+	   else where t_pop() is called */
+	env = getenv("REALMS");
+	if (env == NULL)
+		env = "";
+	auth_realms = t_strsplit(env, " ");
+
+	set_use_cyrus_sasl = getenv("USE_CYRUS_SASL") != NULL;
+
+#ifdef USE_CYRUS_SASL2
+	if (set_use_cyrus_sasl)
+		mech_cyrus_sasl_init_lib();
+#endif
+}
+
+void mech_deinit(void)
+{
+	mech_unregister_module(&mech_plain);
+	mech_unregister_module(&mech_digest_md5);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,61 @@
+#ifndef __MECH_H
+#define __MECH_H
+
+#include "auth-login-interface.h"
+
+struct login_connection;
+
+typedef void mech_callback_t(struct auth_login_reply *reply,
+			     const void *data, struct login_connection *conn);
+
+struct auth_request {
+	pool_t pool;
+	char *user, *realm;
+
+	int (*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);
+	void (*auth_free)(struct auth_request *auth_request);
+	/* ... mechanism specific data ... */
+};
+
+struct mech_module {
+	enum auth_mech mech;
+
+	struct auth_request *(*auth_new)(struct login_connection *conn,
+					 unsigned int id,
+					 mech_callback_t *callback);
+};
+
+extern enum auth_mech auth_mechanisms;
+extern const char *const *auth_realms;
+
+void mech_register_module(struct mech_module *module);
+void mech_unregister_module(struct mech_module *module);
+
+void mech_request_new(struct login_connection *conn,
+		      struct auth_login_request_new *request,
+		      mech_callback_t *callback);
+void mech_request_continue(struct login_connection *conn,
+			   struct auth_login_request_continue *request,
+			   const unsigned char *data,
+			   mech_callback_t *callback);
+void mech_request_free(struct login_connection *conn,
+		       struct auth_request *auth_request, unsigned int id);
+
+void mech_init_login_reply(struct auth_login_reply *reply);
+void *mech_auth_success(struct auth_login_reply *reply,
+			struct auth_request *auth_request,
+			const void *data, size_t data_size);
+
+void mech_cyrus_sasl_init_lib(void);
+struct auth_request *mech_cyrus_sasl_new(struct login_connection *conn,
+					 struct auth_login_request_new *request,
+					 mech_callback_t *callback);
+
+void mech_init(void);
+void mech_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-pam.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,253 @@
+/*
+   Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
+
+   You're allowed to do whatever you like with this software (including
+   re-distribution in source and/or binary form, with or without
+   modification), provided that credit is given where it is due and any
+   modified versions are marked as such.  There's absolutely no warranty.
+*/
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_PAM
+
+#include "common.h"
+#include "passdb.h"
+#include "mycrypt.h"
+#include "safe-memset.h"
+
+#include <stdlib.h>
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#  include <security/pam_appl.h>
+#elif defined(HAVE_PAM_PAM_APPL_H)
+#  include <pam/pam_appl.h>
+#endif
+
+#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM)
+/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
+   Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
+   so I thought this might work better. */
+#  define linux_const
+#else
+#  define linux_const			const
+#endif
+typedef linux_const void *pam_item_t;
+
+#ifdef AUTH_PAM_USERPASS
+#  include <security/pam_client.h>
+
+#  ifndef PAM_BP_RCONTROL
+/* Linux-PAM prior to 0.74 */
+#    define PAM_BP_RCONTROL	PAM_BP_CONTROL
+#    define PAM_BP_WDATA	PAM_BP_DATA
+#    define PAM_BP_RDATA	PAM_BP_DATA
+#  endif
+
+#  define USERPASS_AGENT_ID		"userpass"
+#  define USERPASS_AGENT_ID_LENGTH	8
+
+#  define USERPASS_USER_MASK		0x03
+#  define USERPASS_USER_REQUIRED	1
+#  define USERPASS_USER_KNOWN		2
+#  define USERPASS_USER_FIXED		3
+#endif
+
+struct pam_userpass {
+	const char *user;
+	const char *pass;
+};
+
+static char *service_name;
+
+static int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
+	struct pam_response **resp, void *appdata_ptr)
+{
+	/* @UNSAFE */
+	struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
+#ifdef AUTH_PAM_USERPASS
+	pamc_bp_t prompt;
+	const char *input;
+	char *output;
+	char flags;
+	size_t userlen, passlen;
+
+	if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
+		return PAM_CONV_ERR;
+
+	prompt = (pamc_bp_t)msg[0]->msg;
+	input = PAM_BP_RDATA(prompt);
+
+	if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
+	    strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
+		return PAM_CONV_ERR;
+
+	flags = input[USERPASS_AGENT_ID_LENGTH + 1];
+	input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
+
+	if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
+	    strcmp(input, userpass->user))
+		return PAM_CONV_AGAIN;
+
+	if (!(*resp = malloc(sizeof(struct pam_response))))
+		return PAM_CONV_ERR;
+
+	userlen = strlen(userpass->user);
+	passlen = strlen(userpass->pass);
+
+	prompt = NULL;
+	PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
+	output = PAM_BP_WDATA(prompt);
+
+	memcpy(output, userpass->user, userlen + 1);
+	memcpy(output + userlen + 1, userpass->pass, passlen);
+
+	(*resp)[0].resp_retcode = 0;
+	(*resp)[0].resp = (char *)prompt;
+#else
+	char *string;
+	int i;
+
+	if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
+		return PAM_CONV_ERR;
+
+	for (i = 0; i < num_msg; i++) {
+		switch (msg[i]->msg_style) {
+		case PAM_PROMPT_ECHO_ON:
+			string = strdup(userpass->user);
+			if (string == NULL)
+				i_fatal("Out of memory");
+			break;
+		case PAM_PROMPT_ECHO_OFF:
+			string = strdup(userpass->pass);
+			if (string == NULL)
+				i_fatal("Out of memory");
+			break;
+		case PAM_ERROR_MSG:
+		case PAM_TEXT_INFO:
+			string = NULL;
+			break;
+		default:
+			while (--i >= 0) {
+				if ((*resp)[i].resp == NULL)
+					continue;
+				safe_memset((*resp)[i].resp, 0,
+					    strlen((*resp)[i].resp));
+				free((*resp)[i].resp);
+				(*resp)[i].resp = NULL;
+			}
+
+			free(*resp);
+			*resp = NULL;
+
+			return PAM_CONV_ERR;
+		}
+
+		(*resp)[i].resp_retcode = PAM_SUCCESS;
+		(*resp)[i].resp = string;
+	}
+#endif
+
+	return PAM_SUCCESS;
+}
+
+static int pam_auth(pam_handle_t *pamh, const char *user)
+{
+	char *item;
+	int status;
+
+	if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
+		if (verbose) {
+			i_info("PAM: pam_authenticate(%s) failed: %s",
+			       user, pam_strerror(pamh, status));
+		}
+		return status;
+	}
+
+#ifdef HAVE_PAM_SETCRED
+	if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+		if (verbose) {
+			i_info("PAM: pam_setcred(%s) failed: %s",
+			       user, pam_strerror(pamh, status));
+		}
+		return status;
+	}
+#endif
+
+	if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
+		if (verbose) {
+			i_info("PAM: pam_acct_mgmt(%s) failed: %s",
+			       user, pam_strerror(pamh, status));
+		}
+		return status;
+	}
+
+	status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
+	if (status != PAM_SUCCESS) {
+		if (verbose) {
+			i_info("PAM: pam_get_item(%s) failed: %s",
+			       user, pam_strerror(pamh, status));
+		}
+		return status;
+	}
+
+	return PAM_SUCCESS;
+}
+
+static enum passdb_result
+pam_verify_plain(const char *user, const char *realm, const char *password)
+{
+	pam_handle_t *pamh;
+	struct pam_userpass userpass;
+	struct pam_conv conv;
+	int status, status2;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+
+	conv.conv = pam_userpass_conv;
+	conv.appdata_ptr = &userpass;
+
+	userpass.user = user;
+	userpass.pass = password;
+
+	status = pam_start(service_name, user, &conv, &pamh);
+	if (status != PAM_SUCCESS) {
+		if (verbose) {
+			i_info("PAM: pam_start(%s) failed: %s",
+			       user, pam_strerror(pamh, status));
+		}
+		return PASSDB_RESULT_INTERNAL_FAILURE;
+	}
+
+	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;
+	}
+
+	/* FIXME: check for PASSDB_RESULT_UNKNOWN_USER somehow */
+	return status == PAM_SUCCESS ? PASSDB_RESULT_OK :
+		PASSDB_RESULT_PASSWORD_MISMATCH;
+}
+
+static void pam_init(const char *args)
+{
+	service_name = i_strdup(*args != '\0' ? args : "imap");
+}
+
+static void pam_deinit(void)
+{
+	i_free(service_name);
+}
+
+struct passdb_module passdb_pam = {
+	pam_init,
+	pam_deinit,
+
+	pam_verify_plain,
+	NULL
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-passwd-file.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_PASSWD_FILE
+
+#include "common.h"
+#include "passdb.h"
+#include "passwd-file.h"
+
+#include "hex-binary.h"
+#include "md5.h"
+#include "mycrypt.h"
+
+struct passwd_file *passdb_pwf = NULL;
+
+static enum passdb_result
+passwd_file_verify_plain(const char *user, const char *realm,
+			 const char *password)
+{
+	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;
+
+	switch (pu->password_type) {
+	case PASSWORD_NONE:
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+
+	case PASSWORD_DES:
+		if (strcmp(mycrypt(password, pu->password), pu->password) == 0)
+			return PASSDB_RESULT_OK;
+
+		if (verbose) {
+			i_info("passwd-file(%s): DES password mismatch",
+			       pu->user_realm);
+		}
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+
+	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 (verbose) {
+			i_info("passwd-file(%s): MD5 password mismatch",
+			       pu->user_realm);
+		}
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+
+	case PASSWORD_DIGEST_MD5:
+		/* user:realm:passwd */
+		str = t_strconcat(t_strcut(pu->user_realm, '@'), ":",
+				  pu->realm == NULL ? "" : pu->realm,  ":",
+				  password, NULL);
+
+		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 (verbose) {
+			i_info("passwd-file(%s): DIGEST-MD5 password mismatch",
+			       pu->user_realm);
+		}
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+	}
+
+	i_unreached();
+}
+
+static const char *
+passwd_file_lookup_credentials(const char *user, const char *realm,
+			       enum passdb_credentials credentials)
+{
+	struct passwd_user *pu;
+
+	pu = passwd_file_lookup_user(passdb_pwf, user, realm);
+	if (pu == NULL)
+		return NULL;
+
+	if (pu->password_type == PASSWORD_NONE) {
+		if (verbose)
+			i_info("passwd-file(%s): No password", pu->user_realm);
+		return NULL;
+	}
+
+	switch (credentials) {
+	case PASSDB_CREDENTIALS_DIGEST_MD5:
+		if (pu->password_type == PASSWORD_DIGEST_MD5)
+			return pu->password;
+
+		if (verbose) {
+			i_info("passwd-file(%s): No DIGEST-MD5 password",
+			       pu->user_realm);
+		}
+		return NULL;
+	default:
+		if (verbose) {
+			i_info("passwd-file(%s): Unsupported credentials %u",
+			       pu->user_realm, (unsigned int)credentials);
+		}
+		return NULL;
+	}
+}
+
+static void passwd_file_init(const char *args)
+{
+	if (userdb_pwf != NULL && strcmp(userdb_pwf->path, args) == 0) {
+		passdb_pwf = userdb_pwf;
+                passdb_pwf->refcount++;
+	} else {
+		passdb_pwf = passwd_file_parse(args);
+	}
+}
+
+static void passwd_file_deinit(void)
+{
+	passwd_file_unref(passdb_pwf);
+}
+
+struct passdb_module passdb_passwd_file = {
+	passwd_file_init,
+	passwd_file_deinit,
+
+	passwd_file_verify_plain,
+	passwd_file_lookup_credentials
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-passwd.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_PASSWD
+
+#include "common.h"
+#include "safe-memset.h"
+#include "passdb.h"
+#include "mycrypt.h"
+
+#include <pwd.h>
+
+static enum passdb_result
+passwd_verify_plain(const char *user, const char *realm, const char *password)
+{
+	struct passwd *pw;
+	int result;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+	pw = getpwnam(user);
+	if (pw == NULL) {
+		if (errno != 0)
+			i_error("getpwnam(%s) failed: %m", user);
+		else if (verbose)
+			i_info("passwd(%s): unknown user", user);
+		return PASSDB_RESULT_USER_UNKNOWN;
+	}
+
+	if (!IS_VALID_PASSWD(pw->pw_passwd)) {
+		if (verbose) {
+			i_info("passwd(%s): invalid password field '%s'",
+			       user, pw->pw_passwd);
+		}
+		return PASSDB_RESULT_USER_DISABLED;
+	}
+
+	/* check if the password is valid */
+	result = strcmp(mycrypt(password, pw->pw_passwd), pw->pw_passwd) == 0;
+
+	/* clear the passwords from memory */
+	safe_memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
+
+	if (!result) {
+		if (verbose)
+			i_info("passwd(%s): password mismatch", user);
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+	}
+
+	return PASSDB_RESULT_OK;
+}
+
+static void passwd_deinit(void)
+{
+	endpwent();
+}
+
+struct passdb_module passdb_passwd = {
+	NULL,
+	passwd_deinit,
+
+	passwd_verify_plain,
+	NULL
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-shadow.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_PASSWD
+
+#include "common.h"
+#include "safe-memset.h"
+#include "passdb.h"
+#include "mycrypt.h"
+
+#include <shadow.h>
+
+static enum passdb_result
+shadow_verify_plain(const char *user, const char *realm, const char *password)
+{
+	struct spwd *spw;
+	int result;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+	spw = getspnam(user);
+	if (spw == NULL) {
+		if (errno != 0)
+			i_error("getspnam(%s) failed: %m", user);
+		else if (verbose)
+			i_info("shadow(%s): unknown user", user);
+		return PASSDB_RESULT_USER_UNKNOWN;
+	}
+
+	if (!IS_VALID_PASSWD(spw->sp_pwdp)) {
+		if (verbose) {
+			i_info("shadow(%s): invalid password field '%s'",
+			       user, spw->sp_pwdp);
+		}
+		return PASSDB_RESULT_USER_DISABLED;
+	}
+
+	/* check if the password is valid */
+	result = strcmp(mycrypt(password, spw->sp_pwdp), spw->sp_pwdp) == 0;
+
+	/* clear the passwords from memory */
+	safe_memset(spw->sp_pwdp, 0, strlen(spw->sp_pwdp));
+
+	if (!result) {
+		if (verbose)
+			i_info("shadow(%s): password mismatch", user);
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+	}
+
+	return PASSDB_RESULT_OK;
+}
+
+static void shadow_deinit(void)
+{
+        endspent();
+}
+
+struct passdb_module passdb_shadow = {
+	NULL,
+	shadow_deinit,
+
+	shadow_verify_plain,
+	NULL
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb-vpopmail.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,60 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+/* Thanks to Courier-IMAP for showing how the vpopmail API should be used */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef PASSDB_VPOPMAIL
+
+#include "common.h"
+#include "safe-memset.h"
+#include "passdb.h"
+#include "mycrypt.h"
+
+#include "userdb-vpopmail.h"
+
+static enum passdb_result
+vpopmail_verify_plain(const char *user, const char *realm, const char *password)
+{
+	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->pw_gid & NO_IMAP) != 0) {
+		if (verbose)
+			i_info("vpopmail(%s): IMAP disabled", user);
+		return PASSDB_RESULT_USER_DISABLED;
+	}
+
+	/* verify password */
+	result = strcmp(mycrypt(password, vpw->pw_passwd), vpw->pw_passwd) == 0;
+	safe_memset(vpw->pw_passwd, 0, strlen(vpw->pw_passwd));
+
+	if (!result) {
+		if (verbose)
+			i_info("vpopmail(%s): password mismatch", user);
+		return PASSDB_RESULT_PASSWORD_MISMATCH;
+	}
+
+	return PASSDB_RESULT_OK;
+}
+
+static void vpopmail_deinit(void)
+{
+	vclose();
+}
+
+struct passdb_module passdb_vpopmail = {
+	NULL,
+	vpopmail_deinit,
+
+	vpopmail_verify_plain,
+	NULL
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "common.h"
+#include "mech.h"
+#include "passdb.h"
+
+#include <stdlib.h>
+
+struct passdb_module *passdb;
+
+const char *passdb_credentials_to_str(enum passdb_credentials credentials)
+{
+	switch (credentials) {
+	case PASSDB_CREDENTIALS_PLAINTEXT:
+		return "plaintext";
+	case PASSDB_CREDENTIALS_DIGEST_MD5:
+		return "digest-md5";
+	}
+
+	return "??";
+}
+
+void passdb_init(void)
+{
+	const char *name, *args;
+
+	passdb = NULL;
+
+	name = getenv("PASSDB");
+	if (name == NULL)
+		i_fatal("PASSDB environment is unset");
+
+#ifdef PASSDB_PASSWD
+	if (strcasecmp(name, "passwd") == 0)
+		passdb = &passdb_passwd;
+#endif
+#ifdef PASSDB_PASSWD_FILE
+	if (strcasecmp(name, "passwd-file") == 0)
+		passdb = &passdb_passwd_file;
+#endif
+#ifdef PASSDB_PAM
+	if (strcasecmp(name, "pam") == 0)
+		passdb = &passdb_pam;
+#endif
+#ifdef PASSDB_SHADOW
+	if (strcasecmp(name, "shadow") == 0)
+		passdb = &passdb_shadow;
+#endif
+#ifdef PASSDB_VPOPMAIL
+	if (strcasecmp(name, "vpopmail") == 0)
+		passdb = &passdb_vpopmail;
+#endif
+
+	if (passdb == NULL)
+		i_fatal("Unknown passdb type '%s'", name);
+
+	/* initialize */
+	if (passdb->init != NULL) {
+		args = getenv("PASSDB_ARGS");
+		if (args == NULL) args = "";
+
+		passdb->init(args);
+	}
+
+	if ((auth_mechanisms & AUTH_MECH_PLAIN) &&
+	    passdb->verify_plain == NULL)
+		i_fatal("Passdb %s doesn't support PLAIN method", name);
+
+	if ((auth_mechanisms & AUTH_MECH_DIGEST_MD5) &&
+	    passdb->lookup_credentials == NULL)
+		i_fatal("Passdb %s doesn't support DIGEST-MD5 method", name);
+}
+
+void passdb_deinit(void)
+{
+	if (passdb != NULL && passdb->deinit != NULL)
+		passdb->deinit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passdb.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,48 @@
+#ifndef __PASSDB_H
+#define __PASSDB_H
+
+#define IS_VALID_PASSWD(pass) \
+	((pass)[0] != '\0' && (pass)[0] != '*' && (pass)[0] != '!')
+
+enum passdb_credentials {
+	PASSDB_CREDENTIALS_PLAINTEXT,
+	PASSDB_CREDENTIALS_DIGEST_MD5
+};
+
+enum passdb_result {
+	PASSDB_RESULT_USER_UNKNOWN = -1,
+	PASSDB_RESULT_USER_DISABLED = -2,
+	PASSDB_RESULT_INTERNAL_FAILURE = -3,
+
+	PASSDB_RESULT_PASSWORD_MISMATCH = 0,
+	PASSDB_RESULT_OK = 1,
+};
+
+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);
+
+	/* 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);
+};
+
+const char *passdb_credentials_to_str(enum passdb_credentials credentials);
+
+extern struct passdb_module *passdb;
+
+extern struct passdb_module passdb_passwd;
+extern struct passdb_module passdb_shadow;
+extern struct passdb_module passdb_passwd_file;
+extern struct passdb_module passdb_pam;
+extern struct passdb_module passdb_vpopmail;
+
+void passdb_init(void);
+void passdb_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passwd-file.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,226 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#if defined (USERDB_PASSWD_FILE) || defined(PASSDB_PASSWD_FILE)
+
+#include "common.h"
+#include "userdb.h"
+#include "passwd-file.h"
+
+#include "buffer.h"
+#include "istream.h"
+#include "hash.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static void passwd_file_add(struct passwd_file *pw, const char *username,
+			    const char *pass, const char *const *args)
+{
+	/* args = uid, gid, user info, home dir, shell, mail, chroot */
+	struct passwd_user *pu;
+	const char *p;
+
+	if (hash_lookup(pw->users, username) != NULL) {
+		i_error("User %s already exists in password file %s",
+			username, pw->path);
+		return;
+	}
+
+	pu = p_new(pw->pool, struct passwd_user, 1);
+	pu->user_realm = p_strdup(pw->pool, username);
+
+	p = pass == NULL ? NULL : strchr(pass, '[');
+	if (p == NULL) {
+		pu->password = p_strdup(pw->pool, pass);
+		pu->password_type = pass == NULL ? PASSWORD_NONE : PASSWORD_DES;
+	} else {
+		/* password[type] - we're being libpam-pwdfile compatible
+		   here. it uses 13 = DES and 34 = MD5. We add
+		   56 = Digest-MD5. */
+		pu->password = p_strdup_until(pw->pool, pass, p);
+		if (p[1] == '3' && p[2] == '4') {
+			pu->password_type = PASSWORD_MD5;
+			str_lcase(pu->password);
+		} else if (p[1] == '5' && p[2] == '6') {
+			pu->password_type = PASSWORD_DIGEST_MD5;
+			if (strlen(pu->password) != 32) {
+				i_error("User %s has invalid password in "
+					"file %s", username, pw->path);
+				return;
+			}
+			str_lcase(pu->password);
+		} else {
+			pu->password_type = PASSWORD_DES;
+		}
+	}
+
+	if (*args != NULL) {
+		pu->uid = atoi(*args);
+		if (pu->uid == 0) {
+			i_error("User %s has UID 0 in password file %s",
+				username, pw->path);
+			return;
+		}
+		args++;
+	}
+
+	if (*args != NULL) {
+		pu->gid = atoi(*args);
+		if (pu->gid == 0) {
+			i_error("User %s has GID 0 in password file %s",
+				username, pw->path);
+			return;
+		}
+		args++;
+	}
+
+	/* user info */
+	if (*args != NULL)
+		args++;
+
+	/* home */
+	if (*args != NULL) {
+		pu->home = p_strdup(pw->pool, *args);
+		args++;
+	}
+
+	/* shell */
+	if (*args != NULL)
+		args++;
+
+	/* mail storage */
+	if (*args != NULL) {
+		pu->mail = p_strdup(pw->pool, *args);
+		args++;
+	}
+
+	/* chroot */
+	if (*args != NULL && strstr(*args, "chroot") != NULL)
+		pu->chroot = TRUE;
+
+	hash_insert(pw->users, pu->user_realm, pu);
+}
+
+static void passwd_file_open(struct passwd_file *pw)
+{
+	struct istream *input;
+	const char *const *args;
+	const char *line;
+	struct stat st;
+	int fd;
+
+	fd = open(pw->path, O_RDONLY);
+	if (fd == -1)
+		i_fatal("Can't open passwd-file %s: %m", pw->path);
+
+	if (fstat(fd, &st) != 0)
+		i_fatal("fstat() failed for passwd-file %s: %m", pw->path);
+
+	pw->fd = fd;
+	pw->stamp = st.st_mtime;
+
+	pw->pool = pool_alloconly_create("passwd_file", 10240);;
+	pw->users = hash_create(default_pool, pw->pool, 100,
+				str_hash, (hash_cmp_callback_t)strcmp);
+
+	input = i_stream_create_file(pw->fd, default_pool, 4096, FALSE);
+	for (;;) {
+		line = i_stream_next_line(input);
+		if (line == NULL) {
+			if (i_stream_read(input) <= 0)
+				break;
+                        continue;
+		}
+
+		if (*line == '\0' || *line == ':')
+			continue; /* no username */
+
+		t_push();
+		args = t_strsplit(line, ":");
+		if (args[1] != NULL) {
+			/* at least two fields */
+			passwd_file_add(pw, args[0], args[1], args+2);
+		}
+		t_pop();
+	}
+	i_stream_unref(input);
+}
+
+static void passwd_file_close(struct passwd_file *pw)
+{
+	if (pw->fd != -1) {
+		if (close(pw->fd) < 0)
+			i_error("close(passwd_file) failed: %m");
+		pw->fd = -1;
+	}
+
+	if (pw->users != NULL) {
+		hash_destroy(pw->users);
+		pw->users = NULL;
+	}
+	if (pw->pool != NULL) {
+		pool_unref(pw->pool);
+		pw->pool = NULL;
+	}
+}
+
+struct passwd_file *passwd_file_parse(const char *path)
+{
+	struct passwd_file *pw;
+
+	pw = i_new(struct passwd_file, 1);
+	pw->refcount = 1;
+	pw->path = i_strdup(path);
+
+	passwd_file_open(pw);
+	return pw;
+}
+
+void passwd_file_unref(struct passwd_file *pw)
+{
+	if (--pw->refcount == 0) {
+		passwd_file_close(pw);
+		i_free(pw->path);
+		i_free(pw);
+	}
+}
+
+static void passwd_file_sync(struct passwd_file *pw)
+{
+	struct stat st;
+
+	if (stat(pw->path, &st) < 0)
+		i_fatal("stat() failed for %s: %m", pw->path);
+
+	if (st.st_mtime != pw->stamp) {
+		passwd_file_close(pw);
+		passwd_file_open(pw);
+	}
+}
+
+struct passwd_user *
+passwd_file_lookup_user(struct passwd_file *pw,
+			const char *user, const char *realm)
+{
+	struct passwd_user *pu;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+
+	passwd_file_sync(pw);
+
+	pu = hash_lookup(pw->users, user);
+	if (pu == NULL) {
+		if (verbose)
+			i_info("passwd-file(%s): unknown user", user);
+	}
+
+	return pu;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/passwd-file.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,48 @@
+#ifndef __PASSWD_FILE_H
+#define __PASSWD_FILE_H
+
+enum password_type {
+	PASSWORD_NONE,
+	PASSWORD_DES,
+	PASSWORD_MD5,
+	PASSWORD_DIGEST_MD5
+};
+
+struct passwd_user {
+	char *user_realm; /* user@realm */
+	const char *realm; /* NULL or points to user_realm */
+
+	uid_t uid;
+	gid_t gid;
+
+	char *home;
+	char *mail;
+
+	enum password_type password_type;
+	char *password;
+
+	unsigned int chroot:1;
+};
+
+struct passwd_file {
+	int refcount;
+	pool_t pool;
+
+	char *path;
+	time_t stamp;
+	int fd;
+
+	struct hash_table *users;
+};
+
+extern struct passwd_file *userdb_pwf;
+extern struct passwd_file *passdb_pwf;
+
+struct passwd_user *
+passwd_file_lookup_user(struct passwd_file *pw,
+			const char *user, const char *realm);
+
+struct passwd_file *passwd_file_parse(const char *path);
+void passwd_file_unref(struct passwd_file *pw);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb-passwd-file.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef USERDB_PASSWD_FILE
+
+#include "common.h"
+#include "userdb.h"
+#include "passwd-file.h"
+
+struct passwd_file *userdb_pwf = NULL;
+
+static struct user_data *passwd_file_lookup(const char *user, const char *realm)
+{
+	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;
+
+	pool = pool_alloconly_create("user_data", 512);
+	data = p_new(pool, struct user_data, 1);
+	data->pool = pool;
+
+	data->uid = pu->uid;
+	data->gid = pu->gid;
+
+	data->virtual_user = realm == NULL ? p_strdup(data->pool, user) :
+		p_strconcat(data->pool, user, "@", realm, NULL);
+	data->home = p_strdup(data->pool, pu->home);
+	data->mail = p_strdup(data->pool, pu->mail);
+
+	data->chroot = pu->chroot;
+	return data;
+}
+
+static void passwd_file_init(const char *args)
+{
+	if (passdb_pwf != NULL && strcmp(passdb_pwf->path, args) == 0) {
+		userdb_pwf = passdb_pwf;
+                userdb_pwf->refcount++;
+	} else {
+		userdb_pwf = passwd_file_parse(args);
+	}
+}
+
+static void passwd_file_deinit(void)
+{
+	passwd_file_unref(userdb_pwf);
+}
+
+struct userdb_module userdb_passwd_file = {
+	passwd_file_init,
+	passwd_file_deinit,
+
+	passwd_file_lookup
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb-passwd.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef USERDB_PASSWD
+
+#include "common.h"
+#include "userdb.h"
+
+#include <pwd.h>
+
+static struct user_data *passwd_lookup(const char *user, const char *realm)
+{
+	struct user_data *data;
+	struct passwd *pw;
+	pool_t pool;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+	pw = getpwnam(user);
+	if (pw == NULL) {
+		if (errno != 0)
+			i_error("getpwnam(%s) failed: %m", user);
+		else if (verbose)
+			i_info("passwd(%s): unknown user", user);
+		return NULL;
+	}
+
+	pool = pool_alloconly_create("user_data", 512);
+	data = p_new(pool, struct user_data, 1);
+	data->pool = pool;
+
+	data->uid = pw->pw_uid;
+	data->gid = pw->pw_gid;
+
+	data->system_user = p_strdup(data->pool, pw->pw_name);
+	data->virtual_user = data->system_user;
+	data->home = p_strdup(data->pool, pw->pw_dir);
+
+	return data;
+}
+
+struct userdb_module userdb_passwd = {
+	NULL, NULL,
+	passwd_lookup
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb-static.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,89 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#ifdef USERDB_STATIC
+
+#include "common.h"
+#include "str.h"
+#include "var-expand.h"
+#include "userdb.h"
+
+#include <stdlib.h>
+
+static uid_t static_uid;
+static gid_t static_gid;
+static char *static_home_template;
+
+static struct user_data *static_lookup(const char *user, const char *realm)
+{
+	struct user_data *data;
+	pool_t pool;
+	string_t *str;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+
+	pool = pool_alloconly_create("user_data", 512);
+	data = p_new(pool, struct user_data, 1);
+	data->pool = pool;
+
+	data->uid = static_uid;
+	data->gid = static_gid;
+
+	data->system_user = p_strdup(data->pool, user);
+	data->virtual_user = data->system_user;
+
+	str = t_str_new(256);
+	var_expand(str, static_home_template, user, NULL);
+	data->home = p_strdup(data->pool, str_c(str));
+
+	return data;
+}
+
+static void static_init(const char *args)
+{
+	const char *const *tmp;
+
+	static_uid = 0;
+	static_gid = 0;
+	static_home_template = NULL;
+
+	for (tmp = t_strsplit(args, " "); *tmp != NULL; tmp++) {
+		if (**tmp == '\0')
+			continue;
+
+		if (strncasecmp(*tmp, "uid=", 4) == 0)
+			static_uid = atoi(*tmp + 4);
+		else if (strncasecmp(*tmp, "gid=", 4) == 0)
+			static_gid = atoi(*tmp + 4);
+		else if (strncasecmp(*tmp, "home=", 5) == 0) {
+			i_free(static_home_template);
+			static_home_template = i_strdup(*tmp + 5);
+		} else {
+			i_fatal("Invalid static userdb option: '%s'", *tmp);
+		}
+	}
+
+	if (static_uid == 0)
+		i_fatal("static userdb: uid missing");
+	if (static_gid == 0)
+		i_fatal("static userdb: gid missing");
+	if (static_home_template == NULL)
+		i_fatal("static userdb: home option missing");
+}
+
+static void static_deinit(void)
+{
+	i_free(static_home_template);
+}
+
+struct userdb_module userdb_static = {
+	static_init,
+	static_deinit,
+
+	static_lookup
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb-vpopmail.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,117 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+/* Thanks to Courier-IMAP for showing how the vpopmail API should be used */
+
+#include "config.h"
+#undef HAVE_CONFIG_H
+
+#if defined(PASSDB_VPOPMAIL) || defined(USERDB_VPOPMAIL)
+
+#include "common.h"
+#include "userdb.h"
+#include "userdb-vpopmail.h"
+
+struct vqpasswd *vpopmail_lookup_vqp(const char *user, const char *realm,
+				     char vpop_user[VPOPMAIL_LIMIT],
+				     char vpop_domain[VPOPMAIL_LIMIT])
+{
+	struct vqpasswd *vpw;
+
+	if (realm != NULL) {
+		if (strlen(user) >= VPOPMAIL_LIMIT ||
+		    strlen(realm) >= VPOPMAIL_LIMIT)
+			return NULL;
+	} else {
+		/* vpop_user must be zero-filled or parse_email() leaves an
+		   extra character after the user name. we'll fill vpop_domain
+		   as well just to be sure... */
+		memset(vpop_user, '\0', VPOPMAIL_LIMIT);
+		memset(vpop_domain, '\0', VPOPMAIL_LIMIT);
+
+		if (parse_email(t_strdup_noconst(user), vpop_user, vpop_domain,
+				VPOPMAIL_LIMIT-1) < 0) {
+			if (verbose) {
+				i_info("vpopmail(%s): parse_email() failed",
+				       user);
+			}
+			return NULL;
+		}
+	}
+
+	vpw = vauth_getpw(vpop_user, vpop_domain);
+	if (vpw == NULL) {
+		if (verbose)
+			i_info("vpopmail(%s): unknown user", user);
+		return NULL;
+	}
+
+	return vpw;
+}
+
+#ifdef USERDB_VPOPMAIL
+
+static struct user_data *vpopmail_lookup(const char *user, const char *realm)
+{
+	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
+	struct vqpasswd *vpw;
+        struct user_data *data;
+	uid_t uid;
+	gid_t gid;
+	pool_t pool;
+
+	if (realm != NULL)
+		user = t_strconcat(user, "@", realm, NULL);
+
+	vpw = vpopmail_lookup_vqp(user, realm, vpop_user, vpop_domain);
+	if (vpw == NULL)
+		return NULL;
+
+	/* we have to get uid/gid separately, because the gid field in
+	   struct vqpasswd isn't really gid at all but just some flags... */
+	if (vget_assign(vpop_domain, NULL, 0, &uid, &gid) == NULL) {
+		if (verbose) {
+			i_info("vpopmail(%s): vget_assign(%s) failed",
+			       user, vpop_domain);
+		}
+		return NULL;
+	}
+
+	if (vpw->pw_dir == NULL || vpw->pw_dir[0] == '\0') {
+		/* user's homedir doesn't exist yet, create it */
+		if (verbose) {
+			i_info("vpopmail(%s): pw_dir isn't set, creating",
+			       user);
+		}
+
+		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;
+		}
+
+		/* get the user again so pw_dir is visible */
+		vpw = vauth_getpw(vpop_user, vpop_domain);
+		if (vpw == NULL)
+			return NULL;
+	}
+
+	pool = pool_alloconly_create("user_data", 1024);
+	data = p_new(pool, struct user_data, 1);
+	data->pool = pool;
+
+	data->uid = uid;
+	data->gid = gid;
+
+	data->virtual_user = p_strdup(data->pool, vpw->pw_name);
+	data->home = p_strdup(data->pool, vpw->pw_dir);
+
+	return data;
+}
+
+struct userdb_module userdb_vpopmail = {
+	NULL, NULL,
+	vpopmail_lookup
+};
+
+#endif
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb-vpopmail.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,17 @@
+#ifndef __USERDB_VPOPMAIL_H
+#define __USERDB_VPOPMAIL_H
+
+#include <stdio.h>
+#include <vpopmail.h>
+#include <vauth.h>
+
+/* Limit user and domain to 80 chars each (+1 for \0). I wouldn't recommend
+   raising this limit at least much, vpopmail is full of potential buffer
+   overflows. */
+#define VPOPMAIL_LIMIT 81
+
+struct vqpasswd *vpopmail_lookup_vqp(const char *user, const char *realm,
+				     char vpop_user[VPOPMAIL_LIMIT],
+				     char vpop_domain[VPOPMAIL_LIMIT]);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb.c	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "common.h"
+#include "userdb.h"
+
+#include <stdlib.h>
+
+struct userdb_module *userdb;
+
+void userdb_init(void)
+{
+	const char *name, *args;
+
+	userdb = NULL;
+
+	name = getenv("USERDB");
+	if (name == NULL)
+		i_fatal("USERDB environment is unset");
+
+#ifdef USERDB_PASSWD
+	if (strcasecmp(name, "passwd") == 0)
+		userdb = &userdb_passwd;
+#endif
+#ifdef USERDB_PASSWD_FILE
+	if (strcasecmp(name, "passwd-file") == 0)
+		userdb = &userdb_passwd_file;
+#endif
+#ifdef USERDB_STATIC
+	if (strcasecmp(name, "static") == 0)
+		userdb = &userdb_static;
+#endif
+#ifdef USERDB_VPOPMAIL
+	if (strcasecmp(name, "vpopmail") == 0)
+		userdb = &userdb_vpopmail;
+#endif
+
+	if (userdb == NULL)
+		i_fatal("Unknown userdb type '%s'", name);
+
+	/* initialize */
+	if (userdb->init != NULL) {
+		args = getenv("USERDB_ARGS");
+		if (args == NULL) args = "";
+
+		userdb->init(args);
+	}
+}
+
+void userdb_deinit(void)
+{
+	if (userdb != NULL && userdb->deinit != NULL)
+		userdb->deinit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/userdb.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,35 @@
+#ifndef __USERDB_H
+#define __USERDB_H
+
+struct user_data {
+	pool_t pool;
+
+	char *virtual_user;
+	char *home;
+	char *mail;
+
+	char *system_user;
+	uid_t uid;
+	gid_t gid;
+
+	int chroot; /* chroot to home directory */
+};
+
+struct userdb_module {
+	void (*init)(const char *args);
+	void (*deinit)(void);
+
+	struct user_data *(*lookup)(const char *user, const char *realm);
+};
+
+extern struct userdb_module *userdb;
+
+extern struct userdb_module userdb_static;
+extern struct userdb_module userdb_passwd;
+extern struct userdb_module userdb_passwd_file;
+extern struct userdb_module userdb_vpopmail;
+
+void userdb_init(void);
+void userdb_deinit(void);
+
+#endif
--- a/src/auth/userinfo-pam.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,258 +0,0 @@
-/*
-   Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
-
-   You're allowed to do whatever you like with this software (including
-   re-distribution in source and/or binary form, with or without
-   modification), provided that credit is given where it is due and any
-   modified versions are marked as such.  There's absolutely no warranty.
-*/
-
-#include "config.h"
-#undef HAVE_CONFIG_H
-
-#ifdef USERINFO_PAM
-
-#include "userinfo-passwd.h"
-
-#include <stdlib.h>
-#ifdef HAVE_SECURITY_PAM_APPL_H
-#  include <security/pam_appl.h>
-#elif defined(HAVE_PAM_PAM_APPL_H)
-#  include <pam/pam_appl.h>
-#endif
-
-#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM)
-/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
-   Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
-   so I thought this might work better. */
-#  define linux_const
-#else
-#  define linux_const			const
-#endif
-typedef linux_const void *pam_item_t;
-
-#ifdef AUTH_PAM_USERPASS
-#  include <security/pam_client.h>
-
-#  ifndef PAM_BP_RCONTROL
-/* Linux-PAM prior to 0.74 */
-#    define PAM_BP_RCONTROL	PAM_BP_CONTROL
-#    define PAM_BP_WDATA	PAM_BP_DATA
-#    define PAM_BP_RDATA	PAM_BP_DATA
-#  endif
-
-#  define USERPASS_AGENT_ID		"userpass"
-#  define USERPASS_AGENT_ID_LENGTH	8
-
-#  define USERPASS_USER_MASK		0x03
-#  define USERPASS_USER_REQUIRED	1
-#  define USERPASS_USER_KNOWN		2
-#  define USERPASS_USER_FIXED		3
-#endif
-
-struct pam_userpass {
-	const char *user;
-	const char *pass;
-};
-
-static char *service_name;
-
-static int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
-	struct pam_response **resp, void *appdata_ptr)
-{
-	/* @UNSAFE */
-	struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
-#ifdef AUTH_PAM_USERPASS
-	pamc_bp_t prompt;
-	const char *input;
-	char *output;
-	char flags;
-	size_t userlen, passlen;
-
-	if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
-		return PAM_CONV_ERR;
-
-	prompt = (pamc_bp_t)msg[0]->msg;
-	input = PAM_BP_RDATA(prompt);
-
-	if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
-	    strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
-		return PAM_CONV_ERR;
-
-	flags = input[USERPASS_AGENT_ID_LENGTH + 1];
-	input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
-
-	if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
-	    strcmp(input, userpass->user))
-		return PAM_CONV_AGAIN;
-
-	if (!(*resp = malloc(sizeof(struct pam_response))))
-		return PAM_CONV_ERR;
-
-	userlen = strlen(userpass->user);
-	passlen = strlen(userpass->pass);
-
-	prompt = NULL;
-	PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
-	output = PAM_BP_WDATA(prompt);
-
-	memcpy(output, userpass->user, userlen + 1);
-	memcpy(output + userlen + 1, userpass->pass, passlen);
-
-	(*resp)[0].resp_retcode = 0;
-	(*resp)[0].resp = (char *)prompt;
-#else
-	char *string;
-	int i;
-
-	if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
-		return PAM_CONV_ERR;
-
-	for (i = 0; i < num_msg; i++) {
-		switch (msg[i]->msg_style) {
-		case PAM_PROMPT_ECHO_ON:
-			string = strdup(userpass->user);
-			if (string == NULL)
-				i_fatal("Out of memory");
-			break;
-		case PAM_PROMPT_ECHO_OFF:
-			string = strdup(userpass->pass);
-			if (string == NULL)
-				i_fatal("Out of memory");
-			break;
-		case PAM_ERROR_MSG:
-		case PAM_TEXT_INFO:
-			string = NULL;
-			break;
-		default:
-			while (--i >= 0) {
-				if ((*resp)[i].resp == NULL)
-					continue;
-				safe_memset((*resp)[i].resp, 0,
-					    strlen((*resp)[i].resp));
-				free((*resp)[i].resp);
-				(*resp)[i].resp = NULL;
-			}
-
-			free(*resp);
-			*resp = NULL;
-
-			return PAM_CONV_ERR;
-		}
-
-		(*resp)[i].resp_retcode = PAM_SUCCESS;
-		(*resp)[i].resp = string;
-	}
-#endif
-
-	return PAM_SUCCESS;
-}
-
-static int pam_auth(pam_handle_t *pamh, const char *user)
-{
-	char *item;
-	int status;
-
-	if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
-		if (verbose) {
-			i_info("PAM: pam_authenticate(%s) failed: %s",
-			       user, pam_strerror(pamh, status));
-		}
-		return status;
-	}
-
-#ifdef HAVE_PAM_SETCRED
-	if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
-		if (verbose) {
-			i_info("PAM: pam_setcred(%s) failed: %s",
-			       user, pam_strerror(pamh, status));
-		}
-		return status;
-	}
-#endif
-
-	if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
-		if (verbose) {
-			i_info("PAM: pam_acct_mgmt(%s) failed: %s",
-			       user, pam_strerror(pamh, status));
-		}
-		return status;
-	}
-
-	status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
-	if (status != PAM_SUCCESS) {
-		if (verbose) {
-			i_info("PAM: pam_get_item(%s) failed: %s",
-			       user, pam_strerror(pamh, status));
-		}
-		return status;
-	}
-
-	return PAM_SUCCESS;
-}
-
-static int pam_verify_plain(const char *user, const char *password,
-			    struct auth_cookie_reply_data *reply)
-{
-	pam_handle_t *pamh;
-	struct pam_userpass userpass;
-	struct pam_conv conv;
-	struct passwd *pw;
-	int status, status2;
-
-	conv.conv = pam_userpass_conv;
-	conv.appdata_ptr = &userpass;
-
-	userpass.user = user;
-	userpass.pass = password;
-
-	status = pam_start(service_name, user, &conv, &pamh);
-	if (status != PAM_SUCCESS) {
-		if (verbose) {
-			i_info("PAM: pam_start(%s) failed: %s",
-			       user, pam_strerror(pamh, status));
-		}
-		return FALSE;
-	}
-
-	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 FALSE;
-	}
-
-	if (status != PAM_SUCCESS)
-		return FALSE;
-
-	/* password ok, save the user info */
-	pw = getpwnam(user);
-	if (pw == NULL) {
-		i_error("PAM: getpwnam(%s) failed: %m", user);
-		return FALSE;
-	}
-
-	safe_memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
-	passwd_fill_cookie_reply(pw, reply);
-	return TRUE;
-}
-
-static void pam_init(const char *args)
-{
-	service_name = i_strdup(*args != '\0' ? args : "imap");
-}
-
-static void pam_deinit(void)
-{
-	i_free(service_name);
-}
-
-struct user_info_module userinfo_pam = {
-	pam_init,
-	pam_deinit,
-
-	pam_verify_plain,
-	NULL
-};
-
-#endif
--- a/src/auth/userinfo-passwd-file.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,426 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "config.h"
-#undef HAVE_CONFIG_H
-
-#ifdef USERINFO_PASSWD_FILE
-
-#include "userinfo-passwd.h"
-
-#include "buffer.h"
-#include "istream.h"
-#include "hash.h"
-#include "hex-binary.h"
-#include "md5.h"
-#include "mycrypt.h"
-
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-struct passwd_file {
-	pool_t pool;
-
-	char *path;
-	time_t stamp;
-	int fd;
-
-	struct hash_table *users;
-};
-
-enum password_type {
-	PASSWORD_DES,
-	PASSWORD_MD5,
-	PASSWORD_DIGEST_MD5
-};
-
-struct passwd_user {
-	char *user_realm; /* user:realm */
-	const char *realm; /* NULL or points to user_realm */
-	char *password;
-	char *home;
-	char *mail;
-
-	uid_t uid;
-	gid_t gid;
-
-	enum password_type password_type;
-	unsigned int chroot:1;
-};
-
-static struct passwd_file *passwd_file;
-
-static void passwd_file_sync(void);
-
-static int get_reply_data(struct passwd_user *pu,
-			  struct auth_cookie_reply_data *reply)
-{
-	const char *user;
-	struct passwd *pw;
-
-	if (pu->uid == 0 || pu->gid == 0 ||
-	    (pu->home == NULL && pu->mail == NULL)) {
-		/* all required information was not set in passwd file,
-		   check from system's passwd */
-		user = pu->realm == NULL ? pu->user_realm :
-			t_strndup(pu->user_realm, strlen(pu->user_realm) -
-				  strlen(pu->realm) - 1);
-
-		pw = getpwnam(user);
-		if (pw == NULL) {
-			i_error("passwd-file(%s): missing info and "
-				"not found with getpwnam()", user);
-			return FALSE;
-		}
-
-		passwd_fill_cookie_reply(pw, reply);
-	}
-
-	if (pu->uid != 0)
-		reply->uid = pu->uid;
-	if (pu->gid != 0)
-		reply->gid = pu->gid;
-
-	if (pu->home != NULL) {
-		if (strocpy(reply->home, pu->home, sizeof(reply->home)) < 0)
-			i_panic("home overflow");
-	}
-
-	if (pu->mail != NULL) {
-		if (strocpy(reply->mail, pu->mail, sizeof(reply->mail)) < 0)
-			i_panic("mail overflow");
-	}
-
-	if (strocpy(reply->virtual_user, pu->user_realm,
-		    sizeof(reply->virtual_user)) < 0)
-		i_panic("virtual_user overflow");
-
-	if (pu->realm != NULL) {
-		/* @UNSAFE: ':' -> '@' to make it look prettier */
-		size_t pos;
-
-		pos = (size_t) (pu->realm - (const char *) pu->user_realm);
-		reply->virtual_user[pos-1] = '@';
-	}
-
-	reply->chroot = pu->chroot;
-	return TRUE;
-}
-
-static int passwd_file_verify_plain(const char *user, const char *password,
-				    struct auth_cookie_reply_data *reply)
-{
-	struct passwd_user *pu;
-	unsigned char digest[16];
-	const char *str;
-
-	passwd_file_sync();
-
-	/* find it from all realms */
-	pu = hash_lookup(passwd_file->users, user);
-	if (pu == NULL) {
-		if (verbose)
-			i_info("passwd-file(%s): unknown user", user);
-		return FALSE;
-	}
-
-	/* verify that password matches */
-	switch (pu->password_type) {
-	case PASSWORD_DES:
-		if (strcmp(mycrypt(password, pu->password),
-			   pu->password) != 0) {
-			if (verbose) {
-				i_info("passwd-file(%s): DES password mismatch",
-				       user);
-			}
-			return FALSE;
-		}
-		break;
-	case PASSWORD_MD5:
-		md5_get_digest(password, strlen(password), digest);
-		str = binary_to_hex(digest, sizeof(digest));
-
-		if (strcmp(str, pu->password) != 0) {
-			if (verbose) {
-				i_info("passwd-file(%s): MD5 password mismatch",
-				       user);
-			}
-			return FALSE;
-		}
-		break;
-	case PASSWORD_DIGEST_MD5:
-		/* user:realm:passwd */
-		str = t_strconcat(pu->user_realm,
-				  pu->realm == NULL ? ":" : "",  ":",
-				  password, NULL);
-
-		md5_get_digest(str, strlen(str), digest);
-		str = binary_to_hex(digest, sizeof(digest));
-
-		if (strcmp(str, pu->password) != 0) {
-			if (verbose) {
-				i_info("passwd-file(%s): "
-				       "DIGEST-MD5 password mismatch", user);
-			}
-			return FALSE;
-		}
-		break;
-	default:
-                i_unreached();
-	}
-
-	/* found */
-	return get_reply_data(pu, reply);
-}
-
-static int passwd_file_lookup_digest_md5(const char *user, const char *realm,
-					 unsigned char digest[16],
-					 struct auth_cookie_reply_data *reply)
-{
-	const char *id;
-	struct passwd_user *pu;
-	buffer_t *buf;
-
-	passwd_file_sync();
-
-	/* FIXME: we simply ignore UTF8 setting.. */
-
-	id = realm == NULL || *realm == '\0' ? user :
-		t_strconcat(user, ":", realm, NULL);
-
-	pu = hash_lookup(passwd_file->users, id);
-	if (pu == NULL) {
-		if (verbose)
-			i_info("passwd-file(%s): unknown user", user);
-		return FALSE;
-	}
-
-	/* found */
-	i_assert(strlen(pu->password) == 32);
-
-	buf = buffer_create_data(data_stack_pool, digest, 16);
-	if (!hex_to_binary(pu->password, buf)) {
-		if (verbose)
-			i_info("passwd-file(%s): invalid password field", user);
-		return FALSE;
-	}
-
-	return get_reply_data(pu, reply);
-}
-
-static void passwd_file_add(struct passwd_file *pw, const char *username,
-			    const char *pass, const char *const *args)
-{
-	/* args = uid, gid, user info, home dir, shell, realm, mail, chroot */
-	struct passwd_user *pu;
-	const char *p;
-
-	if (strlen(username) >= AUTH_MAX_USER_LEN) {
-		i_error("Username %s is too long (max. %d chars) in password "
-			"file %s", username, AUTH_MAX_USER_LEN, pw->path);
-		return;
-	}
-
-	pu = p_new(pw->pool, struct passwd_user, 1);
-
-	p = strchr(pass, '[');
-	if (p == NULL) {
-		pu->password = p_strdup(pw->pool, pass);
-		pu->password_type = PASSWORD_DES;
-	} else {
-		/* password[type] - we're being libpam-pwdfile compatible
-		   here. it uses 13 = DES and 34 = MD5. We add
-		   56 = Digest-MD5. */
-		pu->password = p_strndup(pw->pool, pass, (size_t) (p-pass));
-		if (p[1] == '3' && p[2] == '4') {
-			pu->password_type = PASSWORD_MD5;
-			str_lcase(pu->password);
-		} else if (p[1] == '5' && p[2] == '6') {
-			pu->password_type = PASSWORD_DIGEST_MD5;
-			if (strlen(pu->password) != 32) {
-				i_error("User %s has invalid password in "
-					"file %s", username, pw->path);
-				return;
-			}
-			str_lcase(pu->password);
-		} else {
-			pu->password_type = PASSWORD_DES;
-		}
-	}
-
-	if (args[0] != NULL) {
-		pu->uid = atoi(args[0]);
-		if (pu->uid == 0) {
-			i_error("User %s has UID 0 in password file %s",
-				username, pw->path);
-			return;
-		}
-		args++;
-	}
-
-	if (args[0] != NULL) {
-		pu->gid = atoi(args[0]);
-		if (pu->gid == 0) {
-			i_error("User %s has GID 0 in password file %s",
-				username, pw->path);
-			return;
-		}
-		args++;
-	}
-
-	/* user info */
-	if (args[0] != NULL)
-		args++;
-
-	/* home */
-	if (args[0] != NULL) {
-		if (strlen(args[0]) >= AUTH_MAX_HOME_LEN) {
-			i_error("User %s has too long home directory in "
-				"password file %s", username, pw->path);
-			return;
-		}
-
-		pu->home = p_strdup(pw->pool, args[0]);
-		args++;
-	}
-
-	/* shell */
-	if (args[0] != NULL)
-		args++;
-
-	/* realm */
-	if (args[0] == NULL || *args[0] == '\0') {
-		pu->user_realm = p_strdup(pw->pool, username);
-		if (hash_lookup(pw->users, username) != NULL) {
-			i_error("User %s already exists in password file %s",
-				username, pw->path);
-			return;
-		}
-	} else {
-		pu->user_realm = p_strconcat(pw->pool, username, ":",
-					     args[0], NULL);
-		pu->realm = pu->user_realm + strlen(username)+1;
-
-		if (hash_lookup(pw->users, pu->user_realm) != NULL) {
-			i_error("User %s already exists in realm %s in "
-				"password file %s", username, args[0],
-				pw->path);
-			return;
-		}
-	}
-
-	/* mail storage */
-	if (args[0] != NULL) {
-		if (strlen(args[0]) >= AUTH_MAX_MAIL_LEN) {
-			i_error("User %s has too long mail storage in "
-				"password file %s", username, pw->path);
-			return;
-		}
-
-		pu->mail = p_strdup(pw->pool, args[0]);
-		args++;
-	}
-
-	/* chroot */
-	if (args[0] != NULL && strstr(args[0], "chroot") != NULL)
-		pu->chroot = TRUE;
-
-	hash_insert(pw->users, pu->user_realm, pu);
-}
-
-static void passwd_file_parse_file(struct passwd_file *pw)
-{
-	struct istream *input;
-	const char *const *args;
-	const char *line;
-
-	input = i_stream_create_file(pw->fd, default_pool, 2048, FALSE);
-	for (;;) {
-		line = i_stream_next_line(input);
-		if (line == NULL) {
-			if (i_stream_read(input) <= 0)
-				break;
-                        continue;
-		}
-
-		if (*line == '\0' || *line == ':')
-			continue; /* no username */
-
-		t_push();
-		args = t_strsplit(line, ":");
-		if (args[1] != NULL && IS_VALID_PASSWD(args[1])) {
-			/* valid user/pass */
-			passwd_file_add(pw, args[0], args[1], args+2);
-		}
-		t_pop();
-	}
-	i_stream_unref(input);
-}
-
-static struct passwd_file *passwd_file_parse(const char *path)
-{
-	struct passwd_file *pw;
-	pool_t pool;
-	struct stat st;
-	int fd;
-
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		i_fatal("Can't open passwd-file %s: %m", path);
-	}
-
-	if (fstat(fd, &st) != 0)
-		i_fatal("fstat() failed for passwd-file %s: %m", path);
-
-	pool = pool_alloconly_create("passwd_file", 10240);
-	pw = p_new(pool, struct passwd_file, 1);
-	pw->pool = pool;
-	pw->path = p_strdup(pool, path);
-	pw->stamp = st.st_mtime;
-	pw->fd = fd;
-	pw->users = hash_create(default_pool, pool, 100,
-				str_hash, (hash_cmp_callback_t)strcmp);
-
-	passwd_file_parse_file(pw);
-	return pw;
-}
-
-static void passwd_file_free(struct passwd_file *pw)
-{
-	pool_unref(pw->pool);
-}
-
-static void passwd_file_init(const char *args)
-{
-	passwd_file = passwd_file_parse(args);
-}
-
-static void passwd_file_deinit(void)
-{
-	passwd_file_free(passwd_file);
-}
-
-static void passwd_file_sync(void)
-{
-	const char *path;
-	struct stat st;
-
-	if (stat(passwd_file->path, &st) < 0)
-		i_fatal("stat() failed for %s: %m", passwd_file->path);
-
-	if (st.st_mtime != passwd_file->stamp) {
-		path = t_strdup(passwd_file->path);
-		passwd_file_free(passwd_file);
-		passwd_file = passwd_file_parse(path);
-	}
-}
-
-struct user_info_module userinfo_passwd_file = {
-	passwd_file_init,
-	passwd_file_deinit,
-
-	passwd_file_verify_plain,
-        passwd_file_lookup_digest_md5
-};
-
-#endif
--- a/src/auth/userinfo-passwd.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
-   Loosely based on auth_passwd.c from popa3d by
-   Solar Designer <solar@openwall.com>
-
-   Copyright (C) 2002 Timo Sirainen
-*/
-
-#include "config.h"
-#undef HAVE_CONFIG_H
-
-#include "userinfo-passwd.h"
-
-void passwd_fill_cookie_reply(struct passwd *pw,
-			      struct auth_cookie_reply_data *reply)
-{
-	reply->uid = pw->pw_uid;
-	reply->gid = pw->pw_gid;
-
-	if (strocpy(reply->system_user, pw->pw_name,
-		    sizeof(reply->system_user)) < 0)
-		i_panic("system_user overflow");
-	if (strocpy(reply->virtual_user, pw->pw_name,
-		    sizeof(reply->virtual_user)) < 0)
-		i_panic("virtual_user overflow");
-	if (strocpy(reply->home, pw->pw_dir, sizeof(reply->home)) < 0)
-		i_panic("home overflow");
-}
-
-#ifdef USERINFO_PASSWD
-
-#include "mycrypt.h"
-
-static int passwd_verify_plain(const char *user, const char *password,
-			       struct auth_cookie_reply_data *reply)
-{
-	struct passwd *pw;
-	int result;
-
-	pw = getpwnam(user);
-	if (pw == NULL) {
-		if (errno != 0)
-			i_error("getpwnam(%s) failed: %m", user);
-		else if (verbose)
-			i_info("passwd(%s): unknown user", user);
-		return FALSE;
-	}
-
-	if (!IS_VALID_PASSWD(pw->pw_passwd)) {
-		if (verbose) {
-			i_info("passwd(%s): invalid password field '%s'",
-			       user, pw->pw_passwd);
-		}
-		return FALSE;
-	}
-
-	/* check if the password is valid */
-	result = strcmp(mycrypt(password, pw->pw_passwd), pw->pw_passwd) == 0;
-
-	/* clear the passwords from memory */
-	safe_memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
-
-	if (!result) {
-		if (verbose)
-			i_info("passwd(%s): password mismatch", user);
-		return FALSE;
-	}
-
-	/* password ok, save the user info */
-        passwd_fill_cookie_reply(pw, reply);
-	return TRUE;
-}
-
-static void passwd_deinit(void)
-{
-	endpwent();
-}
-
-struct user_info_module userinfo_passwd = {
-	NULL,
-	passwd_deinit,
-
-	passwd_verify_plain,
-	NULL
-};
-
-#endif
--- a/src/auth/userinfo-passwd.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#ifndef __USERINFO_PASSWD_H
-#define __USERINFO_PASSWD_H
-
-#include "common.h"
-#include "safe-memset.h"
-#include "userinfo.h"
-
-#include <pwd.h>
-
-#define IS_VALID_PASSWD(pass) \
-	((pass)[0] != '\0' && (pass)[0] != '*' && (pass)[0] != '!')
-
-void passwd_fill_cookie_reply(struct passwd *pw,
-			      struct auth_cookie_reply_data *reply);
-
-#endif
--- a/src/auth/userinfo-shadow.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
-   Loosely based on auth_shadow.c from popa3d by
-   Solar Designer <solar@openwall.com>
-
-   Copyright (C) 2002 Timo Sirainen
-*/
-
-#include "config.h"
-#undef HAVE_CONFIG_H
-
-#ifdef USERINFO_SHADOW
-
-#include "userinfo-passwd.h"
-#include "mycrypt.h"
-
-#include <shadow.h>
-
-static int shadow_verify_plain(const char *user, const char *password,
-			       struct auth_cookie_reply_data *reply)
-{
-	struct passwd *pw;
-	struct spwd *spw;
-	int result;
-
-	spw = getspnam(user);
-	if (spw == NULL) {
-		if (errno != 0)
-			i_error("getspnam(%s) failed: %m", user);
-		else if (verbose)
-			i_info("shadow(%s): unknown user", user);
-		return FALSE;
-	}
-
-	if (!IS_VALID_PASSWD(spw->sp_pwdp)) {
-		if (verbose) {
-			i_info("shadow(%s): invalid password field '%s'",
-			       user, spw->sp_pwdp);
-		}
-		return FALSE;
-	}
-
-	/* check if the password is valid */
-	result = strcmp(mycrypt(password, spw->sp_pwdp), spw->sp_pwdp) == 0;
-
-	/* clear the passwords from memory */
-	safe_memset(spw->sp_pwdp, 0, strlen(spw->sp_pwdp));
-
-	if (!result) {
-		if (verbose)
-			i_info("shadow(%s): password mismatch", user);
-		return FALSE;
-	}
-
-	/* password ok, save the user info */
-	pw = getpwnam(user);
-	if (pw == NULL) {
-		i_error("shadow(%s): getpwnam() failed: %m", user);
-		return FALSE;
-	}
-
-        passwd_fill_cookie_reply(pw, reply);
-	return TRUE;
-}
-
-static void shadow_deinit(void)
-{
-	endpwent();
-        endspent();
-}
-
-struct user_info_module userinfo_shadow = {
-	NULL,
-	shadow_deinit,
-
-	shadow_verify_plain,
-	NULL
-};
-
-#endif
--- a/src/auth/userinfo-vpopmail.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-/* Thanks to Courier-IMAP for showing how the vpopmail API should be used */
-
-#include "config.h"
-#undef HAVE_CONFIG_H
-
-#ifdef USERINFO_VPOPMAIL
-
-#include "userinfo-passwd.h"
-#include "mycrypt.h"
-
-#include <stdio.h>
-#include <vpopmail.h>
-#include <vauth.h>
-
-/* Limit user and domain to 80 chars each (+1 for \0). I wouldn't recommend
-   raising this limit at least much, vpopmail is full of potential buffer
-   overflows. */
-#define VPOPMAIL_LIMIT 81
-
-static int vpopmail_verify_plain(const char *user, const char *password,
-				 struct auth_cookie_reply_data *reply)
-{
-	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
-	struct vqpasswd *vpw;
-	int result;
-
-	/* vpop_user must be zero-filled or parse_email() leaves an extra
-	   character after the user name. we'll fill vpop_domain as well
-	   just to be sure... */
-	memset(vpop_user, '\0', sizeof(vpop_user));
-	memset(vpop_domain, '\0', sizeof(vpop_domain));
-
-	if (parse_email(t_strdup_noconst(user), vpop_user, vpop_domain,
-			sizeof(vpop_user)-1) < 0) {
-		if (verbose)
-			i_info("vpopmail(%s): parse_email() failed", user);
-		return FALSE;
-	}
-
-	/* we have to get uid/gid separately, because the gid field in
-	   struct vqpasswd isn't really gid at all but just some flags... */
-	if (vget_assign(vpop_domain, NULL, 0,
-			&reply->uid, &reply->gid) == NULL) {
-		if (verbose) {
-			i_info("vpopmail(%s): vget_assign(%s) failed",
-			       user, vpop_domain);
-		}
-		return FALSE;
-	}
-
-	vpw = vauth_getpw(vpop_user, vpop_domain);
-	if (vpw != NULL && (vpw->pw_dir == NULL || vpw->pw_dir[0] == '\0')) {
-		/* user's homedir doesn't exist yet, create it */
-		if (verbose) {
-			i_info("vpopmail(%s): pw_dir isn't set, creating",
-			       user);
-		}
-
-		if (make_user_dir(vpop_user, vpop_domain,
-				  reply->uid, reply->gid) == NULL) {
-			i_error("vpopmail(%s): make_user_dir(%s, %s) failed",
-				user, vpop_user, vpop_domain);
-			return FALSE;
-		}
-
-		vpw = vauth_getpw(vpop_user, vpop_domain);
-	}
-
-	if (vpw == NULL) {
-		if (verbose) {
-			i_info("vpopmail(%s): vauth_getpw(%s, %s) failed",
-			       user, vpop_user, vpop_domain);
-		}
-		return FALSE;
-	}
-
-	if (vpw->pw_gid & NO_IMAP) {
-		if (verbose)
-			i_info("vpopmail(%s): IMAP disabled", user);
-		return FALSE;
-	}
-
-	/* verify password */
-	result = strcmp(mycrypt(password, vpw->pw_passwd), vpw->pw_passwd) == 0;
-	safe_memset(vpw->pw_passwd, 0, strlen(vpw->pw_passwd));
-
-	if (!result) {
-		if (verbose)
-			i_info("vpopmail(%s): password mismatch", user);
-		return FALSE;
-	}
-
-	if (strocpy(reply->system_user, vpw->pw_name,
-		    sizeof(reply->system_user)) < 0) {
-		i_panic("Username too large (%u > %u)",
-			strlen(vpw->pw_name), sizeof(reply->system_user)-1);
-	}
-	if (strocpy(reply->virtual_user, vpw->pw_name,
-		    sizeof(reply->virtual_user)) < 0) {
-		i_panic("Username too large (%u > %u)",
-			strlen(vpw->pw_name), sizeof(reply->virtual_user)-1);
-	}
-	if (strocpy(reply->home, vpw->pw_dir, sizeof(reply->home)) < 0) {
-		i_panic("Home directory too large (%u > %u)",
-			strlen(vpw->pw_dir), sizeof(reply->home)-1);
-	}
-
-	return TRUE;
-}
-
-static void vpopmail_deinit(void)
-{
-	vclose();
-}
-
-struct user_info_module userinfo_vpopmail = {
-	NULL,
-	vpopmail_deinit,
-
-	vpopmail_verify_plain,
-	NULL
-};
-
-#endif
--- a/src/auth/userinfo.c	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "userinfo.h"
-
-#include <stdlib.h>
-
-struct user_info_module *userinfo;
-
-void userinfo_init(void)
-{
-	const char *name, *args;
-
-	userinfo = NULL;
-
-	name = getenv("USERINFO");
-	if (name == NULL)
-		i_fatal("USERINFO environment is unset");
-
-#ifdef USERINFO_PASSWD
-	if (strcasecmp(name, "passwd") == 0)
-		userinfo = &userinfo_passwd;
-#endif
-#ifdef USERINFO_SHADOW
-	if (strcasecmp(name, "shadow") == 0)
-		userinfo = &userinfo_shadow;
-#endif
-#ifdef USERINFO_PAM
-	if (strcasecmp(name, "pam") == 0)
-		userinfo = &userinfo_pam;
-#endif
-#ifdef USERINFO_PASSWD_FILE
-	if (strcasecmp(name, "passwd-file") == 0)
-		userinfo = &userinfo_passwd_file;
-#endif
-#ifdef USERINFO_VPOPMAIL
-	if (strcasecmp(name, "vpopmail") == 0)
-		userinfo = &userinfo_vpopmail;
-#endif
-
-	if (userinfo == NULL)
-		i_fatal("Unknown userinfo type '%s'", name);
-
-	/* initialize */
-	if (userinfo->init != NULL) {
-		args = getenv("USERINFO_ARGS");
-		if (args == NULL) args = "";
-
-		userinfo->init(args);
-	}
-
-	if ((auth_mechanisms & AUTH_MECH_PLAIN) &&
-	    userinfo->verify_plain == NULL)
-		i_fatal("Userinfo %s doesn't support PLAIN method", name);
-	if ((auth_mechanisms & AUTH_MECH_DIGEST_MD5) &&
-	    userinfo->lookup_digest_md5 == NULL)
-		i_fatal("Userinfo %s doesn't support DIGEST-MD5 method", name);
-}
-
-void userinfo_deinit(void)
-{
-	if (userinfo != NULL && userinfo->deinit != NULL)
-		userinfo->deinit();
-}
--- a/src/auth/userinfo.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#ifndef __USERINFO_H
-#define __USERINFO_H
-
-#include "auth-interface.h"
-
-struct user_info_module {
-	void (*init)(const char *args);
-	void (*deinit)(void);
-
-	/* Returns TRUE if user/pass matches, and fills reply with user
-	   information. reply should have been initialized (zeroed) before
-	   calling this function. */
-	int (*verify_plain)(const char *user, const char *password,
-			    struct auth_cookie_reply_data *reply);
-
-	/* Digest-MD5 specific password lookup. The digest is filled with
-	   the MD5 password which consists of a MD5 sum of
-	   "user:realm:password". If utf8 is TRUE, the user and realm are
-	   in UTF-8, otherwise ISO-8859-1. Returns TRUE if user was found. */
-	int (*lookup_digest_md5)(const char *user, const char *realm,
-				 unsigned char digest[16],
-				 struct auth_cookie_reply_data *reply);
-};
-
-extern struct user_info_module *userinfo;
-
-extern struct user_info_module userinfo_passwd;
-extern struct user_info_module userinfo_shadow;
-extern struct user_info_module userinfo_pam;
-extern struct user_info_module userinfo_passwd_file;
-extern struct user_info_module userinfo_vpopmail;
-
-void userinfo_init(void);
-void userinfo_deinit(void);
-
-#endif
--- a/src/imap/main.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/imap/main.c	Mon Jan 27 03:33:40 2003 +0200
@@ -12,7 +12,7 @@
 #include <syslog.h>
 
 #define IS_STANDALONE() \
-        (getenv("LOGIN_TAG") == NULL)
+        (getenv("LOGGED_IN") == NULL)
 
 struct ioloop *ioloop;
 static char log_prefix[128]; /* syslog() needs this to be permanent */
@@ -104,9 +104,6 @@
 		client_send_line(client, t_strconcat(
 			"* PREAUTH [CAPABILITY "CAPABILITY_STRING"] "
 			"Logged in as ", getenv("USER"), NULL));
-	} else {
-		client_send_line(client, t_strconcat(getenv("LOGIN_TAG"),
-						     " OK Logged in.", NULL));
 	}
 }
 
@@ -125,8 +122,10 @@
 int main(int argc __attr_unused__, char *argv[], char *envp[])
 {
 #ifdef DEBUG
-	if (getenv("LOGIN_TAG") != NULL)
+	if (getenv("LOGGED_IN") != NULL) {
 		fd_debug_verify_leaks(3, 1024);
+		putenv("DISABLE_ALARMHUP=1"); /* annoying when debugging */
+	}
 #endif
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
--- a/src/login/auth-connection.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/auth-connection.c	Mon Jan 27 03:33:40 2003 +0200
@@ -12,29 +12,12 @@
 #include <dirent.h>
 #include <sys/stat.h>
 
-#define MAX_INBUF_SIZE AUTH_MAX_REQUEST_DATA_SIZE
-#define MAX_OUTBUF_SIZE \
-	(sizeof(struct auth_continued_request_data) + \
-	 AUTH_MAX_REQUEST_DATA_SIZE)
-
-struct auth_connection {
-	struct auth_connection *next;
+/* Maximum size for an auth reply. 50kB should be more than enough. */
+#define MAX_INBUF_SIZE (1024*50)
 
-	char *path;
-	int fd;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-
-	unsigned int auth_process;
-	enum auth_mech available_auth_mechs;
-        struct auth_reply_data in_reply;
-
-        struct hash_table *requests;
-
-	unsigned int init_received:1;
-	unsigned int in_reply_received:1;
-};
+#define MAX_OUTBUF_SIZE \
+	(sizeof(struct auth_login_request_continue) + \
+	 AUTH_LOGIN_MAX_REQUEST_DATA_SIZE)
 
 enum auth_mech available_auth_mechs;
 
@@ -62,7 +45,7 @@
 static struct auth_connection *auth_connection_new(const char *path)
 {
 	struct auth_connection *conn;
-        struct client_auth_init_data init_data;
+        struct auth_login_handshake_input handshake;
 	int fd;
 
 	fd = net_connect_unix(path);
@@ -72,6 +55,9 @@
 		return NULL;
 	}
 
+	/* we depend on auth process - if it's slow, just wait */
+        net_set_nonblock(fd, FALSE);
+
 	conn = i_new(struct auth_connection, 1);
 	conn->path = i_strdup(path);
 	conn->fd = fd;
@@ -87,9 +73,9 @@
 	auth_connections = conn;
 
 	/* send our handshake */
-	memset(&init_data, 0, sizeof(init_data));
-	init_data.pid = login_process_uid;
-	if (o_stream_send(conn->output, &init_data, sizeof(init_data)) < 0) {
+	memset(&handshake, 0, sizeof(handshake));
+	handshake.pid = login_process_uid;
+	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
                 auth_connection_destroy(conn);
 		return NULL;
 	}
@@ -102,19 +88,13 @@
 	i_free(request);
 }
 
-static void request_abort(struct auth_request *request)
-{
-	request->callback(request, request->conn->auth_process,
-			  AUTH_RESULT_INTERNAL_FAILURE,
-			  (const unsigned char *) "Authentication process died",
-			  0, NULL, request->context);
-	request_destroy(request);
-}
-
 static void request_hash_destroy(void *key __attr_unused__, void *value,
 				 void *context __attr_unused__)
 {
-	request_abort(value);
+	struct auth_request *request = value;
+
+	request->callback(request, NULL, NULL, request->context);
+	request_destroy(request);
 }
 
 static void auth_connection_destroy(struct auth_connection *conn)
@@ -181,39 +161,32 @@
                 available_auth_mechs |= conn->available_auth_mechs;
 }
 
-static void auth_handle_init(struct auth_connection *conn,
-			     struct auth_init_data *init_data)
+static void auth_handle_handshake(struct auth_connection *conn,
+				  struct auth_login_handshake_output *handshake)
 {
-	conn->auth_process = init_data->auth_process;
-	conn->available_auth_mechs = init_data->auth_mechanisms;
-	conn->init_received = TRUE;
+	conn->pid = handshake->pid;
+	conn->available_auth_mechs = handshake->auth_mechanisms;
+	conn->handshake_received = TRUE;
 
 	update_available_auth_mechs();
 }
 
 static void auth_handle_reply(struct auth_connection *conn,
-			      struct auth_reply_data *reply_data,
+			      struct auth_login_reply *reply,
 			      const unsigned char *data)
 {
 	struct auth_request *request;
 
-	request = hash_lookup(conn->requests, POINTER_CAST(reply_data->id));
+	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
 	if (request == NULL) {
 		i_error("BUG: imap-auth sent us reply with unknown ID %u",
-			reply_data->id);
+			reply->id);
 		return;
 	}
 
-	/* save the returned cookie */
-	memcpy(request->cookie, reply_data->cookie, AUTH_COOKIE_SIZE);
+	request->callback(request, reply, data, request->context);
 
-	t_push();
-	request->callback(request, request->conn->auth_process,
-			  reply_data->result, data, reply_data->data_size,
-			  reply_data->virtual_user, request->context);
-	t_pop();
-
-	if (reply_data->result != AUTH_RESULT_CONTINUE)
+	if (reply->result != AUTH_LOGIN_RESULT_CONTINUE)
 		request_destroy(request);
 }
 
@@ -221,7 +194,7 @@
 		       struct io *io __attr_unused__)
 {
 	struct auth_connection *conn = context;
-        struct auth_init_data init_data;
+        struct auth_login_handshake_output handshake;
 	const unsigned char *data;
 	size_t size;
 
@@ -241,45 +214,40 @@
 		return;
 	}
 
-	data = i_stream_get_data(conn->input, &size);
+	if (!conn->handshake_received) {
+		data = i_stream_get_data(conn->input, &size);
+		if (size == sizeof(handshake)) {
+			memcpy(&handshake, data, sizeof(handshake));
+			i_stream_skip(conn->input, sizeof(handshake));
 
-	if (!conn->init_received) {
-		if (size == sizeof(struct auth_init_data)) {
-			memcpy(&init_data, data, sizeof(struct auth_init_data));
-			i_stream_skip(conn->input,
-				      sizeof(struct auth_init_data));
-
-			auth_handle_init(conn, &init_data);
-		} else if (size > sizeof(struct auth_init_data)) {
-			i_error("BUG: imap-auth sent us too much "
-				"initialization data (%"PRIuSIZE_T " vs %"
-				PRIuSIZE_T")", size,
-				sizeof(struct auth_init_data));
+			auth_handle_handshake(conn, &handshake);
+		} else if (size > sizeof(handshake)) {
+			i_error("BUG: imap-auth sent us too large handshake "
+				"(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
+				sizeof(handshake));
 			auth_connection_destroy(conn);
 		}
-
 		return;
 	}
 
-	if (!conn->in_reply_received) {
+	if (!conn->reply_received) {
 		data = i_stream_get_data(conn->input, &size);
-		if (size < sizeof(struct auth_reply_data))
+		if (size < sizeof(conn->reply))
 			return;
 
-		memcpy(&conn->in_reply, data, sizeof(struct auth_reply_data));
-		data += sizeof(struct auth_reply_data);
-		size -= sizeof(struct auth_reply_data);
-		i_stream_skip(conn->input, sizeof(struct auth_reply_data));
-		conn->in_reply_received = TRUE;
+		memcpy(&conn->reply, data, sizeof(conn->reply));
+		i_stream_skip(conn->input, sizeof(conn->reply));
+		conn->reply_received = TRUE;
 	}
 
-	if (size < conn->in_reply.data_size)
+	data = i_stream_get_data(conn->input, &size);
+	if (size < conn->reply.data_size)
 		return;
 
 	/* we've got a full reply */
-	conn->in_reply_received = FALSE;
-	auth_handle_reply(conn, &conn->in_reply, data);
-	i_stream_skip(conn->input, conn->in_reply.data_size);
+	conn->reply_received = FALSE;
+	auth_handle_reply(conn, &conn->reply, data);
+	i_stream_skip(conn->input, conn->reply.data_size);
 }
 
 int auth_init_request(enum auth_mech mech, auth_callback_t callback,
@@ -287,12 +255,12 @@
 {
 	struct auth_connection *conn;
 	struct auth_request *request;
-	struct auth_init_request_data request_data;
+	struct auth_login_request_new auth_request;
 
 	if (auth_reconnect)
 		auth_connect_missing();
 
-	conn = auth_connection_get(mech, sizeof(request_data), error);
+	conn = auth_connection_get(mech, sizeof(auth_request), error);
 	if (conn == NULL)
 		return FALSE;
 
@@ -311,11 +279,11 @@
 	hash_insert(conn->requests, POINTER_CAST(request->id), request);
 
 	/* send request to auth */
-	request_data.type = AUTH_REQUEST_INIT;
-	request_data.mech = request->mech;
-	request_data.id = request->id;
-	if (o_stream_send(request->conn->output, &request_data,
-			  sizeof(request_data)) < 0)
+	auth_request.type = AUTH_LOGIN_REQUEST_NEW;
+	auth_request.mech = request->mech;
+	auth_request.id = request->id;
+	if (o_stream_send(request->conn->output, &auth_request,
+			  sizeof(auth_request)) < 0)
 		auth_connection_destroy(request->conn);
 	return TRUE;
 }
@@ -323,16 +291,15 @@
 void auth_continue_request(struct auth_request *request,
 			   const unsigned char *data, size_t data_size)
 {
-	struct auth_continued_request_data request_data;
+	struct auth_login_request_continue auth_request;
 
 	/* send continued request to auth */
-	memcpy(request_data.cookie, request->cookie, AUTH_COOKIE_SIZE);
-	request_data.type = AUTH_REQUEST_CONTINUE;
-	request_data.id = request->id;
-	request_data.data_size = data_size;
+	auth_request.type = AUTH_LOGIN_REQUEST_CONTINUE;
+	auth_request.id = request->id;
+	auth_request.data_size = data_size;
 
-	if (o_stream_send(request->conn->output, &request_data,
-			  sizeof(request_data)) < 0)
+	if (o_stream_send(request->conn->output, &auth_request,
+			  sizeof(auth_request)) < 0)
 		auth_connection_destroy(request->conn);
 	else if (o_stream_send(request->conn->output, data, data_size) < 0)
 		auth_connection_destroy(request->conn);
--- a/src/login/auth-connection.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/auth-connection.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,26 +1,40 @@
 #ifndef __AUTH_CONNECTION_H
 #define __AUTH_CONNECTION_H
 
+struct client;
 struct auth_request;
 
-/* If result == AUTH_RESULT_INTERNAL_FAILURE, request may be NULL and
-   reply_data_size contains the error message. */
-typedef void (*auth_callback_t)(struct auth_request *request,
-				unsigned int auth_process,
-				enum auth_result result,
-				const unsigned char *reply_data,
-				size_t reply_data_size,
-				const char *virtual_user,
-				void *context);
+/* reply is NULL if auth connection died */
+typedef void auth_callback_t(struct auth_request *request,
+			     struct auth_login_reply *reply,
+			     const unsigned char *data, struct client *client);
+
+struct auth_connection {
+	struct auth_connection *next;
+
+	char *path;
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	unsigned int pid;
+	enum auth_mech available_auth_mechs;
+        struct auth_login_reply reply;
+
+        struct hash_table *requests;
+
+	unsigned int handshake_received:1;
+	unsigned int reply_received:1;
+};
 
 struct auth_request {
         enum auth_mech mech;
         struct auth_connection *conn;
 
 	unsigned int id;
-	unsigned char cookie[AUTH_COOKIE_SIZE];
 
-	auth_callback_t callback;
+	auth_callback_t *callback;
 	void *context;
 
 	unsigned int init_sent:1;
@@ -28,7 +42,7 @@
 
 extern enum auth_mech available_auth_mechs;
 
-int auth_init_request(enum auth_mech mech, auth_callback_t callback,
+int auth_init_request(enum auth_mech mech, auth_callback_t *callback,
 		      void *context, const char **error);
 
 void auth_continue_request(struct auth_request *request,
--- a/src/login/client-authenticate.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/client-authenticate.c	Mon Jan 27 03:33:40 2003 +0200
@@ -77,23 +77,20 @@
 	client_unref(client);
 }
 
-static void master_callback(enum master_reply_result result, void *context)
+static void master_callback(struct client *client, int success)
 {
-	struct client *client = context;
+	const char *reason = NULL;
 
-	switch (result) {
-	case MASTER_RESULT_SUCCESS:
-		client_destroy(client, t_strconcat("Login: ",
-						   client->virtual_user, NULL));
-		client_unref(client);
-		break;
-	case MASTER_RESULT_INTERNAL_FAILURE:
-		client_auth_abort(client, "Internal failure");
-		break;
-	default:
-		client_auth_abort(client, NULL);
-		break;
+	if (success)
+		reason = t_strconcat("Login: ", client->virtual_user, NULL);
+	else {
+		reason = t_strconcat("Internal login failure: ",
+				     client->virtual_user, NULL);
+		client_send_line(client, "* BYE Internal login failure.");
 	}
+
+	client_destroy(client, reason);
+	client_unref(client);
 }
 
 static void client_send_auth_data(struct client *client,
@@ -115,28 +112,55 @@
 	t_pop();
 }
 
+static const char *auth_login_get_str(struct auth_login_reply *reply,
+				      const unsigned char *data, size_t idx)
+{
+	size_t stop;
+
+	if (idx >= reply->data_size || idx >= reply->reply_idx)
+		return NULL;
+
+	stop = reply->reply_idx < reply->data_size ?
+		reply->reply_idx-1 : reply->data_size;
+
+	return t_strndup(data, stop);
+}
+
 static int auth_callback(struct auth_request *request,
-			 unsigned int auth_process, enum auth_result result,
-			 const unsigned char *reply_data,
-			 size_t reply_data_size, const char *virtual_user,
-			 void *context)
+			 struct auth_login_reply *reply,
+			 const unsigned char *data, void *context)
 {
 	struct client *client = context;
+	const char *user, *realm;
 
-	switch (result) {
-	case AUTH_RESULT_CONTINUE:
+	if (reply == NULL) {
+		/* failed */
+		client->auth_request = NULL;
+		client_auth_abort(client, "NO Authentication process died.");
+		return FALSE;
+	}
+
+	switch (reply->result) {
+	case AUTH_LOGIN_RESULT_CONTINUE:
 		client->auth_request = request;
 		return TRUE;
 
-	case AUTH_RESULT_SUCCESS:
+	case AUTH_LOGIN_RESULT_SUCCESS:
 		client->auth_request = NULL;
 
+		user = auth_login_get_str(reply, data, reply->username_idx);
+		realm = auth_login_get_str(reply, data, reply->realm_idx);
+
 		i_free(client->virtual_user);
-		client->virtual_user = i_strdup(virtual_user);
+		client->virtual_user = realm == NULL ?
+			i_strdup(user) : i_strconcat(user, "@", realm, NULL);
 
-		master_request_imap(client->fd, auth_process, client->cmd_tag,
-				    request->cookie, &client->ip,
-				    master_callback, client);
+		/* we should be able to log in. if we fail, just
+		   disconnect the client. */
+		client_send_tagline(client, "OK Logged in.");
+
+		master_request_imap(client, master_callback,
+				    request->conn->pid, request->id);
 
 		/* disable IO until we're back from master */
 		if (client->io != NULL) {
@@ -145,42 +169,33 @@
 		}
 		return FALSE;
 
-	case AUTH_RESULT_FAILURE:
+	case AUTH_LOGIN_RESULT_FAILURE:
 		/* see if we have error message */
 		client->auth_request = NULL;
 
-		if (reply_data_size > 0 &&
-		    reply_data[reply_data_size-1] == '\0') {
+		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
 			client_auth_abort(client, t_strconcat(
 				"NO Authentication failed: ",
-				(const char *) reply_data, NULL));
+				(const char *) data, NULL));
 		} else {
 			/* default error message */
 			client_auth_abort(client, NULL);
 		}
 		return FALSE;
-	default:
-		client->auth_request = NULL;
+	}
 
-		client_auth_abort(client, t_strconcat(
-			"NO Authentication failed: ", reply_data, NULL));
-		return FALSE;
-	}
+	i_unreached();
 }
 
 static void login_callback(struct auth_request *request,
-			   unsigned int auth_process, enum auth_result result,
-			   const unsigned char *reply_data,
-			   size_t reply_data_size, const char *virtual_user,
-			   void *context)
+			   struct auth_login_reply *reply,
+			   const unsigned char *data, struct client *client)
 {
-	struct client *client = context;
 	const void *ptr;
 	size_t size;
 
-	if (auth_callback(request, auth_process, result,
-			  reply_data, reply_data_size, virtual_user, context)) {
-                ptr = buffer_get_data(client->plain_login, &size);
+	if (auth_callback(request, reply, data, client)) {
+		ptr = buffer_get_data(client->plain_login, &size);
 		auth_continue_request(request, ptr, size);
 
 		buffer_set_used_size(client->plain_login, 0);
@@ -233,17 +248,12 @@
 }
 
 static void authenticate_callback(struct auth_request *request,
-				  unsigned int auth_process,
-				  enum auth_result result,
-				  const unsigned char *reply_data,
-				  size_t reply_data_size,
-				  const char *virtual_user, void *context)
+				  struct auth_login_reply *reply,
+				  const unsigned char *data,
+				  struct client *client)
 {
-	struct client *client = context;
-
-	if (auth_callback(request, auth_process, result,
-			  reply_data, reply_data_size, virtual_user, context))
-		client_send_auth_data(client, reply_data, reply_data_size);
+	if (auth_callback(request, reply, data, client))
+		client_send_auth_data(client, data, reply->data_size);
 }
 
 static void client_auth_input(void *context, int fd __attr_unused__,
--- a/src/login/client.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/client.h	Mon Jan 27 03:33:40 2003 +0200
@@ -2,6 +2,7 @@
 #define __CLIENT_H
 
 #include "network.h"
+#include "master.h"
 
 struct client {
 	time_t created;
@@ -23,6 +24,8 @@
 	struct auth_request *auth_request;
 	char *virtual_user;
 
+	master_callback_t *master_callback;
+
 	unsigned int tls:1;
 	unsigned int cmd_finished:1;
 	unsigned int skip_line:1;
--- a/src/login/common.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/common.h	Mon Jan 27 03:33:40 2003 +0200
@@ -2,7 +2,7 @@
 #define __COMMON_H
 
 #include "lib.h"
-#include "../auth/auth-interface.h"
+#include "../auth/auth-login-interface.h"
 
 extern int disable_plaintext_auth, process_per_connection, verbose_proctitle;
 extern unsigned int max_logging_users;
--- a/src/login/master.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/master.c	Mon Jan 27 03:33:40 2003 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "hash.h"
 #include "ioloop.h"
 #include "network.h"
 #include "fdpass.h"
@@ -9,88 +10,47 @@
 
 #include <unistd.h>
 
-struct waiting_request {
-	struct waiting_request *next;
-
-	unsigned int id;
-	master_callback_t callback;
-	void *context;
-};
-
 static struct io *io_master;
-static struct waiting_request *requests, **next_request;
+static struct hash_table *master_requests;
 
 static unsigned int master_pos;
-static char master_buf[sizeof(struct master_reply)];
+static char master_buf[sizeof(struct master_login_reply)];
 
-static void push_request(unsigned int id, master_callback_t callback,
-			 void *context)
+static void request_handle(struct master_login_reply *reply)
 {
-	struct waiting_request *req;
+	struct client *client;
 
-	req = i_new(struct waiting_request, 1);
-	req->id = id;
-	req->callback = callback;
-	req->context = context;
+	client = hash_lookup(master_requests, POINTER_CAST(reply->tag));
+	if (client == NULL)
+		i_fatal("Master sent reply with unknown tag %u", reply->tag);
 
-	*next_request = req;
-	next_request = &req->next;
+	client->master_callback(client, reply->success);
+
+	hash_remove(master_requests, POINTER_CAST(reply->tag));
 }
 
-static void pop_request(struct master_reply *reply)
+void master_request_imap(struct client *client, master_callback_t *callback,
+			 unsigned int auth_pid, unsigned int auth_id)
 {
-	struct waiting_request *req;
-
-	req = requests;
-	if (req == NULL) {
-		i_error("Master sent us unrequested reply for id %d",
-			reply->id);
-		return;
-	}
-
-	if (reply->id != req->id) {
-		i_fatal("Master sent invalid id for reply "
-			"(got %d, expecting %d)", reply->id, req->id);
-	}
-
-	req->callback(reply->result, req->context);
-
-	requests = req->next;
-	if (requests == NULL)
-		next_request = &requests;
-
-	i_free(req);
-}
-
-void master_request_imap(int fd, unsigned int auth_process,
-			 const char *login_tag,
-			 unsigned char cookie[AUTH_COOKIE_SIZE],
-			 struct ip_addr *ip,
-			 master_callback_t callback, void *context)
-{
-	struct master_request req;
-
-	i_assert(fd > 1);
+	struct master_login_request req;
 
 	memset(&req, 0, sizeof(req));
-	req.id = fd;
-	req.auth_process = auth_process;
-	memcpy(&req.ip, ip, sizeof(struct ip_addr));
-	memcpy(req.cookie, cookie, AUTH_COOKIE_SIZE);
-
-	if (strocpy(req.login_tag, login_tag, sizeof(req.login_tag)) < 0)
-		strocpy(req.login_tag, "*", sizeof(req.login_tag));
+	req.tag = client->fd;
+	req.auth_pid = auth_pid;
+	req.auth_id = auth_id;
+	req.ip = client->ip;
 
 	if (fd_send(LOGIN_MASTER_SOCKET_FD,
-		    fd, &req, sizeof(req)) != sizeof(req))
+		    client->fd, &req, sizeof(req)) != sizeof(req))
 		i_fatal("fd_send() failed: %m");
 
-	push_request(req.id, callback, context);
+	client->master_callback = callback;
+	hash_insert(master_requests, POINTER_CAST(req.tag), client);
 }
 
 void master_notify_finished(void)
 {
-	struct master_request req;
+	struct master_login_request req;
 
 	if (io_master == NULL)
 		return;
@@ -138,7 +98,7 @@
 		return;
 
 	/* reply is now read */
-	pop_request((struct master_reply *) master_buf);
+	request_handle((struct master_login_reply *) master_buf);
 	master_pos = 0;
 }
 
@@ -146,8 +106,8 @@
 {
 	main_ref();
 
-	requests = NULL;
-	next_request = &requests;
+	master_requests = hash_create(default_pool, default_pool,
+				      0, NULL, NULL);
 
         master_pos = 0;
 	io_master = io_add(LOGIN_MASTER_SOCKET_FD, IO_READ, master_input, NULL);
@@ -159,13 +119,7 @@
 
 void master_deinit(void)
 {
-	struct waiting_request *next;
-
-	while (requests != NULL) {
-		next = requests->next;
-		i_free(requests);
-		requests = next;
-	}
+	hash_destroy(master_requests);
 
 	if (io_master != NULL)
 		io_remove(io_master);
--- a/src/login/master.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/login/master.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,17 +1,14 @@
 #ifndef __MASTER_H
 #define __MASTER_H
 
-#include "../master/master-interface.h"
+struct client;
 
-typedef void (*master_callback_t)(enum master_reply_result result,
-				  void *context);
+#include "../master/master-login-interface.h"
 
-/* Request IMAP process for given cookie. */
-void master_request_imap(int fd, unsigned int auth_process,
-			 const char *login_tag,
-			 unsigned char cookie[AUTH_COOKIE_SIZE],
-			 struct ip_addr *ip,
-			 master_callback_t callback, void *context);
+typedef void master_callback_t(struct client *client, int success);
+
+void master_request_imap(struct client *client, master_callback_t *callback,
+			 unsigned int auth_pid, unsigned int auth_id);
 
 /* Notify master that we're not listening for new connections anymore. */
 void master_notify_finished(void);
--- a/src/master/Makefile.am	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/Makefile.am	Mon Jan 27 03:33:40 2003 +0200
@@ -26,7 +26,8 @@
 noinst_HEADERS = \
 	auth-process.h \
 	common.h \
+	imap-process.h \
 	login-process.h \
-	master-interface.h \
+	master-login-interface.h \
 	settings.h \
 	ssl-init.h
--- a/src/master/auth-process.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/auth-process.c	Mon Jan 27 03:33:40 2003 +0200
@@ -5,6 +5,7 @@
 #include "env-util.h"
 #include "fd-close-on-exec.h"
 #include "network.h"
+#include "istream.h"
 #include "ostream.h"
 #include "restrict-access.h"
 #include "restrict-process-size.h"
@@ -16,6 +17,9 @@
 #include <syslog.h>
 #include <sys/stat.h>
 
+#define MAX_INBUF_SIZE \
+	(sizeof(struct auth_master_reply) + AUTH_MASTER_MAX_REPLY_DATA_SIZE)
+
 struct auth_process {
 	struct auth_process *next;
 
@@ -23,113 +27,154 @@
 	pid_t pid;
 	int fd;
 	struct io *io;
+	struct istream *input;
 	struct ostream *output;
 
-	unsigned int reply_pos;
-	char reply_buf[sizeof(struct auth_cookie_reply_data)];
+	struct auth_master_reply auth_reply;
 
-	struct waiting_request *requests, **next_request;
+	struct hash_table *requests;
 
 	unsigned int initialized:1;
-};
-
-struct waiting_request {
-        struct waiting_request *next;
-	unsigned int id;
-
-	auth_callback_t callback;
-	void *context;
+	unsigned int in_auth_reply:1;
 };
 
 static struct timeout *to;
 static struct auth_process *processes;
+static unsigned int auth_tag;
 
 static void auth_process_destroy(struct auth_process *p);
 
-static void push_request(struct auth_process *process, unsigned int id,
-			 auth_callback_t callback, void *context)
+static int handle_request(struct auth_process *process,
+			  struct auth_master_reply *reply,
+			  const unsigned char *data)
 {
-	struct waiting_request *req;
+	size_t nul_pos;
+	void *context;
+
+	context = hash_lookup(process->requests, POINTER_CAST(reply->tag));
+	if (context == NULL) {
+		i_error("Auth process %s sent unrequested reply with tag %u",
+			dec2str(process->pid), reply->tag);
+		return TRUE;
+	}
 
-	req = i_new(struct waiting_request, 1);
-	req->id = id;
-	req->callback = callback;
-	req->context = context;
+	/* make sure the reply looks OK */
+	if (reply->data_size == 0) {
+		nul_pos = 0;
+		data = (const unsigned char *) "";
+	} else {
+		nul_pos = reply->data_size-1;
+	}
+
+	if (data[nul_pos] != '\0') {
+		i_error("Auth process %s sent invalid reply",
+			dec2str(process->pid));
+		return FALSE;
+	}
 
-	*process->next_request = req;
-	process->next_request = &req->next;
+	/* fix the request so that all the values point to \0 terminated
+	   strings */
+	if (reply->system_user_idx >= reply->data_size)
+		reply->system_user_idx = nul_pos;
+	if (reply->virtual_user_idx >= reply->data_size)
+		reply->virtual_user_idx = nul_pos;
+	if (reply->home_idx >= reply->data_size)
+		reply->home_idx = nul_pos;
+	if (reply->mail_idx >= reply->data_size)
+		reply->mail_idx = nul_pos;
+
+	auth_master_callback(reply, data, context);
+	hash_remove(process->requests, POINTER_CAST(reply->tag));
+	return TRUE;
 }
 
-static void pop_request(struct auth_process *process,
-			struct auth_cookie_reply_data *reply)
+void auth_process_request(struct auth_process *process, unsigned int login_pid,
+			  unsigned int login_id, void *context)
 {
-	struct waiting_request *req;
+	struct auth_master_request req;
+	ssize_t ret;
 
-	req = process->requests;
-	if (req == NULL) {
-		i_warning("imap-auth %s sent us unrequested reply for id %d",
-			  dec2str(process->pid), reply->id);
-		return;
-	}
+	req.tag = ++auth_tag;
+	req.id = login_id;
+	req.login_pid = login_pid;
 
-	if (reply->id != req->id) {
-		i_fatal("imap-auth %s sent invalid id for reply "
-			"(got %d, expecting %d)",
-			dec2str(process->pid), reply->id, req->id);
-	}
-
-	/* auth process isn't trusted, validate all data to make sure
-	   it's not trying to exploit us */
-	if (!VALIDATE_STR(reply->system_user) ||
-	    !VALIDATE_STR(reply->virtual_user) || !VALIDATE_STR(reply->mail) ||
-	    !VALIDATE_STR(reply->home)) {
-		i_error("auth: Received corrupted data");
+	ret = o_stream_send(process->output, &req, sizeof(req));
+	if ((size_t)ret != sizeof(req)) {
+		if (ret >= 0) {
+			/* FIXME: well .. I'm not sure if it'd be better to
+			   just block here. I don't think this condition should
+			   happen often, so this could mean that the auth
+			   process is stuck. Or that the computer is just
+			   too heavily loaded. Possibility to block infinitely
+			   is annoying though, so for now don't do it. */
+			i_warning("Auth process %s transmit buffer full, "
+				  "killing..", dec2str(process->pid));
+		}
 		auth_process_destroy(process);
 		return;
 	}
 
-	process->requests = req->next;
-	if (process->requests == NULL)
-		process->next_request = &process->requests;
-
-	req->callback(reply, req->context);
-
-	i_free(req);
+	hash_insert(process->requests, POINTER_CAST(req.tag), context);
 }
 
-static void auth_process_input(void *context, int fd,
+static void auth_process_input(void *context, int fd __attr_unused__,
 			       struct io *io __attr_unused__)
 {
 	struct auth_process *p = context;
-	int ret;
+	const unsigned char *data;
+	size_t size;
 
-	ret = net_receive(fd, p->reply_buf + p->reply_pos,
-			  sizeof(p->reply_buf) - p->reply_pos);
-	if (ret < 0) {
+	switch (i_stream_read(p->input)) {
+	case 0:
+		return;
+	case -1:
 		/* disconnected */
 		auth_process_destroy(p);
 		return;
+	case -2:
+		/* buffer full */
+		i_error("BUG: Auth process %s sent us more than %d "
+			"bytes of data", dec2str(p->pid), (int)MAX_INBUF_SIZE);
+		auth_process_destroy(p);
+		return;
 	}
 
 	if (!p->initialized) {
-		if (p->reply_buf[0] != 'O') {
+		data = i_stream_get_data(p->input, &size);
+		i_assert(size > 0);
+
+		if (data[0] != 'O') {
 			i_fatal("Auth process sent invalid initialization "
 				"notification");
 		}
 
-		p->initialized = TRUE;
+		i_stream_skip(p->input, 1);
 
-		ret--;
-		memmove(p->reply_buf, p->reply_buf + 1, ret);
+		p->initialized = TRUE;
 	}
 
-	p->reply_pos += ret;
-	if (p->reply_pos < sizeof(p->reply_buf))
+	if (!p->in_auth_reply) {
+		data = i_stream_get_data(p->input, &size);
+		if (size < sizeof(struct auth_master_reply))
+			return;
+
+		p->in_auth_reply = TRUE;
+		memcpy(&p->auth_reply, data, sizeof(p->auth_reply));
+
+		i_stream_skip(p->input, sizeof(p->auth_reply));
+	}
+
+	data = i_stream_get_data(p->input, &size);
+	if (p->auth_reply.data_size < size)
 		return;
 
 	/* reply is now read */
-	pop_request(p, (struct auth_cookie_reply_data *) p->reply_buf);
-	p->reply_pos = 0;
+	if (!handle_request(p, &p->auth_reply, data))
+		auth_process_destroy(p);
+	else {
+		p->in_auth_reply = FALSE;
+		i_stream_skip(p->input, p->auth_reply.data_size);
+	}
 }
 
 static struct auth_process *
@@ -144,23 +189,29 @@
 	p->pid = pid;
 	p->fd = fd;
 	p->io = io_add(fd, IO_READ, auth_process_input, p);
+	p->input = i_stream_create_file(fd, default_pool,
+					MAX_INBUF_SIZE, FALSE);
 	p->output = o_stream_create_file(fd, default_pool,
-				sizeof(struct auth_cookie_request_data)*100,
-				IO_PRIORITY_DEFAULT, FALSE);
-
-	p->next_request = &p->requests;
+					 sizeof(struct auth_master_request)*100,
+					 IO_PRIORITY_DEFAULT, FALSE);
+	p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL);
 
 	p->next = processes;
 	processes = p;
 	return p;
 }
 
+static void request_hash_destroy(void *key __attr_unused__,
+				 void *value, void *context __attr_unused__)
+{
+	auth_master_callback(NULL, NULL, value);
+}
+
 static void auth_process_destroy(struct auth_process *p)
 {
 	struct auth_process **pos;
-	struct waiting_request *next;
 
-	if (!p->initialized) {
+	if (!p->initialized && io_loop_is_running(ioloop)) {
 		i_error("Auth process died too early - shutting down");
 		io_loop_stop(ioloop);
 	}
@@ -172,15 +223,12 @@
 		}
 	}
 
-	for (; p->requests != NULL; p->requests = next) {
-		next = p->requests->next;
-
-		p->requests->callback(NULL, p->requests->context);
-		i_free(p->requests);
-	}
-
 	(void)unlink(t_strconcat(set_login_dir, "/", p->name, NULL));
 
+	hash_foreach(p->requests, request_hash_destroy, NULL);
+	hash_destroy(p->requests);
+
+	i_stream_unref(p->input);
 	o_stream_unref(p->output);
 	io_remove(p->io);
 	if (close(p->fd) < 0)
@@ -275,8 +323,10 @@
 	env_put(t_strconcat("AUTH_PROCESS=", dec2str(getpid()), NULL));
 	env_put(t_strconcat("MECHANISMS=", config->mechanisms, NULL));
 	env_put(t_strconcat("REALMS=", config->realms, NULL));
-	env_put(t_strconcat("USERINFO=", config->userinfo, NULL));
-	env_put(t_strconcat("USERINFO_ARGS=", config->userinfo_args, NULL));
+	env_put(t_strconcat("USERDB=", config->userdb, NULL));
+	env_put(t_strconcat("USERDB_ARGS=", config->userdb_args, NULL));
+	env_put(t_strconcat("PASSDB=", config->passdb, NULL));
+	env_put(t_strconcat("PASSDB_ARGS=", config->passdb_args, NULL));
 
 	if (config->use_cyrus_sasl)
 		env_put("USE_CYRUS_SASL=1");
@@ -299,35 +349,18 @@
 	return -1;
 }
 
-struct auth_process *auth_process_find(unsigned int id)
+struct auth_process *auth_process_find(unsigned int pid)
 {
 	struct auth_process *p;
 
 	for (p = processes; p != NULL; p = p->next) {
-		if ((unsigned int)p->pid == id)
+		if ((unsigned int)p->pid == pid)
 			return p;
 	}
 
 	return NULL;
 }
 
-void auth_process_request(unsigned int login_pid,
-			  struct auth_process *process, unsigned int id,
-			  unsigned char cookie[AUTH_COOKIE_SIZE],
-			  auth_callback_t callback, void *context)
-{
-	struct auth_cookie_request_data req;
-
-	req.id = id;
-	req.login_pid = login_pid;
-	memcpy(req.cookie, cookie, AUTH_COOKIE_SIZE);
-
-	if (o_stream_send(process->output, &req, sizeof(req)) < 0)
-		auth_process_destroy(process);
-
-	push_request(process, id, callback, context);
-}
-
 static unsigned int auth_process_get_count(const char *name)
 {
 	struct auth_process *p;
--- a/src/master/auth-process.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/auth-process.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,18 +1,15 @@
 #ifndef __AUTH_PROCESS_H
 #define __AUTH_PROCESS_H
 
-/* cookie_reply is NULL if some error occured */
-typedef void (*auth_callback_t)(struct auth_cookie_reply_data *cookie_reply,
-				void *context);
+void auth_master_callback(struct auth_master_reply *reply,
+			  const unsigned char *data, void *context);
 
 /* Find process for given id */
-struct auth_process *auth_process_find(unsigned int id);
+struct auth_process *auth_process_find(unsigned int pid);
 
 /* Request information about given cookie */
-void auth_process_request(unsigned int login_pid,
-			  struct auth_process *process, unsigned int id,
-			  unsigned char cookie[AUTH_COOKIE_SIZE],
-			  auth_callback_t callback, void *context);
+void auth_process_request(struct auth_process *process, unsigned int login_pid,
+			  unsigned int login_id, void *context);
 
 /* Close any fds used by auth processes */
 void auth_processes_destroy_all(void);
--- a/src/master/common.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/common.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,12 +1,13 @@
 #ifndef __COMMON_H
 #define __COMMON_H
 
+struct ip_addr;
+
 #include "lib.h"
 #include "hash.h"
 #include "settings.h"
 
-#include "../auth/auth-interface.h"
-#include "master-interface.h"
+#include "../auth/auth-master-interface.h"
 
 enum {
 	PROCESS_TYPE_UNKNOWN,
@@ -34,13 +35,6 @@
 
 void clean_child_process(void);
 
-enum master_reply_result
-create_imap_process(int socket, struct ip_addr *ip,
-		    const char *system_user, const char *virtual_user,
-		    uid_t uid, gid_t gid, const char *home, int chroot,
-		    const char *mail, const char *login_tag);
-void imap_process_destroyed(pid_t pid);
-
 /* misc */
 #define VALIDATE_STR(str) \
 	validate_str(str, sizeof(str))
--- a/src/master/imap-process.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/imap-process.c	Mon Jan 27 03:33:40 2003 +0200
@@ -1,10 +1,14 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "fd-close-on-exec.h"
 #include "env-util.h"
 #include "str.h"
+#include "network.h"
 #include "restrict-access.h"
 #include "restrict-process-size.h"
+#include "var-expand.h"
+#include "imap-process.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -48,7 +52,7 @@
 	const char *const *chroot_dirs;
 
 	if (*dir == '\0')
-		return TRUE;
+		return FALSE;
 
 	if (set_valid_chroot_dirs == NULL)
 		return FALSE;
@@ -68,8 +72,7 @@
 				   const char *home)
 {
 	string_t *str;
-	const char *p, *var;
-	unsigned int width;
+	const char *p;
 
 	str = t_str_new(256);
 
@@ -91,93 +94,48 @@
 	}
 
 	/* expand %vars */
-	for (; *env != '\0'; env++) {
-		if (*env != '%')
-			str_append_c(str, *env);
-		else {
-			width = 0;
-			while (env[1] >= '0' && env[1] <= '9') {
-				width = width*10 + (env[1] - '0');
-				env++;
-			}
-
-			switch (env[1]) {
-			case '%':
-				var = "%";
-				break;
-			case 'u':
-				var = user;
-				break;
-			case 'h':
-				var = home;
-				break;
-			case 'n':
-				var = t_strcut(user, '@');
-				break;
-			case 'd':
-				var = strchr(user, '@');
-				if (var != NULL) var++;
-				break;
-			default:
-				var = NULL;
-				break;
-			}
-
-			if (var == NULL)
-				str_append_c(str, '%');
-			else {
-				env++;
-				if (width == 0)
-					str_append(str, var);
-				else
-					str_append_n(str, var, width);
-			}
-		}
-	}
-
-
+        var_expand(str, env, user, home);
 	return str_c(str);
 }
 
-enum master_reply_result
-create_imap_process(int socket, struct ip_addr *ip,
-		    const char *system_user, const char *virtual_user,
-		    uid_t uid, gid_t gid, const char *home, int chroot,
-		    const char *mail, const char *login_tag)
+int create_imap_process(int socket, struct ip_addr *ip,
+			struct auth_master_reply *reply,
+			const unsigned char *data)
 {
 	static char *argv[] = { NULL, NULL, NULL };
-	const char *host;
+	const char *host, *mail;
 	char title[1024];
 	pid_t pid;
 	int i, err;
 
 	if (imap_process_count == set_max_imap_processes) {
 		i_error("Maximum number of imap processes exceeded");
-		return MASTER_RESULT_INTERNAL_FAILURE;
+		return FALSE;
 	}
 
-	if (!validate_uid_gid(uid, gid))
-		return MASTER_RESULT_FAILURE;
+	if (!validate_uid_gid(reply->uid, reply->gid))
+		return FALSE;
 
-	if (chroot && !validate_chroot(home))
-		return MASTER_RESULT_FAILURE;
+	if (reply->chroot && !validate_chroot(data + reply->home_idx))
+		return FALSE;
 
 	pid = fork();
 	if (pid < 0) {
 		i_error("fork() failed: %m");
-		return MASTER_RESULT_INTERNAL_FAILURE;
+		return FALSE;
 	}
 
 	if (pid != 0) {
 		/* master */
 		imap_process_count++;
 		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_IMAP);
-		return MASTER_RESULT_SUCCESS;
+		return TRUE;
 	}
 
 	clean_child_process();
 
 	/* move the imap socket into stdin, stdout and stderr fds */
+	fd_close_on_exec(socket, FALSE);
 	for (i = 0; i < 3; i++) {
 		if (dup2(socket, i) < 0)
 			i_fatal("imap: dup2(%d) failed: %m", i);
@@ -188,10 +146,13 @@
 
 	/* setup environment - set the most important environment first
 	   (paranoia about filling up environment without noticing) */
-	restrict_access_set_env(system_user, uid, gid, chroot ? home : NULL);
+	restrict_access_set_env(data + reply->system_user_idx,
+				reply->uid, reply->gid,
+				reply->chroot ? data + reply->home_idx : NULL);
 	restrict_process_size(set_imap_process_size);
 
-	env_put(t_strconcat("HOME=", home, NULL));
+	env_put("LOGGED_IN=1");
+	env_put(t_strconcat("HOME=", data + reply->home_idx, NULL));
 	env_put(t_strconcat("MAIL_CACHE_FIELDS=", set_mail_cache_fields, NULL));
 	env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
 			    set_mail_never_cache_fields, NULL));
@@ -221,22 +182,23 @@
 	/* user given environment - may be malicious. virtual_user comes from
 	   auth process, but don't trust that too much either. Some auth
 	   mechanism might allow leaving extra data there. */
-	if ((mail == NULL || *mail == '\0') && set_default_mail_env != NULL) {
+	mail = data + reply->mail_idx;
+	if (*mail == '\0' && set_default_mail_env != NULL) {
 		mail = expand_mail_env(set_default_mail_env,
-				       virtual_user, home);
-		env_put(t_strconcat("MAIL=", mail, NULL));
+				       data + reply->virtual_user_idx,
+				       data + reply->home_idx);
 	}
 
 	env_put(t_strconcat("MAIL=", mail, NULL));
-	env_put(t_strconcat("USER=", virtual_user, NULL));
-	env_put(t_strconcat("LOGIN_TAG=", login_tag, NULL));
+	env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
 
 	if (set_verbose_proctitle) {
 		host = net_ip2host(ip);
 		if (host == NULL)
 			host = "??";
 
-		i_snprintf(title, sizeof(title), "[%s %s]", virtual_user, host);
+		i_snprintf(title, sizeof(title), "[%s %s]",
+			   data + reply->virtual_user_idx, host);
 		argv[1] = title;
 	}
 
@@ -257,7 +219,7 @@
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", set_imap_executable);
 
 	/* not reached */
-	return 0;
+	return FALSE;
 }
 
 void imap_process_destroyed(pid_t pid __attr_unused__)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/imap-process.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,11 @@
+#ifndef __IMAP_PROCESS_H
+#define __IMAP_PROCESS_H
+
+struct auth_master_reply;
+
+int create_imap_process(int socket, struct ip_addr *ip,
+			struct auth_master_reply *reply,
+			const unsigned char *data);
+void imap_process_destroyed(pid_t pid);
+
+#endif
--- a/src/master/login-process.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/login-process.c	Mon Jan 27 03:33:40 2003 +0200
@@ -11,7 +11,8 @@
 #include "restrict-process-size.h"
 #include "login-process.h"
 #include "auth-process.h"
-#include "master-interface.h"
+#include "imap-process.h"
+#include "master-login-interface.h"
 
 #include <unistd.h>
 #include <syslog.h>
@@ -31,12 +32,12 @@
 
 struct login_auth_request {
 	struct login_process *process;
-	unsigned int login_id;
-	unsigned int auth_id;
+	unsigned int tag;
+
+	unsigned int login_tag;
 	int fd;
 
 	struct ip_addr ip;
-	char login_tag[LOGIN_TAG_SIZE];
 };
 
 static unsigned int auth_id_counter;
@@ -51,38 +52,30 @@
 static void login_process_destroy(struct login_process *p);
 static void login_process_unref(struct login_process *p);
 
-static void auth_callback(struct auth_cookie_reply_data *cookie_reply,
-			  void *context)
+void auth_master_callback(struct auth_master_reply *reply,
+			  const unsigned char *data, void *context)
 {
 	struct login_auth_request *request = context;
-        struct login_process *process;
-	struct master_reply reply;
+	struct master_login_reply master_reply;
 
-	if (cookie_reply == NULL || !cookie_reply->success)
-		reply.result = MASTER_RESULT_FAILURE;
+	if (reply == NULL || !reply->success)
+		master_reply.success = FALSE;
 	else {
-		reply.result = create_imap_process(request->fd,
-						   &request->ip,
-						   cookie_reply->system_user,
-						   cookie_reply->virtual_user,
-						   cookie_reply->uid,
-						   cookie_reply->gid,
-						   cookie_reply->home,
-						   cookie_reply->chroot,
-						   cookie_reply->mail,
-						   request->login_tag);
+		master_reply.success = create_imap_process(request->fd,
+							   &request->ip,
+							   reply, data);
 	}
 
 	/* reply to login */
-	reply.id = request->login_id;
+	master_reply.tag = request->login_tag;
 
-	process = request->process;
-	if (o_stream_send(process->output, &reply, sizeof(reply)) < 0)
-		login_process_destroy(process);
+	if (o_stream_send(request->process->output, &master_reply,
+			  sizeof(master_reply)) < 0)
+		login_process_destroy(request->process);
 
 	if (close(request->fd) < 0)
 		i_error("close(imap client) failed: %m");
-	login_process_unref(process);
+	login_process_unref(request->process);
 	i_free(request);
 }
 
@@ -113,7 +106,7 @@
 	struct login_process *p = context;
 	struct auth_process *auth_process;
 	struct login_auth_request *authreq;
-	struct master_request req;
+	struct master_login_request req;
 	int client_fd, ret;
 
 	ret = fd_read(p->fd, &req, sizeof(req), &client_fd);
@@ -148,36 +141,25 @@
 		return;
 	}
 
-	/* login process isn't trusted, validate all data to make sure
-	   it's not trying to exploit us */
-	if (!VALIDATE_STR(req.login_tag)) {
-		i_error("login: Received corrupted data");
-		if (close(client_fd) < 0)
-			i_error("close(imap client) failed: %m");
-		login_process_destroy(p);
-		return;
-	}
+	fd_close_on_exec(client_fd, TRUE);
 
 	/* ask the cookie from the auth process */
 	authreq = i_new(struct login_auth_request, 1);
 	p->refcount++;
 	authreq->process = p;
-	authreq->login_id = req.id;
-	authreq->auth_id = ++auth_id_counter;
+	authreq->tag = ++auth_id_counter;
+	authreq->login_tag = req.tag;
 	authreq->fd = client_fd;
-	memcpy(&authreq->ip, &req.ip, sizeof(struct ip_addr));
-	if (strocpy(authreq->login_tag, req.login_tag,
-		    sizeof(authreq->login_tag)) < 0)
-		i_panic("login_tag overflow");
+	authreq->ip = req.ip;
 
-	auth_process = auth_process_find(req.auth_process);
+	auth_process = auth_process_find(req.auth_pid);
 	if (auth_process == NULL) {
 		i_error("login: Authentication process %u doesn't exist",
-			req.auth_process);
-		auth_callback(NULL, authreq);
+			req.auth_pid);
+		auth_master_callback(NULL, NULL, authreq);
 	} else {
-		auth_process_request(p->pid, auth_process, authreq->auth_id,
-				     req.cookie, auth_callback, authreq);
+		auth_process_request(auth_process, p->pid,
+				     req.auth_id, authreq);
 	}
 }
 
@@ -194,7 +176,7 @@
 	p->listening = TRUE;
 	p->io = io_add(fd, IO_READ, login_process_input, p);
 	p->output = o_stream_create_file(fd, default_pool,
-					 sizeof(struct master_reply)*10,
+					 sizeof(struct master_login_reply)*10,
 					 IO_PRIORITY_DEFAULT, FALSE);
 
 	hash_insert(processes, POINTER_CAST(pid), p);
@@ -223,7 +205,7 @@
 		return;
 	p->destroyed = TRUE;
 
-	if (!p->initialized) {
+	if (!p->initialized && io_loop_is_running(ioloop)) {
 		i_error("Login process died too early - shutting down");
 		io_loop_stop(ioloop);
 	}
--- a/src/master/login-process.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/login-process.h	Mon Jan 27 03:33:40 2003 +0200
@@ -1,5 +1,5 @@
-#ifndef __CHILD_LOGIN_H
-#define __CHILD_LOGIN_H
+#ifndef __LOGIN_PROCESS_H
+#define __LOGIN_PROCESS_H
 
 void login_process_abormal_exit(pid_t pid);
 void login_processes_destroy_all(void);
--- a/src/master/main.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/main.c	Mon Jan 27 03:33:40 2003 +0200
@@ -9,6 +9,7 @@
 
 #include "auth-process.h"
 #include "login-process.h"
+#include "imap-process.h"
 #include "ssl-init.h"
 
 #include <stdio.h>
--- a/src/master/master-interface.h	Mon Jan 27 03:27:51 2003 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#ifndef __MASTER_INTERFACE_H
-#define __MASTER_INTERFACE_H
-
-#include "network.h"
-#include "../auth/auth-interface.h"
-
-#define LOGIN_TAG_SIZE 32
-
-#define LOGIN_MASTER_SOCKET_FD 0
-#define LOGIN_IMAP_LISTEN_FD 1
-#define LOGIN_IMAPS_LISTEN_FD 2
-
-enum master_reply_result {
-	MASTER_RESULT_INTERNAL_FAILURE,
-	MASTER_RESULT_SUCCESS,
-	MASTER_RESULT_FAILURE
-};
-
-struct master_request {
-	unsigned int id;
-
-	unsigned int auth_process;
-	unsigned char cookie[AUTH_COOKIE_SIZE];
-
-	struct ip_addr ip;
-	char login_tag[LOGIN_TAG_SIZE];
-};
-
-struct master_reply {
-	unsigned int id;
-	enum master_reply_result result;
-};
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/master-login-interface.h	Mon Jan 27 03:33:40 2003 +0200
@@ -0,0 +1,24 @@
+#ifndef __MASTER_LOGIN_INTERFACE_H
+#define __MASTER_LOGIN_INTERFACE_H
+
+#include "network.h"
+
+#define LOGIN_MASTER_SOCKET_FD 0
+#define LOGIN_IMAP_LISTEN_FD 1
+#define LOGIN_IMAPS_LISTEN_FD 2
+
+struct master_login_request {
+	unsigned int tag;
+
+	unsigned int auth_pid;
+	unsigned int auth_id;
+
+	struct ip_addr ip;
+};
+
+struct master_login_reply {
+	unsigned int tag;
+	int success;
+};
+
+#endif
--- a/src/master/settings.c	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/settings.c	Mon Jan 27 03:33:40 2003 +0200
@@ -342,8 +342,10 @@
 	i_free(auth->name);
 	i_free(auth->mechanisms);
 	i_free(auth->realms);
-	i_free(auth->userinfo);
-	i_free(auth->userinfo_args);
+	i_free(auth->userdb);
+	i_free(auth->userdb_args);
+	i_free(auth->passdb);
+	i_free(auth->passdb_args);
 	i_free(auth->executable);
 	i_free(auth->user);
 	i_free(auth->chroot);
@@ -380,8 +382,6 @@
 	/* check the easy string values first */
 	if (strcmp(key, "auth_mechanisms") == 0)
 		ptr = &auth->mechanisms;
-	else if (strcmp(key, "auth_methods") == 0) /* backwards compatibility */
-		ptr = &auth->mechanisms;
 	else if (strcmp(key, "auth_realms") == 0)
 		ptr = &auth->realms;
 	else if (strcmp(key, "auth_executable") == 0)
@@ -399,18 +399,33 @@
 		return NULL;
 	}
 
-	if (strcmp(key, "auth_userinfo") == 0) {
-		/* split it into userinfo + userinfo_args */
+	if (strcmp(key, "auth_userdb") == 0) {
+		/* split it into userdb + userdb_args */
 		for (p = value; *p != ' ' && *p != '\0'; )
 			p++;
 
-		i_free(auth->userinfo);
-		auth->userinfo = i_strdup_until(value, p);
+		i_free(auth->userdb);
+		auth->userdb = i_strdup_until(value, p);
 
 		while (*p == ' ') p++;
 
-		i_free(auth->userinfo_args);
-		auth->userinfo_args = i_strdup(p);
+		i_free(auth->userdb_args);
+		auth->userdb_args = i_strdup(p);
+		return NULL;
+	}
+
+	if (strcmp(key, "auth_passdb") == 0) {
+		/* split it into passdb + passdb_args */
+		for (p = value; *p != ' ' && *p != '\0'; )
+			p++;
+
+		i_free(auth->passdb);
+		auth->passdb = i_strdup_until(value, p);
+
+		while (*p == ' ') p++;
+
+		i_free(auth->passdb_args);
+		auth->passdb_args = i_strdup(p);
 		return NULL;
 	}
 
--- a/src/master/settings.h	Mon Jan 27 03:27:51 2003 +0200
+++ b/src/master/settings.h	Mon Jan 27 03:33:40 2003 +0200
@@ -66,7 +66,8 @@
 	char *name;
 	char *mechanisms;
 	char *realms;
-	char *userinfo, *userinfo_args;
+	char *userdb, *userdb_args;
+	char *passdb, *passdb_args;
 	char *executable;
 	char *user;
 	char *chroot;