changeset 2733:9b9d9c164a31 HEAD

Login process cleanups. Share more authentication code between pop3/imap.
author Timo Sirainen <tss@iki.fi>
date Mon, 11 Oct 2004 20:14:26 +0300
parents 69c521ecf570
children e2ce951fa8e6
files src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/login-common/Makefile.am src/login-common/auth-common.c src/login-common/auth-common.h src/login-common/client-common.h src/login-common/sasl-server.c src/login-common/sasl-server.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h
diffstat 12 files changed, 518 insertions(+), 751 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/imap-login/client-authenticate.c	Mon Oct 11 20:14:26 2004 +0300
@@ -11,14 +11,8 @@
 #include "str-sanitize.h"
 #include "imap-parser.h"
 #include "auth-client.h"
-#include "ssl-proxy.h"
 #include "client.h"
 #include "client-authenticate.h"
-#include "auth-common.h"
-#include "master.h"
-
-/* Used only for string sanitization while verbose_auth is set. */
-#define MAX_MECH_NAME 64
 
 const char *client_authenticate_get_capabilities(int secured)
 {
@@ -45,214 +39,6 @@
 	return str_c(str);
 }
 
-static void client_auth_abort(struct imap_client *client, const char *msg)
-{
-	client->authenticating = FALSE;
-
-	if (client->common.auth_request != NULL) {
-		auth_client_request_abort(client->common.auth_request);
-		client->common.auth_request = NULL;
-	}
-
-	if (msg != NULL && verbose_auth)
-		client_syslog(client, "Authentication failed: %s", msg);
-
-	client_send_tagline(client, msg != NULL ?
-			    t_strconcat("NO ", msg, NULL) :
-			    "NO Authentication failed.");
-
-	/* get back to normal client input */
-	if (client->common.io != NULL)
-		io_remove(client->common.io);
-	client->common.io = client->common.fd == -1 ? NULL :
-		io_add(client->common.fd, IO_READ, client_input, client);
-
-	client_unref(client);
-}
-
-static void master_callback(struct client *_client, int success)
-{
-	struct imap_client *client = (struct imap_client *) _client;
-	const char *reason = NULL;
-
-	if (success) {
-		reason = t_strconcat("Login: ", client->common.virtual_user,
-				     NULL);
-	} else {
-		reason = t_strconcat("Internal login failure: ",
-				     client->common.virtual_user, NULL);
-		client_send_line(client, "* BYE Internal login failure. "
-				 "Error report written to server log.");
-	}
-
-	client_destroy(client, reason);
-}
-
-static void client_send_auth_data(struct imap_client *client,
-				  const unsigned char *data, size_t size)
-{
-	buffer_t *buf;
-	const void *buf_data;
-	size_t buf_size;
-	ssize_t ret;
-
-	t_push();
-
-	buf = buffer_create_dynamic(pool_datastack_create(), size*2);
-	buffer_append(buf, "+ ", 2);
-	base64_encode(data, size, buf);
-	buffer_append(buf, "\r\n", 2);
-
-	buf_data = buffer_get_data(buf, &buf_size);
-	if ((ret = o_stream_send(client->output, buf_data, buf_size)) < 0)
-		client_destroy(client, "Disconnected");
-	else if ((size_t)ret != buf_size)
-		client_destroy(client, "Transmit buffer full");
-
-	t_pop();
-}
-
-static void login_callback(struct auth_request *request,
-			   struct auth_client_request_reply *reply,
-			   const unsigned char *data, void *context)
-{
-	struct imap_client *client = context;
-	const char *error;
-
-	switch (auth_callback(request, reply, data, &client->common,
-			      master_callback, &error)) {
-	case -1:
-	case 0:
-		/* login failed */
-		client_auth_abort(client, error);
-		break;
-
-	default:
-		/* success, we should be able to log in. if we fail, just
-		   disconnect the client. */
-                client->authenticating = FALSE;
-		client_send_tagline(client, "OK Logged in.");
-		client_unref(client);
-	}
-}
-
-static enum auth_client_request_new_flags
-client_get_auth_flags(struct imap_client *client)
-{
-        enum auth_client_request_new_flags auth_flags = 0;
-
-	if (client->common.proxy != NULL &&
-	    ssl_proxy_has_valid_client_cert(client->common.proxy))
-		auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT;
-	if (client->tls)
-		auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED;
-	return auth_flags;
-}
-
-int cmd_login(struct imap_client *client, struct imap_arg *args)
-{
-	const char *user, *pass, *error;
-	struct auth_request_info info;
-	string_t *plain_login;
-
-	/* two arguments: username and password */
-	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
-		return FALSE;
-	if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING)
-		return FALSE;
-	if (args[2].type != IMAP_ARG_EOL)
-		return FALSE;
-
-	user = IMAP_ARG_STR(&args[0]);
-	pass = IMAP_ARG_STR(&args[1]);
-
-	if (!client->secured && disable_plaintext_auth) {
-		if (verbose_auth) {
-			client_syslog(client, "Login failed: "
-				      "Plaintext authentication disabled");
-		}
-		client_send_line(client,
-			"* BAD [ALERT] Plaintext authentication is disabled, "
-			"but your client sent password in plaintext anyway. "
-			"If anyone was listening, the password was exposed.");
-		client_send_tagline(client,
-				    "NO Plaintext authentication disabled.");
-		return TRUE;
-	}
-
-	/* authorization ID \0 authentication ID \0 pass */
-	plain_login = t_str_new(64);
-	str_append_c(plain_login, '\0');
-	str_append(plain_login, user);
-	str_append_c(plain_login, '\0');
-	str_append(plain_login, pass);
-
-	memset(&info, 0, sizeof(info));
-	info.mech = "PLAIN";
-	info.protocol = "IMAP";
-	info.flags = client_get_auth_flags(client);
-	info.local_ip = client->common.local_ip;
-	info.remote_ip = client->common.ip;
-	info.initial_resp_data = str_data(plain_login);
-	info.initial_resp_size = str_len(plain_login);
-
-	client_ref(client);
-
-	client->common.auth_request =
-		auth_client_request_new(auth_client, NULL, &info,
-					login_callback, client, &error);
-	if (client->common.auth_request == NULL) {
-		if (verbose_auth)
-			client_syslog(client, "Login failed: %s", error);
-		client_send_tagline(client, t_strconcat(
-			"NO Login failed: ", error, NULL));
-		client_unref(client);
-		return TRUE;
-	}
-
-	/* don't read any input from client until login is finished */
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
-	}
-
-	client->authenticating = TRUE;
-	return TRUE;
-}
-
-static void authenticate_callback(struct auth_request *request,
-				  struct auth_client_request_reply *reply,
-				  const unsigned char *data, void *context)
-{
-	struct imap_client *client = context;
-	const char *error;
-
-	if (!client->authenticating) {
-		/* client aborted */
-		i_assert(reply == NULL);
-		return;
-	}
-
-	switch (auth_callback(request, reply, data, &client->common,
-			      master_callback, &error)) {
-	case -1:
-		/* login failed */
-		client_auth_abort(client, error);
-		break;
-
-	case 0:
-		/* continue */
-		client_send_auth_data(client, data, reply->data_size);
-		break;
-	default:
-		/* success, we should be able to log in. if we fail, just
-		   disconnect the client. */
-                client->authenticating = FALSE;
-		client_send_tagline(client, "OK Logged in.");
-		client_unref(client);
-	}
-}
-
 static void client_auth_input(void *context)
 {
 	struct imap_client *client = context;
@@ -276,7 +62,8 @@
 		return;
 
 	if (strcmp(line, "*") == 0) {
-		client_auth_abort(client, "Authentication aborted");
+		sasl_server_auth_cancel(&client->common,
+					"Authentication aborted");
 		return;
 	}
 
@@ -285,13 +72,13 @@
 
 	if (base64_decode(line, linelen, NULL, buf) < 0) {
 		/* failed */
-		client_auth_abort(client, "Invalid base64 data");
+		sasl_server_auth_cancel(&client->common, "Invalid base64 data");
 	} else if (client->common.auth_request == NULL) {
-		client_auth_abort(client, "Don't send unrequested data");
+		sasl_server_auth_cancel(&client->common,
+					"Don't send unrequested data");
 	} else {
 		auth_client_request_continue(client->common.auth_request,
-					     buffer_get_data(buf, NULL),
-					     buffer_get_used_size(buf));
+					     buf->data, buf->used);
 	}
 
 	/* clear sensitive data */
@@ -301,75 +88,142 @@
 	safe_memset(buffer_free_without_data(buf), 0, bufsize);
 }
 
+static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
+			  const char *data)
+{
+	struct imap_client *client = (struct imap_client *)_client;
+	struct const_iovec iov[3];
+	size_t data_len;
+	ssize_t ret;
+
+	switch (reply) {
+	case SASL_SERVER_REPLY_SUCCESS:
+		client_send_tagline(client, "OK Logged in.");
+		client_destroy(client, t_strconcat(
+			"Login: ", client->common.virtual_user, NULL));
+		break;
+	case SASL_SERVER_REPLY_AUTH_FAILED:
+		if (data == NULL)
+			client_send_tagline(client, "Authentication failed");
+		else {
+			client_send_tagline(client, t_strconcat(
+				"NO Authentication failed: ", data, NULL));
+		}
+
+		/* get back to normal client input. */
+		if (client->io != NULL)
+			io_remove(client->io);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_input, client);
+		break;
+	case SASL_SERVER_REPLY_MASTER_FAILED:
+		client_send_line(client, "* BYE Internal login failure. "
+				 "Error report written to server log.");
+		client_destroy(client, t_strconcat("Internal login failure: ",
+						   client->common.virtual_user,
+						   NULL));
+		break;
+	case SASL_SERVER_REPLY_CONTINUE:
+		data_len = strlen(data);
+		iov[0].iov_base = "+ ";
+		iov[0].iov_len = 2;
+		iov[1].iov_base = data;
+		iov[1].iov_len = data_len;
+		iov[2].iov_base = "\r\n";
+		iov[2].iov_len = 2;
+
+		ret = o_stream_sendv(client->output, iov, 3);
+		if (ret < 0)
+			client_destroy(client, "Disconnected");
+		else if ((size_t)ret != 2 + data_len + 2)
+			client_destroy(client, "Transmit buffer full");
+		else {
+			/* continue */
+			return;
+		}
+		break;
+	}
+
+	client_unref(client);
+}
+
 int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
 {
-	const struct auth_mech_desc *mech;
-	const char *mech_name, *error;
-	struct auth_request_info info;
+	const char *mech_name;
 
 	/* we want only one argument: authentication mechanism name */
 	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
-		return FALSE;
+		return -1;
 	if (args[1].type != IMAP_ARG_EOL)
-		return FALSE;
+		return -1;
 
 	mech_name = IMAP_ARG_STR(&args[0]);
 	if (*mech_name == '\0')
 		return FALSE;
 
-	mech = auth_client_find_mech(auth_client, mech_name);
-	if (mech == NULL) {
+	client_ref(client);
+	sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL, 0,
+			       sasl_callback);
+	if (!client->common.authenticating)
+		return 1;
+
+	/* following input data will go to authentication */
+	if (client->io != NULL)
+		io_remove(client->io);
+	client->io = io_add(client->common.fd, IO_READ,
+			    client_auth_input, client);
+	return 0;
+}
+
+int cmd_login(struct imap_client *client, struct imap_arg *args)
+{
+	const char *user, *pass;
+	string_t *plain_login;
+
+	/* two arguments: username and password */
+	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
+		return -1;
+	if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING)
+		return -1;
+	if (args[2].type != IMAP_ARG_EOL)
+		return -1;
+
+	user = IMAP_ARG_STR(&args[0]);
+	pass = IMAP_ARG_STR(&args[1]);
+
+	if (!client->common.secured && disable_plaintext_auth) {
 		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: "
-				      "Unsupported mechanism",
-				      str_sanitize(mech_name, MAX_MECH_NAME));
+			client_syslog(&client->common, "Login failed: "
+				      "Plaintext authentication disabled");
 		}
-		client_send_tagline(client,
-				    "NO Unsupported authentication mechanism.");
-		return TRUE;
-	}
-
-	if (!client->secured && mech->plaintext && disable_plaintext_auth) {
-		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: "
-				      "Plaintext authentication disabled",
-				      str_sanitize(mech_name, MAX_MECH_NAME));
-		}
+		client_send_line(client,
+			"* BAD [ALERT] Plaintext authentication is disabled, "
+			"but your client sent password in plaintext anyway. "
+			"If anyone was listening, the password was exposed.");
 		client_send_tagline(client,
 				    "NO Plaintext authentication disabled.");
-		return TRUE;
+		return 1;
 	}
 
-	memset(&info, 0, sizeof(info));
-	info.mech = mech->name;
-	info.protocol = "IMAP";
-	info.flags = client_get_auth_flags(client);
-	info.local_ip = client->common.local_ip;
-	info.remote_ip = client->common.ip;
+	/* authorization ID \0 authentication ID \0 pass */
+	plain_login = buffer_create_dynamic(pool_datastack_create(), 64);
+	buffer_append_c(plain_login, '\0');
+	buffer_append(plain_login, user, strlen(user));
+	buffer_append_c(plain_login, '\0');
+	buffer_append(plain_login, pass, strlen(pass));
 
 	client_ref(client);
-	o_stream_uncork(client->output);
+	sasl_server_auth_begin(&client->common, "IMAP", "PLAIN",
+			       plain_login->data, plain_login->used,
+			       sasl_callback);
+	if (!client->common.authenticating)
+		return 1;
 
-	client->common.auth_request =
-		auth_client_request_new(auth_client, NULL, &info,
-					authenticate_callback, client, &error);
-	if (client->common.auth_request != NULL) {
-		/* following input data will go to authentication */
-		if (client->common.io != NULL)
-			io_remove(client->common.io);
-		client->common.io = io_add(client->common.fd, IO_READ,
-					   client_auth_input, client);
-                client->authenticating = TRUE;
-	} else {
-		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: %s",
-				      str_sanitize(mech_name, MAX_MECH_NAME),
-				      error);
-		}
-		client_send_tagline(client, t_strconcat(
-			"NO Authentication failed: ", error, NULL));
-		client_unref(client);
+	/* don't read any input from client until login is finished */
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 
-	return TRUE;
+	return 0;
 }
--- a/src/imap-login/client.c	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/imap-login/client.c	Mon Oct 11 20:14:26 2004 +0300
@@ -56,8 +56,8 @@
 	if (addr == NULL)
 		addr = "??";
 
-	process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
-					  addr));
+	process_title_set(t_strdup_printf(client->common.tls ?
+					  "[%s TLS]" : "[%s]", addr));
 }
 
 static void client_open_streams(struct imap_client *client, int fd)
@@ -93,10 +93,11 @@
 {
 	const char *auths;
 
-	auths = client_authenticate_get_capabilities(client->secured);
+	auths = client_authenticate_get_capabilities(client->common.secured);
 	return t_strconcat(CAPABILITY_STRING,
-			   (ssl_initialized && !client->tls) ? " STARTTLS" : "",
-			   disable_plaintext_auth && !client->secured ?
+			   (ssl_initialized && !client->common.tls) ?
+			   " STARTTLS" : "",
+			   disable_plaintext_auth && !client->common.secured ?
 			   " LOGINDISABLED" : "", auths, NULL);
 }
 
@@ -120,8 +121,8 @@
 		return;
 	}
 
-	client->tls = TRUE;
-	client->secured = TRUE;
+	client->common.tls = TRUE;
+	client->common.secured = TRUE;
 	client_set_title(client);
 
 	client->common.fd = fd_ssl;
@@ -133,8 +134,7 @@
 	client->skip_line = FALSE;
 
 	client_open_streams(client, fd_ssl);
-	client->common.io = io_add(client->common.fd, IO_READ,
-				   client_input, client);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
 }
 
 static void client_output_starttls(void *context)
@@ -153,7 +153,7 @@
 
 static int cmd_starttls(struct imap_client *client)
 {
-	if (client->tls) {
+	if (client->common.tls) {
 		client_send_tagline(client, "BAD TLS is already active.");
 		return TRUE;
 	}
@@ -165,9 +165,9 @@
 
 	/* remove input handler, SSL proxy gives us a new fd. we also have to
 	   remove it in case we have to wait for buffer to be flushed */
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 
 	client_send_tagline(client, "OK Begin TLS negotiation now.");
@@ -212,17 +212,16 @@
 	if (strcmp(cmd, "LOGOUT") == 0)
 		return cmd_logout(client);
 
-	return FALSE;
+	return -1;
 }
 
 static int client_handle_input(struct imap_client *client)
 {
 	struct imap_arg *args;
 	const char *msg;
-	int fatal;
+	int ret, fatal;
 
-	if (client->authenticating)
-		return FALSE; /* wait until authentication is finished */
+	i_assert(!client->common.authenticating);
 
 	if (client->cmd_finished) {
 		/* clear the previous command from memory. don't do this
@@ -276,8 +275,13 @@
 	}
 	client->skip_line = TRUE;
 
-	if (*client->cmd_tag == '\0' ||
-	    !client_command_execute(client, client->cmd_name, args)) {
+	if (*client->cmd_tag == '\0')
+		ret = -1;
+	else
+		ret = client_command_execute(client, client->cmd_name, args);
+
+	client->cmd_finished = TRUE;
+	if (ret < 0) {
 		if (*client->cmd_tag == '\0')
 			client->cmd_tag = "*";
 		if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
@@ -286,13 +290,12 @@
 			client_destroy(client, "Disconnected: "
 				       "Too many invalid commands");
 			return FALSE;
-		} 
+		}  
 		client_send_tagline(client,
 			"BAD Error in IMAP command received by server.");
 	}
 
-	client->cmd_finished = TRUE;
-	return TRUE;
+	return ret != 0;
 }
 
 int client_read(struct imap_client *client)
@@ -397,10 +400,10 @@
 	client = i_new(struct imap_client, 1);
 	client->created = ioloop_time;
 	client->refcount = 1;
-	client->tls = ssl;
+	client->common.tls = ssl;
 
         addr = net_ip2addr(ip);
-	client->secured = ssl ||
+	client->common.secured = ssl ||
 		(IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
 		(IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0);
 
@@ -409,7 +412,7 @@
 	client->common.fd = fd;
 
 	client_open_streams(client, fd);
-	client->common.io = io_add(fd, IO_READ, client_input, client);
+	client->io = io_add(fd, IO_READ, client_input, client);
 
 	client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
@@ -434,7 +437,7 @@
 	client->destroyed = TRUE;
 
 	if (reason != NULL)
-		client_syslog(client, "%s", reason);
+		client_syslog(&client->common, "%s", reason);
 
 	hash_remove(clients, client);
 
@@ -449,9 +452,9 @@
 	if (client->common.master_tag != 0)
 		master_request_abort(&client->common);
 
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 
 	if (client->common.fd != -1) {
@@ -507,22 +510,6 @@
 	client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
 }
 
-void client_syslog(struct imap_client *client, const char *format, ...)
-{
-	const char *addr;
-	va_list args;
-
-	addr = net_ip2addr(&client->common.ip);
-	if (addr == NULL)
-		addr = "??";
-
-	t_push();
-	va_start(args, format);
-	i_info("%s [%s]", t_strdup_vprintf(format, args), addr);
-	va_end(args);
-	t_pop();
-}
-
 static void client_check_idle(struct imap_client *client)
 {
 	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
--- a/src/imap-login/client.h	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/imap-login/client.h	Mon Oct 11 20:14:26 2004 +0300
@@ -11,6 +11,7 @@
 	time_t created;
 	int refcount;
 
+	struct io *io;
 	struct istream *input;
 	struct ostream *output;
 	struct imap_parser *parser;
@@ -20,12 +21,9 @@
 
 	const char *cmd_tag, *cmd_name;
 
-	unsigned int tls:1;
-	unsigned int secured:1;
 	unsigned int cmd_finished:1;
 	unsigned int skip_line:1;
 	unsigned int input_blocked:1;
-	unsigned int authenticating:1;
 	unsigned int destroyed:1;
 };
 
@@ -33,8 +31,6 @@
 
 void client_send_line(struct imap_client *client, const char *line);
 void client_send_tagline(struct imap_client *client, const char *line);
-void client_syslog(struct imap_client *client, const char *format, ...)
-	__attr_format__(2, 3);
 
 int client_read(struct imap_client *client);
 void client_input(void *context);
--- a/src/login-common/Makefile.am	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/login-common/Makefile.am	Mon Oct 11 20:14:26 2004 +0300
@@ -7,15 +7,15 @@
 	-DSBINDIR=\""$(sbindir)"\"
 
 liblogin_common_a_SOURCES = \
-	auth-common.c \
+	client-common.c \
 	main.c \
 	master.c \
+	sasl-server.c \
 	ssl-proxy.c \
 	ssl-proxy-gnutls.c \
 	ssl-proxy-openssl.c
 
 noinst_HEADERS = \
-	auth-common.h \
 	client-common.h \
 	common.h \
 	master.h \
--- a/src/login-common/auth-common.c	Mon Oct 11 17:29:51 2004 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "ioloop.h"
-#include "client-common.h"
-#include "auth-client.h"
-#include "auth-common.h"
-
-static const char *auth_client_get_str(struct auth_client_request_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 + idx, stop);
-}
-
-int auth_callback(struct auth_request *request,
-		  struct auth_client_request_reply *reply,
-		  const unsigned char *data, struct client *client,
-		  master_callback_t *master_callback, const char **error_r)
-{
-	const char *user;
-
-	*error_r = NULL;
-
-	if (reply == NULL) {
-		/* failed */
-		client->auth_request = NULL;
-		*error_r = "Authentication process died.";
-		return -1;
-	}
-
-	switch (reply->result) {
-	case AUTH_CLIENT_RESULT_CONTINUE:
-		if (client->auth_request != NULL) {
-			i_assert(client->auth_request == request);
-		} else {
-			i_assert(client->auth_request == NULL);
-
-			client->auth_request = request;
-		}
-		return 0;
-
-	case AUTH_CLIENT_RESULT_SUCCESS:
-		client->auth_request = NULL;
-
-		user = auth_client_get_str(reply, data, reply->username_idx);
-
-		i_free(client->virtual_user);
-		client->virtual_user = i_strdup(user);
-
-		master_request_login(client, master_callback,
-			auth_client_request_get_server_pid(request),
-			auth_client_request_get_id(request));
-
-		/* disable IO until we're back from master */
-		if (client->io != NULL) {
-			io_remove(client->io);
-			client->io = NULL;
-		}
-		return 1;
-
-	case AUTH_CLIENT_RESULT_FAILURE:
-		/* see if we have error message */
-		client->auth_request = NULL;
-
-		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
-			*error_r = t_strconcat("Authentication failed: ",
-					       (const char *) data, NULL);
-		}
-		return -1;
-	}
-
-	i_unreached();
-}
--- a/src/login-common/auth-common.h	Mon Oct 11 17:29:51 2004 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-#ifndef __AUTH_COMMON_H
-#define __AUTH_COMMON_H
-
-int auth_callback(struct auth_request *request,
-		  struct auth_client_request_reply *reply,
-		  const unsigned char *data, struct client *client,
-		  master_callback_t *master_callback, const char **error_r);
-
-#endif
-
--- a/src/login-common/client-common.h	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/login-common/client-common.h	Mon Oct 11 20:14:26 2004 +0300
@@ -3,6 +3,7 @@
 
 #include "network.h"
 #include "master.h"
+#include "sasl-server.h"
 
 struct client {
 	struct ip_addr local_ip;
@@ -10,19 +11,27 @@
 	struct ssl_proxy *proxy;
 
 	int fd;
-	struct io *io;
 
+	char *auth_mech_name;
 	struct auth_request *auth_request;
+
 	unsigned int master_tag;
 	master_callback_t *master_callback;
+	sasl_server_callback_t *sasl_callback;
 
 	char *virtual_user;
+	unsigned int tls:1;
+	unsigned int secured:1;
+	unsigned int authenticating:1;
 	/* ... */
 };
 
 struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
 			     const struct ip_addr *ip);
 
+void client_syslog(struct client *client, const char *format, ...)
+	__attr_format__(2, 3);
+
 unsigned int clients_get_count(void);
 void clients_notify_auth_connected(void);
 void clients_destroy_all(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/sasl-server.c	Mon Oct 11 20:14:26 2004 +0300
@@ -0,0 +1,182 @@
+/* Copyright (C) 2002-2004 Timo Sirainen */
+
+#include "common.h"
+#include "base64.h"
+#include "buffer.h"
+#include "str-sanitize.h"
+#include "auth-client.h"
+#include "ssl-proxy.h"
+#include "client-common.h"
+#include "master.h"
+
+/* Used only for string sanitization while verbose_auth is set. */
+#define MAX_MECH_NAME 64
+
+static enum auth_client_request_new_flags
+client_get_auth_flags(struct client *client)
+{
+        enum auth_client_request_new_flags auth_flags = 0;
+
+	if (client->proxy != NULL &&
+	    ssl_proxy_has_valid_client_cert(client->proxy))
+		auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT;
+	if (client->tls)
+		auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED;
+	return auth_flags;
+}
+
+static void master_callback(struct client *client, int success)
+{
+	client->authenticating = FALSE;
+	i_free(client->auth_mech_name);
+	client->auth_mech_name = NULL;
+
+	client->sasl_callback(client, success ? SASL_SERVER_REPLY_SUCCESS :
+			      SASL_SERVER_REPLY_MASTER_FAILED, NULL);
+}
+
+static const char *auth_client_get_str(struct auth_client_request_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 + idx, stop);
+}
+
+static void authenticate_callback(struct auth_request *request,
+				  struct auth_client_request_reply *reply,
+				  const unsigned char *data, void *context)
+{
+	struct client *client = context;
+	buffer_t *buf;
+	const char *user, *error;
+
+	if (!client->authenticating) {
+		/* client aborted */
+		i_assert(reply == NULL);
+		return;
+	}
+
+	if (reply == NULL) {
+		/* failed */
+		client->auth_request = NULL;
+		sasl_server_auth_cancel(client, "Authentication process died.");
+		return;
+	}
+
+	switch (reply->result) {
+	case AUTH_CLIENT_RESULT_CONTINUE:
+		if (client->auth_request != NULL) {
+			i_assert(client->auth_request == request);
+		} else {
+			i_assert(client->auth_request == NULL);
+
+			client->auth_request = request;
+		}
+
+		t_push();
+		buf = buffer_create_dynamic(pool_datastack_create(),
+				MAX_BASE64_ENCODED_SIZE(reply->data_size));
+		base64_encode(data, reply->data_size, buf);
+		buffer_append_c(buf, '\0');
+
+		client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE,
+				      buf->data);
+		t_pop();
+		break;
+	case AUTH_CLIENT_RESULT_SUCCESS:
+		client->auth_request = NULL;
+
+		user = auth_client_get_str(reply, data, reply->username_idx);
+		i_free(client->virtual_user);
+		client->virtual_user = i_strdup(user);
+
+		master_request_login(client, master_callback,
+				auth_client_request_get_server_pid(request),
+				auth_client_request_get_id(request));
+		break;
+	case AUTH_CLIENT_RESULT_FAILURE:
+		client->auth_request = NULL;
+
+		/* see if we have error message */
+		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
+			error = t_strconcat("Authentication failed: ",
+					    (const char *)data, NULL);
+		} else {
+			error = NULL;
+		}
+		sasl_server_auth_cancel(client, error);
+		break;
+	}
+}
+
+void sasl_server_auth_begin(struct client *client,
+			    const char *protocol, const char *mech_name,
+			    const unsigned char *initial_resp,
+			    size_t initial_resp_size,
+			    sasl_server_callback_t *callback)
+{
+	struct auth_request_info info;
+	const struct auth_mech_desc *mech;
+	const char *error;
+
+	client->authenticating = TRUE;
+	client->auth_mech_name = i_strdup(mech_name);
+	client->sasl_callback = callback;
+
+	mech = auth_client_find_mech(auth_client, mech_name);
+	if (mech == NULL) {
+		sasl_server_auth_cancel(client, 
+			"Unsupported authentication mechanism.");
+		return;
+	}
+
+	if (!client->secured && mech->plaintext && disable_plaintext_auth) {
+		sasl_server_auth_cancel(client,
+					"Plaintext authentication disabled.");
+		return;
+	}
+
+	memset(&info, 0, sizeof(info));
+	info.mech = mech->name;
+	info.protocol = protocol;
+	info.flags = client_get_auth_flags(client);
+	info.local_ip = client->local_ip;
+	info.remote_ip = client->ip;
+	info.initial_resp_data = initial_resp;
+	info.initial_resp_size = initial_resp_size;
+
+	client->auth_request =
+		auth_client_request_new(auth_client, NULL, &info,
+					authenticate_callback, client, &error);
+	if (client->auth_request == NULL) {
+		sasl_server_auth_cancel(client,
+			 t_strconcat("Authentication failed: ", error, NULL));
+	}
+}
+
+void sasl_server_auth_cancel(struct client *client, const char *reason)
+{
+	if (verbose_auth && reason != NULL) {
+		client_syslog(client, "Authenticate %s failed: %s",
+			      str_sanitize(client->auth_mech_name,
+					   MAX_MECH_NAME), reason);
+	}
+
+	client->authenticating = FALSE;
+	i_free(client->auth_mech_name);
+	client->auth_mech_name = NULL;
+
+	if (client->auth_request != NULL) {
+		auth_client_request_abort(client->auth_request);
+		client->auth_request = NULL;
+	}
+
+	client->sasl_callback(client, SASL_SERVER_REPLY_AUTH_FAILED, reason);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/sasl-server.h	Mon Oct 11 20:14:26 2004 +0300
@@ -0,0 +1,22 @@
+#ifndef __SASL_SERVER_H
+#define __SASL_SERVER_H
+
+enum sasl_server_reply {
+	SASL_SERVER_REPLY_SUCCESS,
+	SASL_SERVER_REPLY_AUTH_FAILED,
+	SASL_SERVER_REPLY_MASTER_FAILED,
+	SASL_SERVER_REPLY_CONTINUE
+};
+
+typedef void sasl_server_callback_t(struct client *client,
+				    enum sasl_server_reply reply,
+				    const char *data);
+
+void sasl_server_auth_begin(struct client *client,
+			    const char *protocol, const char *mech_name,
+			    const unsigned char *initial_resp,
+			    size_t initial_resp_size,
+			    sasl_server_callback_t *callback);
+void sasl_server_auth_cancel(struct client *client, const char *reason);
+
+#endif
--- a/src/pop3-login/client-authenticate.c	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/pop3-login/client-authenticate.c	Mon Oct 11 20:14:26 2004 +0300
@@ -13,14 +13,8 @@
 #include "auth-client.h"
 #include "../pop3/capability.h"
 #include "ssl-proxy.h"
-#include "master.h"
-#include "auth-common.h"
 #include "client.h"
 #include "client-authenticate.h"
-#include "ssl-proxy.h"
-
-/* Used only for string sanitization while verbose_auth is set. */
-#define MAX_MECH_NAME 64
 
 int cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
 {
@@ -53,212 +47,6 @@
 	return TRUE;
 }
 
-static void client_auth_abort(struct pop3_client *client, const char *msg)
-{
-	client->authenticating = FALSE;
-
-	if (client->common.auth_request != NULL) {
-		auth_client_request_abort(client->common.auth_request);
-		client->common.auth_request = NULL;
-	}
-
-	if (msg != NULL && verbose_auth)
-		client_syslog(client, "Authentication failed: %s", msg);
-
-	client_send_line(client, msg != NULL ? t_strconcat("-ERR ", msg, NULL) :
-			 "-ERR Authentication failed.");
-
-	/* get back to normal client input */
-	if (client->common.io != NULL)
-		io_remove(client->common.io);
-	client->common.io = client->common.fd == -1 ? NULL :
-		io_add(client->common.fd, IO_READ, client_input, client);
-
-	client_unref(client);
-}
-
-static void master_callback(struct client *_client, int success)
-{
-	struct pop3_client *client = (struct pop3_client *) _client;
-	const char *reason = NULL;
-
-	if (success) {
-		reason = t_strconcat("Login: ", client->common.virtual_user,
-				     NULL);
-	} else {
-		reason = t_strconcat("Internal login failure: ",
-				     client->common.virtual_user, NULL);
-		client_send_line(client, "* BYE Internal login failure. "
-				 "Error report written to server log.");
-	}
-
-	client_destroy(client, reason);
-}
-
-static void client_send_auth_data(struct pop3_client *client,
-				  const unsigned char *data, size_t size)
-{
-	buffer_t *buf;
-	const void *buf_data;
-	size_t buf_size;
-	ssize_t ret;
-
-	t_push();
-
-	buf = buffer_create_dynamic(pool_datastack_create(), size*2);
-	buffer_append(buf, "+ ", 2);
-	base64_encode(data, size, buf);
-	buffer_append(buf, "\r\n", 2);
-
-	buf_data = buffer_get_data(buf, &buf_size);
-	if ((ret = o_stream_send(client->output, buf_data, buf_size)) < 0)
-		client_destroy(client, "Disconnected");
-	else if ((size_t)ret != buf_size)
-		client_destroy(client, "Transmit buffer full");
-
-	t_pop();
-}
-
-static void login_callback(struct auth_request *request,
-			   struct auth_client_request_reply *reply,
-			   const unsigned char *data, void *context)
-{
-	struct pop3_client *client = context;
-	const char *error;
-
-	switch (auth_callback(request, reply, data, &client->common,
-			      master_callback, &error)) {
-	case -1:
-	case 0:
-		/* login failed */
-		client_auth_abort(client, error);
-		break;
-
-	default:
-		/* success, we should be able to log in. if we fail, just
-		   disconnect the client. */
-                client->authenticating = FALSE;
-		client_send_line(client, "+OK Logged in.");
-		client_unref(client);
-	}
-}
-
-static enum auth_client_request_new_flags
-client_get_auth_flags(struct pop3_client *client)
-{
-        enum auth_client_request_new_flags auth_flags = 0;
-
-	if (client->common.proxy != NULL &&
-	    ssl_proxy_has_valid_client_cert(client->common.proxy))
-		auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT;
-	if (client->tls)
-		auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED;
-	return auth_flags;
-}
-
-int cmd_user(struct pop3_client *client, const char *args)
-{
-	if (!client->secured && disable_plaintext_auth) {
-		if (verbose_auth) {
-			client_syslog(client, "Login failed: "
-				      "Plaintext authentication disabled");
-		}
-		client_send_line(client,
-				 "-ERR Plaintext authentication disabled.");
-		return TRUE;
-	}
-
-	i_free(client->last_user);
-	client->last_user = i_strdup(args);
-
-	client_send_line(client, "+OK");
-	return TRUE;
-}
-
-int cmd_pass(struct pop3_client *client, const char *args)
-{
-	const char *error;
-	struct auth_request_info info;
-	string_t *plain_login;
-
-	if (client->last_user == NULL) {
-		client_send_line(client, "-ERR No username given.");
-		return TRUE;
-	}
-
-	/* authorization ID \0 authentication ID \0 pass */
-	plain_login = t_str_new(128);
-	str_append_c(plain_login, '\0');
-	str_append(plain_login, client->last_user);
-	str_append_c(plain_login, '\0');
-	str_append(plain_login, args);
-
-	memset(&info, 0, sizeof(info));
-	info.mech = "PLAIN";
-	info.protocol = "POP3";
-	info.flags = client_get_auth_flags(client);
-	info.local_ip = client->common.local_ip;
-	info.remote_ip = client->common.ip;
-	info.initial_resp_data = str_data(plain_login);
-	info.initial_resp_size = str_len(plain_login);
-
-	client_ref(client);
-
-	client->common.auth_request =
-		auth_client_request_new(auth_client, NULL, &info,
-					login_callback, client, &error);
-	if (client->common.auth_request == NULL) {
-		if (verbose_auth)
-			client_syslog(client, "Login failed: %s", error);
-		client_send_line(client,
-			t_strconcat("-ERR Login failed: ", error, NULL));
-		client_unref(client);
-		return TRUE;
-	}
-
-	/* don't read any input from client until login is finished */
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
-	}
-
-	client->authenticating = TRUE;
-	return TRUE;
-}
-
-static void authenticate_callback(struct auth_request *request,
-				  struct auth_client_request_reply *reply,
-				  const unsigned char *data, void *context)
-{
-	struct pop3_client *client = context;
-	const char *error;
-
-	if (!client->authenticating) {
-		/* client aborted */
-		i_assert(reply == NULL);
-		return;
-	}
-
-	switch (auth_callback(request, reply, data, &client->common,
-			      master_callback, &error)) {
-	case -1:
-		/* login failed */
-		client_auth_abort(client, error);
-		break;
-
-	case 0:
-		client_send_auth_data(client, data, reply->data_size);
-		break;
-
-	default:
-		/* success, we should be able to log in. if we fail, just
-		   disconnect the client. */
-                client->authenticating = FALSE;
-		client_send_line(client, "+OK Logged in.");
-		client_unref(client);
-	}
-}
-
 static void client_auth_input(void *context)
 {
 	struct pop3_client *client = context;
@@ -275,7 +63,8 @@
 		return;
 
 	if (strcmp(line, "*") == 0) {
-		client_auth_abort(client, "Authentication aborted");
+		sasl_server_auth_cancel(&client->common,
+					"Authentication aborted");
 		return;
 	}
 
@@ -284,13 +73,13 @@
 
 	if (base64_decode(line, linelen, NULL, buf) < 0) {
 		/* failed */
-		client_auth_abort(client, "Invalid base64 data");
+		sasl_server_auth_cancel(&client->common, "Invalid base64 data");
 	} else if (client->common.auth_request == NULL) {
-		client_auth_abort(client, "Don't send unrequested data");
+		sasl_server_auth_cancel(&client->common,
+					"Don't send unrequested data");
 	} else {
 		auth_client_request_continue(client->common.auth_request,
-					     buffer_get_data(buf, NULL),
-					     buffer_get_used_size(buf));
+					     buf->data, buf->used);
 	}
 
 	/* clear sensitive data */
@@ -300,11 +89,66 @@
 	safe_memset(buffer_free_without_data(buf), 0, bufsize);
 }
 
+static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
+			  const char *data)
+{
+	struct pop3_client *client = (struct pop3_client *)_client;
+	struct const_iovec iov[3];
+	size_t data_len;
+	ssize_t ret;
+
+	switch (reply) {
+	case SASL_SERVER_REPLY_SUCCESS:
+		client_send_line(client, "+OK Logged in.");
+		client_destroy(client, t_strconcat(
+			"Login: ", client->common.virtual_user, NULL));
+		break;
+	case SASL_SERVER_REPLY_AUTH_FAILED:
+		if (data == NULL)
+			client_send_line(client, "-ERR Authentication failed");
+		else {
+			client_send_line(client, t_strconcat(
+				"-ERR Authentication failed: ", data, NULL));
+		}
+
+		/* get back to normal client input. */
+		io_remove(client->io);
+		client->io = io_add(client->common.fd, IO_READ,
+				    client_input, client);
+		break;
+	case SASL_SERVER_REPLY_MASTER_FAILED:
+		client_destroy(client, t_strconcat("Internal login failure: ",
+						   client->common.virtual_user,
+						   NULL));
+		break;
+	case SASL_SERVER_REPLY_CONTINUE:
+		data_len = strlen(data);
+		iov[0].iov_base = "+ ";
+		iov[0].iov_len = 2;
+		iov[1].iov_base = data;
+		iov[1].iov_len = data_len;
+		iov[2].iov_base = "\r\n";
+		iov[2].iov_len = 2;
+
+		ret = o_stream_sendv(client->output, iov, 3);
+		if (ret < 0)
+			client_destroy(client, "Disconnected");
+		else if ((size_t)ret != 2 + data_len + 2)
+			client_destroy(client, "Transmit buffer full");
+		else {
+			/* continue */
+			return;
+		}
+		break;
+	}
+
+	client_unref(client);
+}
+
 int cmd_auth(struct pop3_client *client, const char *args)
 {
-	struct auth_request_info info;
 	const struct auth_mech_desc *mech;
-	const char *mech_name, *error, *p;
+	const char *mech_name, *p;
 	string_t *buf;
 	size_t argslen;
 
@@ -332,29 +176,6 @@
 		args = p+1;
 	}
 
-	mech = auth_client_find_mech(auth_client, mech_name);
-	if (mech == NULL) {
-		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: "
-				      "Unsupported mechanism",
-				      str_sanitize(mech_name, MAX_MECH_NAME));
-		}
-		client_send_line(client,
-				 "-ERR Unsupported authentication mechanism.");
-		return TRUE;
-	}
-
-	if (!client->secured && mech->plaintext && disable_plaintext_auth) {
-		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: "
-				      "Plaintext authentication disabled",
-				      str_sanitize(mech_name, MAX_MECH_NAME));
-		}
-		client_send_line(client,
-				 "-ERR Plaintext authentication disabled.");
-		return TRUE;
-	}
-
 	argslen = strlen(args);
 	buf = buffer_create_static_hard(pool_datastack_create(), argslen);
 
@@ -364,49 +185,80 @@
 		return TRUE;
 	}
 
-	memset(&info, 0, sizeof(info));
-	info.mech = mech->name;
-	info.protocol = "POP3";
-	info.flags = client_get_auth_flags(client);
-	info.local_ip = client->common.local_ip;
-	info.remote_ip = client->common.ip;
-	info.initial_resp_data = str_data(buf);
-	info.initial_resp_size = str_len(buf);
+	client_ref(client);
+	sasl_server_auth_begin(&client->common, "POP3", mech_name,
+			       buf->data, buf->used, sasl_callback);
+	if (!client->common.authenticating)
+		return TRUE;
+
+	/* following input data will go to authentication */
+	if (client->io != NULL)
+		io_remove(client->io);
+	client->io = io_add(client->common.fd, IO_READ,
+			    client_auth_input, client);
+	return TRUE;
+}
+
+int cmd_user(struct pop3_client *client, const char *args)
+{
+	if (!client->secured && disable_plaintext_auth) {
+		if (verbose_auth) {
+			client_syslog(&client->common, "Login failed: "
+				      "Plaintext authentication disabled");
+		}
+		client_send_line(client,
+				 "-ERR Plaintext authentication disabled.");
+		return TRUE;
+	}
+
+	i_free(client->last_user);
+	client->last_user = i_strdup(args);
+
+	client_send_line(client, "+OK");
+	return TRUE;
+}
+
+int cmd_pass(struct pop3_client *client, const char *args)
+{
+	string_t *plain_login;
+
+	if (client->last_user == NULL) {
+		client_send_line(client, "-ERR No username given.");
+		return TRUE;
+	}
+
+	/* authorization ID \0 authentication ID \0 pass */
+	plain_login = t_str_new(128);
+	str_append_c(plain_login, '\0');
+	str_append(plain_login, client->last_user);
+	str_append_c(plain_login, '\0');
+	str_append(plain_login, args);
 
 	client_ref(client);
-	client->common.auth_request =
-		auth_client_request_new(auth_client, NULL, &info,
-					authenticate_callback, client, &error);
-	if (client->common.auth_request != NULL) {
-		/* following input data will go to authentication */
-		if (client->common.io != NULL)
-			io_remove(client->common.io);
-		client->common.io = io_add(client->common.fd, IO_READ,
-					   client_auth_input, client);
-                client->authenticating = TRUE;
-	} else {
-		if (verbose_auth) {
-			client_syslog(client, "Authenticate %s failed: %s",
-				      str_sanitize(mech_name, MAX_MECH_NAME),
-				      error);
-		}
-		client_send_line(client, t_strconcat(
-			"-ERR Authentication failed: ", error, NULL));
-		client_unref(client);
+	sasl_server_auth_begin(&client->common, "POP3", "PLAIN",
+			       plain_login->data, plain_login->used,
+			       sasl_callback);
+	if (!client->common.authenticating)
+		return TRUE;
+
+	/* don't read any input from client until login is finished */
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
-
 	return TRUE;
 }
 
 int cmd_apop(struct pop3_client *client, const char *args)
 {
-	struct auth_request_info info;
-	const char *error, *p;
 	buffer_t *apop_data;
+	const char *p;
 
 	if (client->apop_challenge == NULL) {
-		if (verbose_auth)
-			client_syslog(client, "APOP failed: APOP not enabled");
+		if (verbose_auth) {
+			client_syslog(&client->common,
+				      "APOP failed: APOP not enabled");
+		}
 	        client_send_line(client, "-ERR APOP not enabled.");
 		return TRUE;
 	}
@@ -415,8 +267,8 @@
 	p = strchr(args, ' ');
 	if (p == NULL || strlen(p+1) != 32) {
 		if (verbose_auth) {
-			client_syslog(client, "APOP failed: "
-				      "Invalid parameters");
+			client_syslog(&client->common,
+				      "APOP failed: Invalid parameters");
 		}
 	        client_send_line(client, "-ERR Invalid parameters.");
 		return TRUE;
@@ -431,7 +283,7 @@
 
 	if (hex_to_binary(p+1, apop_data) < 0) {
 		if (verbose_auth) {
-			client_syslog(client, "APOP failed: "
+			client_syslog(&client->common, "APOP failed: "
 				      "Invalid characters in MD5 response");
 		}
 		client_send_line(client,
@@ -439,41 +291,16 @@
 		return TRUE;
 	}
 
-	memset(&info, 0, sizeof(info));
-	info.mech = "APOP";
-	info.protocol = "POP3";
-	info.flags = client_get_auth_flags(client);
-	info.local_ip = client->common.local_ip;
-	info.remote_ip = client->common.ip;
-	info.initial_resp_data =
-		buffer_get_data(apop_data, &info.initial_resp_size);
-
 	client_ref(client);
-	o_stream_uncork(client->output);
-
-	client->common.auth_request =
-		auth_client_request_new(auth_client, &client->auth_id, &info,
-					login_callback, client, &error);
+	sasl_server_auth_begin(&client->common, "POP3", "APOP",
+			       apop_data->data, apop_data->used, sasl_callback);
+	if (!client->common.authenticating)
+		return TRUE;
 
-	if (client->common.auth_request != NULL) {
-		/* don't read any input from client until login is finished */
-		if (client->common.io != NULL) {
-			io_remove(client->common.io);
-			client->common.io = NULL;
-		}
-                client->authenticating = TRUE;
-	} else if (error == NULL) {
-		/* the auth connection was lost. we have no choice
-		   but to fail the APOP logins completely since the
-		   challenge is auth connection-specific. disconnect. */
-		client_destroy(client, "APOP auth connection lost");
-		client_unref(client);
-	} else {
-		if (verbose_auth) 
-			client_syslog(client, "APOP failed: %s", error);
-		client_send_line(client,
-			t_strconcat("-ERR Login failed: ", error, NULL));
-		client_unref(client);
+	/* don't read any input from client until login is finished */
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 	return TRUE;
 }
--- a/src/pop3-login/client.c	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/pop3-login/client.c	Mon Oct 11 20:14:26 2004 +0300
@@ -88,8 +88,7 @@
 	o_stream_unref(client->output);
 
 	client_open_streams(client, fd_ssl);
-	client->common.io = io_add(client->common.fd, IO_READ,
-				   client_input, client);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
 }
 
 static void client_output_starttls(void *context)
@@ -120,9 +119,9 @@
 
 	/* remove input handler, SSL proxy gives us a new fd. we also have to
 	   remove it in case we have to wait for buffer to be flushed */
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 
 	client_send_line(client, "+OK Begin TLS negotiation now.");
@@ -282,8 +281,7 @@
 
 static void client_auth_ready(struct pop3_client *client)
 {
-	client->common.io =
-		io_add(client->common.fd, IO_READ, client_input, client);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
 
 	client->apop_challenge = get_apop_challenge(client);
 	client_send_line(client, t_strconcat("+OK ", greeting,
@@ -340,7 +338,7 @@
 	client->destroyed = TRUE;
 
 	if (reason != NULL)
-		client_syslog(client, "%s", reason);
+		client_syslog(&client->common, "%s", reason);
 
 	hash_remove(clients, client);
 
@@ -355,9 +353,9 @@
 	if (client->common.master_tag != 0)
 		master_request_abort(&client->common);
 
-	if (client->common.io != NULL) {
-		io_remove(client->common.io);
-		client->common.io = NULL;
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
 	}
 
 	net_disconnect(client->common.fd);
@@ -405,22 +403,6 @@
 		client_destroy(client, "Transmit buffer full");
 }
 
-void client_syslog(struct pop3_client *client, const char *format, ...)
-{
-	const char *addr;
-	va_list args;
-
-	addr = net_ip2addr(&client->common.ip);
-	if (addr == NULL)
-		addr = "??";
-
-	t_push();
-	va_start(args, format);
-	i_info("%s [%s]", t_strdup_vprintf(format, args), addr);
-	va_end(args);
-	t_pop();
-}
-
 static void client_check_idle(struct pop3_client *client)
 {
 	if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
--- a/src/pop3-login/client.h	Mon Oct 11 17:29:51 2004 +0300
+++ b/src/pop3-login/client.h	Mon Oct 11 20:14:26 2004 +0300
@@ -12,6 +12,7 @@
 	time_t created;
 	int refcount;
 
+	struct io *io;
 	struct istream *input;
 	struct ostream *output;
 
@@ -33,8 +34,6 @@
 void client_destroy(struct pop3_client *client, const char *reason);
 
 void client_send_line(struct pop3_client *client, const char *line);
-void client_syslog(struct pop3_client *client, const char *format, ...)
-	__attr_format__(2, 3);
 
 int client_read(struct pop3_client *client);
 void client_input(void *context);