changeset 2736:0f31778d3c34 HEAD

Changed dovecot-auth protocol to ASCII based. Should be easier now to write replacement server if needed by someone. Also cleaned up/made more consistent auth code. The new code could still use some cleaning though..
author Timo Sirainen <tss@iki.fi>
date Wed, 13 Oct 2004 19:38:32 +0300
parents 25113dcc9705
children 134ad6e8cd42
files src/auth/Makefile.am src/auth/auth-client-connection.c src/auth/auth-client-connection.h src/auth/auth-client-interface.h src/auth/auth-master-connection.c src/auth/auth-master-connection.h src/auth/auth-master-interface.h src/auth/mech-anonymous.c src/auth/mech-apop.c src/auth/mech-cram-md5.c src/auth/mech-digest-md5.c src/auth/mech-login.c src/auth/mech-ntlm.c src/auth/mech-plain.c src/auth/mech-rpa.c src/auth/mech.c src/auth/mech.h src/auth/userdb.h src/imap-login/client-authenticate.c src/lib-auth/auth-client.h src/lib-auth/auth-server-connection.c src/lib-auth/auth-server-connection.h src/lib-auth/auth-server-request.c src/lib-auth/auth-server-request.h src/login-common/sasl-server.c src/login-common/sasl-server.h src/master/auth-process.c src/master/auth-process.h src/master/common.h src/master/login-process.c src/master/mail-process.c src/master/mail-process.h src/pop3-login/client-authenticate.c
diffstat 33 files changed, 1461 insertions(+), 1688 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/Makefile.am	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/Makefile.am	Wed Oct 13 19:38:32 2004 +0300
@@ -71,7 +71,6 @@
 	auth-client-connection.h \
 	auth-client-interface.h \
 	auth-master-connection.h \
-	auth-master-interface.h \
 	auth-module.h \
 	db-ldap.h \
 	db-mysql.h \
--- a/src/auth/auth-client-connection.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/auth-client-connection.c	Wed Oct 13 19:38:32 2004 +0300
@@ -5,48 +5,99 @@
 #include "istream.h"
 #include "ostream.h"
 #include "network.h"
+#include "base64.h"
+#include "buffer.h"
 #include "hash.h"
+#include "str.h"
+#include "str-sanitize.h"
 #include "safe-memset.h"
 #include "mech.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 
 #include <stdlib.h>
-#include <syslog.h>
 
-#define MAX_INBUF_SIZE \
-	(sizeof(struct auth_client_request_continue) + \
-	 AUTH_CLIENT_MAX_REQUEST_DATA_SIZE)
+/* Used only for string sanitization. */
+#define MAX_MECH_NAME_LEN 64
+
 #define MAX_OUTBUF_SIZE (1024*50)
 
 static void auth_client_connection_unref(struct auth_client_connection *conn);
 
-static void request_callback(struct auth_client_request_reply *reply,
-			     const void *data,
-			     struct auth_client_connection *conn)
+static void auth_client_send(struct auth_client_connection *conn,
+			     const char *fmt, ...) __attr_format__(2, 3);
+static void auth_client_send(struct auth_client_connection *conn,
+			     const char *fmt, ...)
 {
-	struct const_iovec iov[2];
+	va_list args;
+	string_t *str;
+	ssize_t ret;
+
+	i_assert(conn->refcount > 1);
+
+	t_push();
+	va_start(args, fmt);
+	str = t_str_new(256);
+	str_vprintfa(str, fmt, args);
+	str_append_c(str, '\n');
+	ret = o_stream_send(conn->output, str_data(str), str_len(str));
+	if (ret != (ssize_t)str->used) {
+		i_warning("Authentication client %u: "
+			  "Transmit buffer full, killing it", conn->pid);
+		auth_client_connection_destroy(conn);
+	}
+	va_end(args);
+	t_pop();
+}
+
+static void auth_callback(struct auth_request *request,
+			  enum auth_client_result result,
+			  const void *reply, size_t reply_size)
+{
+	string_t *str = NULL;
 	ssize_t ret;
 
-	iov[0].iov_base = reply;
-	iov[0].iov_len = sizeof(*reply);
-	iov[1].iov_base = data;
-	iov[1].iov_len = reply->data_size;
+	t_push();
 
-	ret = o_stream_sendv(conn->output, iov, 2);
-	if (ret == (ssize_t)(iov[0].iov_len + iov[1].iov_len)) {
-		/* all sent */
-		auth_client_connection_unref(conn);
-		return;
+	switch (result) {
+	case AUTH_CLIENT_RESULT_CONTINUE:
+		str = t_str_new(32 + MAX_BASE64_ENCODED_SIZE(reply_size));
+		str_printfa(str, "CONT\t%u\t", request->id);
+		base64_encode(reply, reply_size, str);
+                request->accept_input = TRUE;
+		break;
+	case AUTH_CLIENT_RESULT_SUCCESS:
+		str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size));
+		str_printfa(str, "OK\t%u\t%s", request->id, request->user);
+		if (reply_size > 0) {
+			str_append(str, "\tresp=");
+			base64_encode(reply, reply_size, str);
+		}
+		break;
+	case AUTH_CLIENT_RESULT_FAILURE:
+		str = t_str_new(128);
+		str_printfa(str, "FAIL\t%u", request->id);
+		if (reply != NULL) {
+			str_append_c(str, '\t');
+			str_append(str, reply);
+		}
+		break;
 	}
 
-	if (ret >= 0) {
-		i_warning("Auth client %u: Transmit buffer full, killing it",
-			  conn->pid);
+	str_append_c(str, '\n');
+
+	ret = o_stream_send(request->conn->output, str->data, str->used);
+	if (ret < 0)
+		auth_client_connection_destroy(request->conn);
+	else if ((size_t)ret != str->used) {
+		i_warning("Authentication client %u: "
+			  "Transmit buffer full, killing it",
+			  request->conn->pid);
+		auth_client_connection_destroy(request->conn);
 	}
+	t_pop();
 
-	auth_client_connection_destroy(conn);
-	auth_client_connection_unref(conn);
+	auth_client_connection_unref(request->conn);
 }
 
 struct auth_client_connection *
@@ -63,106 +114,229 @@
 	return NULL;
 }
 
-static int auth_client_input_handshake(struct auth_client_connection *conn)
+static int
+auth_client_input_proto(struct auth_client_connection *conn, const char *args)
 {
-        struct auth_client_handshake_request rec;
-        unsigned char *data;
-	size_t size;
+	if (conn->default_protocol == NULL)
+		conn->default_protocol = p_strdup(conn->pool, args);
+	return TRUE;
+}
 
-	data = i_stream_get_modifyable_data(conn->input, &size);
-	if (size < sizeof(rec))
-		return FALSE;
+static int
+auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
+{
+        struct auth_client_connection *old;
+	unsigned int pid;
 
-	/* Don't just cast because of alignment issues. */
-	memcpy(&rec, data, sizeof(rec));
-	i_stream_skip(conn->input, sizeof(rec));
-
-	if (rec.client_pid == 0) {
-		i_error("BUG: Auth client said it's PID 0");
-		auth_client_connection_destroy(conn);
+	if (conn->pid != 0) {
+		i_error("BUG: Authentication client re-handshaking");
 		return FALSE;
 	}
 
-	if (auth_client_connection_lookup(conn->master,
-					  rec.client_pid) != NULL) {
-		/* well, it might have just reconnected very fast .. although
-		   there's not much reason for it. */
-		i_error("BUG: Auth client gave a PID %u of existing connection",
-			rec.client_pid);
-		auth_client_connection_destroy(conn);
+	pid = (unsigned int)strtoul(args, NULL, 10);
+	if (pid == 0) {
+		i_error("BUG: Authentication client said it's PID 0");
 		return FALSE;
 	}
 
-	conn->pid = rec.client_pid;
+	old = auth_client_connection_lookup(conn->master, pid);
+	if (old != NULL) {
+		/* already exists. it's possible that it just reconnected,
+		   see if the old connection is still there. */
+		if (i_stream_read(old->input) == -1) {
+                        auth_client_connection_destroy(old);
+			old = NULL;
+		}
+	}
+
+	if (old != NULL) {
+		i_error("BUG: Authentication client gave a PID "
+			"%u of existing connection", pid);
+		return FALSE;
+	}
+
+	conn->pid = pid;
 	return TRUE;
 }
 
-static int auth_client_input_request(struct auth_client_connection *conn)
+static int
+auth_client_input_auth(struct auth_client_connection *conn, const char *args)
 {
-        enum auth_client_request_type type;
-        unsigned char *data;
-	size_t size;
+	struct mech_module *mech;
+	struct auth_request *request;
+	const char *const *list, *name, *arg, *initial_resp;
+	const void *initial_resp_data;
+	size_t initial_resp_len;
+	unsigned int id;
+	buffer_t *buf;
+	int valid_client_cert;
+
+	if (conn->pid == 0) {
+		i_error("BUG: Authentication client %u didn't send handshake",
+			conn->pid);
+		return FALSE;
+	}
+
+	/* <id> <mechanism> [...] */
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL || list[1] == NULL) {
+		i_error("BUG: Authentication client %u "
+			"sent broken AUTH request", conn->pid);
+		return FALSE;
+	}
+
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+
+	mech = mech_module_find(list[1]);
+	if (mech == NULL) {
+		/* unsupported mechanism */
+		i_error("BUG: Authentication client %u requested unsupported "
+			"authentication mechanism %s", conn->pid,
+			str_sanitize(list[1], MAX_MECH_NAME_LEN));
+		return FALSE;
+	}
+
+	request = auth_request_new(mech);
+	if (request == NULL)
+		return TRUE;
+
+	request->conn = conn;
+	request->id = id;
+
+	/* parse optional parameters */
+	initial_resp = NULL;
+	valid_client_cert = FALSE;
+	for (list += 2; *list != NULL; list++) {
+		arg = strchr(*list, '=');
+		if (arg == NULL) {
+			name = *list;
+			arg = "";
+		} else {
+			name = t_strdup_until(*list, arg);
+			arg++;
+		}
+
+		if (strcmp(name, "lip") == 0)
+			(void)net_addr2ip(arg, &request->local_ip);
+		else if (strcmp(name, "rip") == 0)
+			(void)net_addr2ip(arg, &request->remote_ip);
+		else if (strcmp(name, "proto") == 0)
+			request->protocol = p_strdup(request->pool, arg);
+		else if (strcmp(name, "resp") == 0)
+			initial_resp = arg;
+		else if (strcmp(name, "valid-client-cert") == 0)
+			valid_client_cert = TRUE;
+	}
+
+	if (request->protocol == NULL)
+		request->protocol = conn->default_protocol;
+	if (request->protocol == NULL) {
+		i_error("BUG: Authentication client %u "
+			"didn't specify protocol in request", conn->pid);
+		auth_request_destroy(request);
+		return FALSE;
+	}
 
-	data = i_stream_get_modifyable_data(conn->input, &size);
-	if (size < sizeof(type))
-		return FALSE;
+	if (ssl_require_client_cert && !valid_client_cert) {
+		/* we fail without valid certificate */
+		if (verbose) {
+			i_info("ssl-cert-check(%s): "
+			       "Client didn't present valid SSL certificate",
+			       get_log_prefix(request));
+		}
+		auth_request_destroy(request);
+		auth_client_send(conn, "FAIL\t%u", id);
+		return TRUE;
+	}
+
+	if (initial_resp == NULL) {
+		initial_resp_data = NULL;
+		initial_resp_len = 0;
+	} else {
+		size_t len = strlen(initial_resp);
+		buf = buffer_create_dynamic(pool_datastack_create(),
+					    MAX_BASE64_DECODED_SIZE(len));
+		if (base64_decode(initial_resp, len, NULL, buf) < 0) {
+			if (verbose) {
+				i_info("%s(%s): Invalid base64 data in "
+				       "initial response", mech->mech_name,
+				       get_log_prefix(request));
+			}
+			auth_request_destroy(request);
+			auth_client_send(conn, "FAIL\t%u\t"
+				"Invalid base64 data in initial response", id);
+			return TRUE;
+		}
+		initial_resp_data = buf->data;
+		initial_resp_len = buf->used;
+	}
+	hash_insert(conn->auth_requests, POINTER_CAST(id), request);
+
+	/* connection is referenced only until auth_callback is called. */
+	conn->refcount++;
+	mech->auth_initial(request, initial_resp_data, initial_resp_len,
+			   auth_callback);
+	return TRUE;
+}
 
-	/* note that we can't directly cast the received data pointer into
-	   structures, as it may not be aligned properly. */
-	memcpy(&type, data, sizeof(type));
+static int
+auth_client_input_cont(struct auth_client_connection *conn, const char *args)
+{
+	struct auth_request *request;
+	const char *data;
+	size_t data_len;
+	buffer_t *buf;
+	unsigned int id;
+
+	data = strchr(args, '\t');
+	if (data++ == NULL) {
+		i_error("BUG: Authentication client %u "
+			"sent broken CONT request", conn->pid);
+		return FALSE;
+	}
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+	request = hash_lookup(conn->auth_requests, POINTER_CAST(id));
+	if (request == NULL) {
+		/* timeouted */
+		auth_client_send(conn, "FAIL\t%u\tTimeouted", id);
+		return TRUE;
+	}
+
+	if (!request->accept_input) {
+		auth_client_send(conn, "FAIL\t%u\tUnexpected continuation", id);
+		auth_request_destroy(request);
+		return TRUE;
+	}
+        request->accept_input = FALSE;
+
+	data_len = strlen(data);
+	buf = buffer_create_dynamic(pool_datastack_create(),
+				    MAX_BASE64_DECODED_SIZE(data_len));
+	if (base64_decode(data, data_len, NULL, buf) < 0) {
+		if (verbose) {
+			i_info("%s(%s): Invalid base64 data in "
+			       "continued response", request->mech->mech_name,
+			       get_log_prefix(request));
+		}
+		auth_client_send(conn, "FAIL\t%u\tInvalid base64 data in "
+				 "continued response", id);
+		auth_request_destroy(request);
+		return TRUE;
+	}
 
 	conn->refcount++;
-	switch (type) {
-	case AUTH_CLIENT_REQUEST_NEW: {
-		struct auth_client_request_new request;
-
-		if (size < sizeof(request))
-			return FALSE;
-
-		memcpy(&request, data, sizeof(request));
-		if (size < sizeof(request) + request.data_size)
-			return FALSE;
-
-		/* we have a full init request */
-		conn->refcount++;
-		mech_request_new(conn, &request, data + sizeof(request),
-				 request_callback);
-		i_stream_skip(conn->input, sizeof(request) + request.data_size);
-		break;
-	}
-	case AUTH_CLIENT_REQUEST_CONTINUE: {
-                struct auth_client_request_continue request;
-
-		if (size < sizeof(request))
-			return FALSE;
-
-		memcpy(&request, data, sizeof(request));
-		if (size < sizeof(request) + request.data_size)
-			return FALSE;
-
-		/* we have a full continued request */
-		conn->refcount++;
-		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);
-		i_stream_skip(conn->input, sizeof(request) + request.data_size);
-		break;
-	}
-	default:
-		/* unknown request */
-		i_error("BUG: Auth client %u sent us unknown request type %u",
-			conn->pid, type);
-		auth_client_connection_destroy(conn);
-	}
-	auth_client_connection_unref(conn);
+	request->mech->auth_continue(request, buf->data, buf->used,
+				     auth_callback);
 	return TRUE;
 }
 
 static void auth_client_input(void *context)
 {
-	struct auth_client_connection *conn  = context;
+	struct auth_client_connection *conn = context;
+	char *line;
+	int ret;
 
 	switch (i_stream_read(conn->input)) {
 	case 0:
@@ -174,18 +348,35 @@
 	case -2:
 		/* buffer full */
 		i_error("BUG: Auth client %u sent us more than %d bytes",
-			conn->pid, (int)MAX_INBUF_SIZE);
+			conn->pid, (int)AUTH_CLIENT_MAX_LINE_LENGTH);
 		auth_client_connection_destroy(conn);
 		return;
 	}
 
-	if (conn->pid == 0) {
-		if (!auth_client_input_handshake(conn))
-			return;
+	conn->refcount++;
+	while ((line = i_stream_next_line(conn->input)) != NULL) {
+		t_push();
+		if (strncmp(line, "AUTH\t", 5) == 0)
+			ret = auth_client_input_auth(conn, line + 5);
+		else if (strncmp(line, "CONT\t", 5) == 0)
+			ret = auth_client_input_cont(conn, line + 5);
+		else if (strncmp(line, "CPID\t", 5) == 0)
+			ret = auth_client_input_cpid(conn, line + 5);
+		else if (strncmp(line, "PROTO\t", 6) == 0)
+			ret = auth_client_input_proto(conn, line + 6);
+		else {
+			/* ignore unknown command */
+			ret = TRUE;
+		}
+		safe_memset(line, 0, strlen(line));
+		t_pop();
+
+		if (!ret) {
+			auth_client_connection_destroy(conn);
+			break;
+		}
 	}
-
-	while (auth_client_input_request(conn))
-		;
+	auth_client_connection_unref(conn);
 }
 
 struct auth_client_connection *
@@ -193,8 +384,8 @@
 {
 	static unsigned int connect_uid_counter = 0;
 	struct auth_client_connection *conn;
-	struct auth_client_handshake_reply handshake_reply;
 	struct const_iovec iov[2];
+	string_t *str;
 
 	pool_t pool;
 
@@ -206,7 +397,8 @@
 	conn->connect_uid = ++connect_uid_counter;
 
 	conn->fd = fd;
-	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
+	conn->input = i_stream_create_file(fd, default_pool,
+					   AUTH_CLIENT_MAX_LINE_LENGTH,
 					   FALSE);
 	conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
 					    FALSE);
@@ -218,13 +410,14 @@
 	conn->next = master->clients;
 	master->clients = conn;
 
-	handshake_reply = *master->handshake_reply;
-	handshake_reply.connect_uid = conn->connect_uid;
+	str = t_str_new(128);
+	str_printfa(str, "SPID\t%u\nCUID\t%u\nDONE\n",
+		    master->pid, conn->connect_uid);
 
-	iov[0].iov_base = &handshake_reply;
-	iov[0].iov_len = sizeof(handshake_reply);
-	iov[1].iov_base = master->handshake_reply + 1;
-	iov[1].iov_len = handshake_reply.data_size;
+	iov[0].iov_base = str_data(mech_handshake);
+	iov[0].iov_len = str_len(mech_handshake);
+	iov[1].iov_base = str_data(str);
+	iov[1].iov_len = str_len(str);
 
 	if (o_stream_sendv(conn->output, iov, 2) < 0) {
 		auth_client_connection_destroy(conn);
--- a/src/auth/auth-client-connection.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/auth-client-connection.h	Wed Oct 13 19:38:32 2004 +0300
@@ -1,8 +1,6 @@
 #ifndef __AUTH_CLIENT_CONNECTION_H
 #define __AUTH_CLIENT_CONNECTION_H
 
-#include "auth-client-interface.h"
-
 struct auth_client_connection {
 	struct auth_client_connection *next;
 
@@ -16,6 +14,7 @@
 
 	pool_t pool;
 	struct hash_table *auth_requests;
+	char *default_protocol;
 
 	unsigned int pid;
 	unsigned int connect_uid;
--- a/src/auth/auth-client-interface.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/auth-client-interface.h	Wed Oct 13 19:38:32 2004 +0300
@@ -1,91 +1,24 @@
 #ifndef __AUTH_CLIENT_INTERFACE_H
 #define __AUTH_CLIENT_INTERFACE_H
 
-/* max. size for auth_client_request_continue.data[] */
-#define AUTH_CLIENT_MAX_REQUEST_DATA_SIZE 4096
-
-/* Client process must finish with single authentication requests in this time,
-   or the whole connection will be killed. */
+#define AUTH_CLIENT_MAX_LINE_LENGTH 8192
 #define AUTH_REQUEST_TIMEOUT 120
 
-enum auth_client_request_new_flags {
-	AUTH_CLIENT_FLAG_SSL_ENABLED		= 0x01,
-	AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT	= 0x02
-};
-
-enum auth_client_request_type {
-	AUTH_CLIENT_REQUEST_NEW = 1,
-        AUTH_CLIENT_REQUEST_CONTINUE
-};
-
-enum auth_client_result {
-	AUTH_CLIENT_RESULT_CONTINUE = 1,
-	AUTH_CLIENT_RESULT_SUCCESS,
-	AUTH_CLIENT_RESULT_FAILURE
-};
-
-/* Client -> Server */
-struct auth_client_handshake_request {
-	unsigned int client_pid; /* unique identifier for client process */
-};
-
-struct auth_client_handshake_mech_desc {
-	uint32_t name_idx;
-	unsigned int plaintext:1;
-	unsigned int advertise:1;
-};
-
-/* Server -> Client */
-struct auth_client_handshake_reply {
-	unsigned int server_pid; /* unique auth process identifier */
-	unsigned int connect_uid; /* unique connection identifier */
-
-	uint32_t mech_count;
-	uint32_t data_size;
-	/* struct auth_client_handshake_mech_desc mech_desc[auth_mech_count]; */
-};
-
-/* New authentication request */
-struct auth_client_request_new {
-	enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_NEW */
-	unsigned int id; /* unique ID for the request */
-
-	enum auth_client_request_new_flags flags;
-
-	uint32_t ip_family; /* if non-zero, data begins with local/remote IPs */
-
-	uint32_t protocol_idx;
-	uint32_t mech_idx;
-	uint32_t initial_resp_idx;
-
-	uint32_t data_size;
-	/* unsigned char data[]; */
-};
-#define AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request) \
-        ((request)->initial_resp_idx != (request)->data_size)
-
-/* Continue authentication request */
-struct auth_client_request_continue {
-	enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_CONTINUE */
-	unsigned int id;
-
-	uint32_t data_size;
-	/* unsigned char data[]; */
-};
-
-/* Reply to authentication */
-struct auth_client_request_reply {
-	unsigned int id;
-
-	enum auth_client_result result;
-
-	/* variable width data, indexes into data[].
-	   Ignore if it points outside data_size. */
-	uint32_t username_idx; /* NUL-terminated */
-	uint32_t reply_idx; /* last, non-NUL terminated */
-
-	uint32_t data_size;
-	/* unsigned char data[]; */
+enum mech_security_flags {
+	/* Don't advertise this as available SASL mechanism (eg. APOP) */
+	MECH_SEC_PRIVATE		= 0x0001,
+	/* Anonymous authentication */
+	MECH_SEC_ANONYMOUS		= 0x0002,
+	/* Transfers plaintext passwords */
+	MECH_SEC_PLAINTEXT		= 0x0004,
+	/* Subject to passive (dictionary) attack */
+	MECH_SEC_DICTIONARY		= 0x0008,
+	/* Subject to active (non-dictionary) attack */
+	MECH_SEC_ACTIVE			= 0x0010,
+	/* Provides forward secrecy between sessions */
+	MECH_SEC_FORWARD_SECRECY	= 0x0020,
+	/* Provides mutual authentication */
+	MECH_SEC_MUTUAL_AUTH		= 0x0040,
 };
 
 #endif
--- a/src/auth/auth-master-connection.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/auth-master-connection.c	Wed Oct 13 19:38:32 2004 +0300
@@ -3,7 +3,9 @@
 #include "common.h"
 #include "buffer.h"
 #include "hash.h"
+#include "str.h"
 #include "ioloop.h"
+#include "istream.h"
 #include "ostream.h"
 #include "network.h"
 #include "mech.h"
@@ -12,11 +14,10 @@
 #include "auth-master-connection.h"
 
 #include <unistd.h>
-
-#define MAX_OUTBUF_SIZE (1024*50)
+#include <stdlib.h>
 
-static struct auth_master_reply failure_reply =
-{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#define MAX_INBUF_SIZE 1024
+#define MAX_OUTBUF_SIZE (1024*50)
 
 struct auth_listener {
 	struct auth_master_connection *master;
@@ -28,164 +29,162 @@
 
 struct master_userdb_request {
 	struct auth_master_connection *conn;
-	unsigned int tag;
+	unsigned int id;
 };
 
 static void master_output(void *context);
 static void auth_master_connection_close(struct auth_master_connection *conn);
 static int auth_master_connection_unref(struct auth_master_connection *conn);
 
-static size_t reply_add(buffer_t *buf, const char *str)
+static void master_send(struct auth_master_connection *conn,
+			const char *fmt, ...) __attr_format__(2, 3);
+static void master_send(struct auth_master_connection *conn,
+			const char *fmt, ...)
 {
-	size_t index;
+	va_list args;
+	string_t *str;
 
-	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;
+	t_push();
+	va_start(args, fmt);
+	str = t_str_new(256);
+	str_vprintfa(str, fmt, args);
+	str_append_c(str, '\n');
+	(void)o_stream_send(conn->output, str_data(str), str_len(str));
+	va_end(args);
+	t_pop();
 }
 
-static struct auth_master_reply *
-fill_reply(const struct user_data *user, size_t *reply_size)
+static void append_user_reply(string_t *str, const struct user_data *user)
 {
-	struct auth_master_reply reply, *reply_p;
-	buffer_t *buf;
-	char *p;
+	const char *p;
 
-	buf = buffer_create_dynamic(pool_datastack_create(),
-				    sizeof(reply) + 256);
-	memset(&reply, 0, sizeof(reply));
-	buffer_append(buf, &reply, sizeof(reply));
-
-	reply.success = TRUE;
+	str_append(str, user->virtual_user);
+	str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user,
+		    dec2str(user->uid), dec2str(user->gid));
 
-	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.mail_idx = reply_add(buf, user->mail);
+	if (user->system_user != NULL)
+		str_printfa(str, "\tsystem_user=%s", user->system_user);
+	if (user->mail != NULL)
+		str_printfa(str, "\tmail=%s", user->mail);
 
 	p = user->home != NULL ? strstr(user->home, "/./") : NULL;
 	if (p == NULL) {
-		reply.home_idx = reply_add(buf, user->home);
-		reply.chroot_idx = reply_add(buf, NULL);
+		if (user->home != NULL)
+			str_printfa(str, "\thome=%s", user->home);
 	} else {
 		/* wu-ftpd like <chroot>/./<home> */
-		reply.chroot_idx =
-			reply_add(buf, t_strdup_until(user->home, p));
-		reply.home_idx = reply_add(buf, p + 3);
-	}
-
-	*reply_size = buffer_get_used_size(buf);
-	reply.data_size = *reply_size - sizeof(reply);
-
-	reply_p = buffer_get_space_unsafe(buf, 0, sizeof(reply));
-	*reply_p = reply;
-
-	return reply_p;
-}
-
-static void master_send_reply(struct auth_master_connection *conn,
-			      struct auth_master_reply *reply,
-			      size_t reply_size, unsigned int tag)
-{
-	ssize_t ret;
-
-	reply->tag = tag;
-
-	ret = o_stream_send(conn->output, reply, reply_size);
-	if (ret < 0) {
-		/* master died, kill ourself too */
-		auth_master_connection_close(conn);
-		return;
-	}
-	i_assert((size_t)ret == reply_size);
-
-	if (o_stream_get_buffer_used_size(conn->output) >= MAX_OUTBUF_SIZE) {
-		/* buffer full, stop accepting more input */
-		if (conn->io != NULL) {
-			io_remove(conn->io);
-			conn->io = NULL;
-		}
+		str_printfa(str, "\thome=%s\tchroot=%s",
+			    p + 3, t_strdup_until(user->home, p));
 	}
 }
 
-static void userdb_callback(struct user_data *user, void *context)
+static void userdb_callback(const struct user_data *user, void *context)
 {
 	struct master_userdb_request *master_request = context;
-	struct auth_master_reply *reply;
-	size_t reply_size;
+	string_t *str;
 
 	if (auth_master_connection_unref(master_request->conn)) {
 		if (user == NULL) {
-			master_send_reply(master_request->conn, &failure_reply,
-					  sizeof(failure_reply),
-					  master_request->tag);
+			master_send(master_request->conn, "NOTFOUND\t%u",
+				    master_request->id);
 		} else {
-			reply = fill_reply(user, &reply_size);
-			master_send_reply(master_request->conn, reply,
-					  reply_size, master_request->tag);
+			str = t_str_new(256);
+			str_printfa(str, "USER\t%u\t", master_request->id);
+			append_user_reply(str,  user);
+			master_send(master_request->conn, "%s", str_c(str));
 		}
 	}
 	i_free(master_request);
 }
 
-static void master_handle_request(struct auth_master_connection *conn,
-				  struct auth_master_request *request)
+static int
+master_input_request(struct auth_master_connection *conn, const char *args)
 {
 	struct auth_client_connection *client_conn;
-	struct auth_request *auth_request;
 	struct master_userdb_request *master_request;
+	struct auth_request *request;
+	const char *const *list;
+	unsigned int id, client_pid, client_id;
 
-	client_conn = auth_client_connection_lookup(conn, request->client_pid);
-	auth_request = client_conn == NULL ? NULL :
+	/* <id> <client-pid> <client-id> */
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL || list[1] == NULL || list[2] == NULL) {
+		i_error("BUG: Master sent broken REQUEST");
+		return FALSE;
+	}
+
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+	client_pid = (unsigned int)strtoul(list[1], NULL, 10);
+	client_id = (unsigned int)strtoul(list[2], NULL, 10);
+
+	client_conn = auth_client_connection_lookup(conn, client_pid);
+	request = client_conn == NULL ? NULL :
 		hash_lookup(client_conn->auth_requests,
-			    POINTER_CAST(request->id));
+			    POINTER_CAST(client_id));
 
-	if (auth_request == NULL) {
+	if (request == NULL) {
 		if (verbose) {
 			i_info("Master request %u.%u not found",
-			       request->client_pid, request->id);
+			       client_pid, client_id);
 		}
-		master_send_reply(conn, &failure_reply, sizeof(failure_reply),
-				  request->tag);
+		master_send(conn, "NOTFOUND\t%u", id);
 	} else {
 		master_request = i_new(struct master_userdb_request, 1);
 		master_request->conn = conn;
-		master_request->tag = request->tag;
+		master_request->id = id;
 
 		conn->refcount++;
-		userdb->lookup(auth_request, userdb_callback,
-			       master_request);
+		userdb->lookup(request, userdb_callback, master_request);
 
 		/* the auth request is finished, we don't need it anymore */
-		mech_request_free(auth_request, request->id);
+		auth_request_destroy(request);
 	}
+	return TRUE;
+}
+
+static int
+master_input_die(struct auth_master_connection *conn)
+{
+	return TRUE;
 }
 
 static void master_input(void *context)
 {
 	struct auth_master_connection *conn = context;
+ 	char *line;
 	int ret;
 
-	ret = net_receive(conn->fd,
-			  conn->request_buf + conn->request_pos,
-			  sizeof(conn->request_buf) - conn->request_pos);
-	if (ret < 0) {
-		/* master died, kill ourself too */
+	switch (i_stream_read(conn->input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+                auth_master_connection_close(conn);
+		return;
+	case -2:
+		/* buffer full */
+		i_error("BUG: Master sent us more than %d bytes",
+			(int)MAX_INBUF_SIZE);
                 auth_master_connection_close(conn);
 		return;
 	}
 
-	conn->request_pos += ret;
-	if (conn->request_pos >= sizeof(conn->request_buf)) {
-		/* reply is now read */
-		master_handle_request(conn, (struct auth_master_request *)
-				      conn->request_buf);
-		conn->request_pos = 0;
+	while ((line = i_stream_next_line(conn->input)) != NULL) {
+		t_push();
+		if (strncmp(line, "REQUEST\t", 8) == 0)
+			ret = master_input_request(conn, line + 8);
+		else if (strcmp(line, "DIE") == 0)
+			ret = master_input_die(conn);
+		else {
+			/* ignore unknown command */
+			ret = TRUE;
+		}
+		t_pop();
+
+		if (!ret) {
+			auth_master_connection_close(conn);
+			return;
+		}
 	}
 }
 
@@ -206,56 +205,18 @@
 	}
 }
 
-static void master_get_handshake_reply(struct auth_master_connection *master)
-{
-	struct mech_module_list *list;
-	buffer_t *buf;
-	struct auth_client_handshake_reply reply;
-	struct auth_client_handshake_mech_desc mech_desc;
-	uint32_t mech_desc_offset;
-
-	memset(&reply, 0, sizeof(reply));
-	memset(&mech_desc, 0, sizeof(mech_desc));
-
-	reply.server_pid = master->pid;
-
-	buf = buffer_create_dynamic(default_pool, 128);
-
-	for (list = mech_modules; list != NULL; list = list->next)
-		reply.mech_count++;
-	buffer_set_used_size(buf, sizeof(reply) +
-			     sizeof(mech_desc) * reply.mech_count);
-
-	mech_desc_offset = sizeof(reply);
-	for (list = mech_modules; list != NULL; list = list->next) {
-		mech_desc.name_idx = buffer_get_used_size(buf) - sizeof(reply);
-		mech_desc.plaintext = list->module.plaintext;
-		mech_desc.advertise = list->module.advertise;
-
-		memcpy(buffer_get_space_unsafe(buf, mech_desc_offset,
-					       sizeof(mech_desc)),
-		       &mech_desc, sizeof(mech_desc));
-		buffer_append(buf, list->module.mech_name,
-			      strlen(list->module.mech_name) + 1);
-
-		mech_desc_offset += sizeof(mech_desc);
-	}
-
-	reply.data_size = buffer_get_used_size(buf) - sizeof(reply);
-	memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)),
-	       &reply, sizeof(reply));
-
-	master->handshake_reply = buffer_free_without_data(buf);
-}
-
 static void
 auth_master_connection_set_fd(struct auth_master_connection *conn, int fd)
 {
+	if (conn->input != NULL)
+		i_stream_unref(conn->input);
 	if (conn->output != NULL)
 		o_stream_unref(conn->output);
 	if (conn->io != NULL)
 		io_remove(conn->io);
 
+	conn->input = i_stream_create_file(fd, default_pool,
+					   MAX_INBUF_SIZE, FALSE);
 	conn->output = o_stream_create_file(fd, default_pool,
 					    (size_t)-1, FALSE);
 	o_stream_set_flush_callback(conn->output, master_output, conn);
@@ -276,22 +237,15 @@
 	conn->listeners_buf = buffer_create_dynamic(default_pool, 64);
 	if (fd != -1)
                 auth_master_connection_set_fd(conn, fd);
-	master_get_handshake_reply(conn);
 	return conn;
 }
 
 void auth_master_connection_send_handshake(struct auth_master_connection *conn)
 {
-	struct auth_master_handshake_reply reply;
-
 	/* just a note to master that we're ok. if we die before, it means
 	   we're broken and a simple restart most likely won't help. */
-	if (conn->output != NULL) {
-		memset(&reply, 0, sizeof(reply));
-		reply.server_pid = conn->pid;
-		if (o_stream_send(conn->output, &reply, sizeof(reply)) < 0)
-                        auth_master_connection_close(conn);
-	}
+	if (conn->output != NULL)
+		master_send(conn, "SPID\t%u", conn->pid);
 }
 
 static void auth_master_connection_close(struct auth_master_connection *conn)
@@ -350,7 +304,6 @@
 
 	if (conn->output != NULL)
 		o_stream_unref(conn->output);
-	i_free(conn->handshake_reply);
 	i_free(conn);
 	return FALSE;
 }
--- a/src/auth/auth-master-connection.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/auth-master-connection.h	Wed Oct 13 19:38:32 2004 +0300
@@ -1,21 +1,16 @@
 #ifndef __AUTH_MASTER_CONNECTION_H
 #define __AUTH_MASTER_CONNECTION_H
 
-#include "auth-master-interface.h"
-
 struct auth_master_connection {
 	unsigned int pid;
 	int refcount;
 
 	int fd;
+	struct istream *input;
 	struct ostream *output;
 	struct io *io;
 	buffer_t *listeners_buf;
 
-	unsigned int request_pos;
-	unsigned char request_buf[sizeof(struct auth_master_request)];
-
-	struct auth_client_handshake_reply *handshake_reply;
 	struct auth_client_connection *clients;
 	struct timeout *to_clients;
 
--- a/src/auth/auth-master-interface.h	Wed Oct 13 15:32:54 2004 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-#ifndef __AUTH_MASTER_INTERFACE_H
-#define __AUTH_MASTER_INTERFACE_H
-
-#define AUTH_MASTER_MAX_REPLY_DATA_SIZE 4096
-
-/* Server -> Master */
-struct auth_master_handshake_reply {
-	unsigned int server_pid;
-};
-
-struct auth_master_request {
-	unsigned int tag;
-
-	unsigned int id;
-	unsigned int client_pid;
-};
-
-struct auth_master_reply {
-	unsigned int tag;
-
-	unsigned int success:1;
-
-	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. */
-	uint32_t system_user_idx;
-	uint32_t virtual_user_idx;
-	uint32_t home_idx, mail_idx, chroot_idx;
-
-	uint32_t data_size;
-	/* unsigned char data[]; */
-};
-
-#endif
--- a/src/auth/mech-anonymous.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-anonymous.c	Wed Oct 13 19:38:32 2004 +0300
@@ -1,85 +1,72 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2004 Timo Sirainen */
 
 #include "common.h"
 #include "mech.h"
 
-static int
-mech_anonymous_auth_continue(struct auth_request *auth_request,
+static void
+mech_anonymous_auth_continue(struct auth_request *request,
 			     const unsigned char *data, size_t data_size,
 			     mech_callback_t *callback)
 {
 	i_assert(anonymous_username != NULL);
 
 	if (verbose) {
-		auth_request->user =
+		/* temporarily set the user to the one that was given,
+		   so that the log message goes right */
+		request->user =
 			p_strndup(pool_datastack_create(), data, data_size);
 		i_info("anonymous(%s): login",
-		       get_log_prefix(auth_request));
+		       get_log_prefix(request));
 	}
 
-	auth_request->callback = callback;
-	auth_request->user = p_strdup(auth_request->pool, anonymous_username);
-
-	mech_auth_finish(auth_request, NULL, 0, TRUE);
-	return TRUE;
-}
-
-static int
-mech_anonymous_auth_initial(struct auth_request *auth_request,
-			    struct auth_client_request_new *request,
-			    const unsigned char *data,
-			    mech_callback_t *callback)
-{
-	struct auth_client_request_reply reply;
-	size_t data_size;
+	request->callback = callback;
+	request->user = p_strdup(request->pool, anonymous_username);
 
-	if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
-		data += request->initial_resp_idx;
-		data_size = request->data_size - request->initial_resp_idx;
-
-		return auth_request->auth_continue(auth_request, data,
-						   data_size, callback);
-	}
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = auth_request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	callback(&reply, NULL, auth_request->conn);
-	return TRUE;
+	mech_auth_finish(request, NULL, 0, TRUE);
 }
 
 static void
-mech_anonymous_auth_free(struct auth_request *auth_request)
+mech_anonymous_auth_initial(struct auth_request *request,
+			    const unsigned char *data, size_t data_size,
+			    mech_callback_t *callback)
 {
-	pool_unref(auth_request->pool);
+	if (data_size == 0)
+		callback(request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0);
+	else {
+		mech_anonymous_auth_continue(request, data, data_size,
+					     callback);
+	}
+}
+
+static void
+mech_anonymous_auth_free(struct auth_request *request)
+{
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_anonymous_auth_new(void)
 {
-        struct auth_request *auth_request;
+        struct auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("anonymous_auth_request", 256);
-	auth_request = p_new(pool, struct auth_request, 1);
-	auth_request->refcount = 1;
-	auth_request->pool = pool;
-	auth_request->auth_initial = mech_anonymous_auth_initial;
-	auth_request->auth_continue = mech_anonymous_auth_continue;
-        auth_request->auth_free = mech_anonymous_auth_free;
+	request = p_new(pool, struct auth_request, 1);
+	request->refcount = 1;
+	request->pool = pool;
 
-	return auth_request;
+	return request;
 }
 
 struct mech_module mech_anonymous = {
 	"ANONYMOUS",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_ANONYMOUS,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) FALSE,
 
-	mech_anonymous_auth_new
+	mech_anonymous_auth_new,
+	mech_anonymous_auth_initial,
+	mech_anonymous_auth_continue,
+        mech_anonymous_auth_free
 };
--- a/src/auth/mech-apop.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-apop.c	Wed Oct 13 19:38:32 2004 +0300
@@ -35,7 +35,7 @@
 apop_credentials_callback(const char *credentials,
 			  struct auth_request *auth_request)
 {
-	struct apop_auth_request *auth =
+	struct apop_auth_request *request =
 		(struct apop_auth_request *)auth_request;
 	unsigned char digest[16];
 	struct md5_context ctx;
@@ -43,41 +43,41 @@
 
 	if (credentials != NULL) {
 		md5_init(&ctx);
-		md5_update(&ctx, auth->challenge, strlen(auth->challenge));
+		md5_update(&ctx, request->challenge,
+			   strlen(request->challenge));
 		md5_update(&ctx, credentials, strlen(credentials));
 		md5_final(&ctx, digest);
 
-		ret = memcmp(digest, auth->digest, 16) == 0;
+		ret = memcmp(digest, request->digest, 16) == 0;
 	}
 
 	mech_auth_finish(auth_request, NULL, 0, ret);
 }
 
-static int
+static void
 mech_apop_auth_initial(struct auth_request *auth_request,
-		       struct auth_client_request_new *request,
-		       const unsigned char *data,
+		       const unsigned char *data, size_t data_size,
 		       mech_callback_t *callback)
 {
-	struct apop_auth_request *auth =
+	struct apop_auth_request *request =
 		(struct apop_auth_request *)auth_request;
 	const unsigned char *tmp, *end, *username = NULL;
 	const char *str, *error;
 
 	auth_request->callback = callback;
 
-	if (!AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+	if (data_size == 0) {
 		/* Should never happen */
 		if (verbose) {
 			i_info("apop(%s): no initial respone",
 			       get_log_prefix(auth_request));
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
-	tmp = data = data + request->initial_resp_idx;
-	end = data + request->data_size - request->initial_resp_idx;
+	tmp = data;
+	end = data + data_size;
 
 	while (tmp != end && *tmp != '\0')
 		tmp++;
@@ -93,9 +93,9 @@
 			       get_log_prefix(auth_request));
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
-	auth->challenge = p_strdup(auth->pool, (const char *)data);
+	request->challenge = p_strdup(request->pool, (const char *)data);
 
 	if (tmp != end) {
 		username = ++tmp;
@@ -110,58 +110,55 @@
 			       get_log_prefix(auth_request));
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 	tmp++;
 
-	auth_request->user = p_strdup(auth->pool, (const char *)username);
+	auth_request->user = p_strdup(request->pool, (const char *)username);
 	if (!mech_fix_username(auth_request->user, &error)) {
 		if (verbose) {
 			i_info("apop(%s): %s",
 			       get_log_prefix(auth_request), error);
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
-	memcpy(auth->digest, tmp, sizeof(auth->digest));
+	memcpy(request->digest, tmp, sizeof(request->digest));
 
 	passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_PLAINTEXT,
 				   apop_credentials_callback);
-	return TRUE;
 }
 
-static void mech_apop_auth_free(struct auth_request *auth_request)
+static void mech_apop_auth_free(struct auth_request *request)
 {
-	pool_unref(auth_request->pool);
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_apop_auth_new(void)
 {
-	struct apop_auth_request *auth;
+	struct apop_auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("apop_auth_request", 256);
-	auth = p_new(pool, struct apop_auth_request, 1);
-	auth->pool = pool;
+	request = p_new(pool, struct apop_auth_request, 1);
+	request->pool = pool;
 
-	auth->auth_request.refcount = 1;
-	auth->auth_request.pool = pool;
-	auth->auth_request.auth_initial = mech_apop_auth_initial;
-	auth->auth_request.auth_continue = NULL;
-	auth->auth_request.auth_free = mech_apop_auth_free;
-
-	return &auth->auth_request;
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	return &request->auth_request;
 }
 
 const struct mech_module mech_apop = {
 	"APOP",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) FALSE,
+	MEMBER(flags) MECH_SEC_PRIVATE | MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) TRUE,
 
 	mech_apop_auth_new,
+	mech_apop_auth_initial,
+	NULL,
+        mech_apop_auth_free
 };
--- a/src/auth/mech-cram-md5.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-cram-md5.c	Wed Oct 13 19:38:32 2004 +0300
@@ -42,11 +42,11 @@
 		buf[i] = (buf[i] % 10) + '0';
 	buf[sizeof(buf)-1] = '\0';
 
-	return t_strdup_printf("<%s.%s@%s>", (const char *) buf,
+	return t_strdup_printf("<%s.%s@%s>", (const char *)buf,
 			       dec2str(ioloop_time), my_hostname);
 }
 
-static int verify_credentials(struct cram_auth_request *auth,
+static int verify_credentials(struct cram_auth_request *request,
 			      const char *credentials)
 {
 	
@@ -66,15 +66,15 @@
 		return FALSE;
 
 	hmac_md5_set_cram_context(&ctx, context_digest);
-	hmac_md5_update(&ctx, auth->challenge, strlen(auth->challenge));
+	hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
 	hmac_md5_final(&ctx, digest);
 
 	response_hex = binary_to_hex(digest, 16);
 
-	if (memcmp(response_hex, auth->response, 32) != 0) {
+	if (memcmp(response_hex, request->response, 32) != 0) {
 		if (verbose) {
 			i_info("cram-md5(%s): password mismatch",
-			       get_log_prefix(&auth->auth_request));
+			       get_log_prefix(&request->auth_request));
 		}
 		return FALSE;
 	}
@@ -82,7 +82,7 @@
 	return TRUE;
 }
 
-static int parse_cram_response(struct cram_auth_request *auth,
+static int parse_cram_response(struct cram_auth_request *request,
 			       const unsigned char *data, size_t size,
 			       const char **error_r)
 {
@@ -102,126 +102,106 @@
 		return FALSE;
 	}
 
-	auth->username = p_strndup(auth->pool, data, space);
+	request->username = p_strndup(request->pool, data, space);
 	space++;
-	auth->response = p_strndup(auth->pool, data + space, size - space);
+	request->response =
+		p_strndup(request->pool, data + space, size - space);
 	return TRUE;
 }
 
 static void credentials_callback(const char *result,
-				 struct auth_request *request)
+				 struct auth_request *auth_request)
 {
-	struct cram_auth_request *auth =
-		(struct cram_auth_request *) request;
+	struct cram_auth_request *request =
+		(struct cram_auth_request *)auth_request;
 
-	if (verify_credentials(auth, result))
-		mech_auth_finish(request, NULL, 0, TRUE);
+	if (verify_credentials(request, result))
+		mech_auth_finish(auth_request, NULL, 0, TRUE);
 	else {
 		if (verbose) {
 			i_info("cram-md5(%s): authentication failed",
-			       get_log_prefix(&auth->auth_request));
+			       get_log_prefix(auth_request));
 		}
-		mech_auth_finish(request, NULL, 0, FALSE);
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
 	}
 }
 
-static int
+static void
 mech_cram_md5_auth_continue(struct auth_request *auth_request,
 			    const unsigned char *data, size_t data_size,
 			    mech_callback_t *callback)
 {
-	struct cram_auth_request *auth =
+	struct cram_auth_request *request =
 		(struct cram_auth_request *)auth_request;
 	const char *error;
 
-	if (parse_cram_response(auth, data, data_size, &error)) {
+	if (parse_cram_response(request, data, data_size, &error)) {
 		auth_request->callback = callback;
 
 		auth_request->user =
-			p_strdup(auth_request->pool, auth->username);
+			p_strdup(auth_request->pool, request->username);
 
 		if (mech_fix_username(auth_request->user, &error)) {
-			passdb->lookup_credentials(&auth->auth_request,
+			passdb->lookup_credentials(auth_request,
 						   PASSDB_CREDENTIALS_CRAM_MD5,
 						   credentials_callback);
-			return TRUE;
+			return;
 		}
 	}
 
 	if (error == NULL)
 		error = "authentication failed";
 
-	if (verbose) {
-		i_info("cram-md5(%s): %s",
-                       get_log_prefix(&auth->auth_request), error);
-	}
+	if (verbose)
+		i_info("cram-md5(%s): %s", get_log_prefix(auth_request), error);
 
 	/* failed */
 	mech_auth_finish(auth_request, NULL, 0, FALSE);
-	return FALSE;
 }
 
-static int
+static void
 mech_cram_md5_auth_initial(struct auth_request *auth_request,
-			   struct auth_client_request_new *request,
 			   const unsigned char *data __attr_unused__,
+			   size_t data_size __attr_unused__,
 			   mech_callback_t *callback)
 {
-	struct cram_auth_request *auth =
+	struct cram_auth_request *request =
 		(struct cram_auth_request *)auth_request;
 
-	struct auth_client_request_reply reply;
-
-	if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
-		/* No initial response in CRAM-MD5 */
-		return FALSE;
-	}
-
-	auth->challenge = p_strdup(auth->pool, get_cram_challenge());
-
-	/* initialize reply */
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	/* send the initial challenge */
-	reply.reply_idx = 0;
-	reply.data_size = strlen(auth->challenge);
-	callback(&reply, auth->challenge, auth_request->conn);
-	return TRUE;
+	request->challenge = p_strdup(request->pool, get_cram_challenge());
+	callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+		 request->challenge, strlen(request->challenge));
 }
 
-static void mech_cram_md5_auth_free(struct auth_request *auth_request)
+static void mech_cram_md5_auth_free(struct auth_request *request)
 {
-	pool_unref(auth_request->pool);
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_cram_md5_auth_new(void)
 {
-	struct cram_auth_request *auth;
+	struct cram_auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("cram_md5_auth_request", 2048);
-	auth = p_new(pool, struct cram_auth_request, 1);
-	auth->pool = pool;
+	request = p_new(pool, struct cram_auth_request, 1);
+	request->pool = pool;
 
-	auth->auth_request.refcount = 1;
-	auth->auth_request.pool = pool;
-	auth->auth_request.auth_initial = mech_cram_md5_auth_initial;
-	auth->auth_request.auth_continue = mech_cram_md5_auth_continue;
-	auth->auth_request.auth_free = mech_cram_md5_auth_free;
-
-	return &auth->auth_request;
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	return &request->auth_request;
 }
 
 struct mech_module mech_cram_md5 = {
 	"CRAM-MD5",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) TRUE,
 
-	mech_cram_md5_auth_new
+	mech_cram_md5_auth_new,
+	mech_cram_md5_auth_initial,
+	mech_cram_md5_auth_continue,
+        mech_cram_md5_auth_free
 };
--- a/src/auth/mech-digest-md5.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-digest-md5.c	Wed Oct 13 19:38:32 2004 +0300
@@ -52,7 +52,7 @@
 	char *rspauth;
 };
 
-static string_t *get_digest_challenge(struct digest_auth_request *auth)
+static string_t *get_digest_challenge(struct digest_auth_request *request)
 {
 	buffer_t *buf;
 	string_t *str;
@@ -79,7 +79,7 @@
 
 	base64_encode(nonce, sizeof(nonce), buf);
 	buffer_append_c(buf, '\0');
-	auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL));
+	request->nonce = p_strdup(request->pool, buffer_get_data(buf, NULL));
 	t_pop();
 
 	str = t_str_new(256);
@@ -89,11 +89,11 @@
 		str_append_c(str, ',');
 	}
 
-	str_printfa(str, "nonce=\"%s\",", auth->nonce);
+	str_printfa(str, "nonce=\"%s\",", request->nonce);
 
 	str_append(str, "qop=\""); first_qop = TRUE;
 	for (i = 0; i < QOP_COUNT; i++) {
-		if (auth->qop & (1 << i)) {
+		if (request->qop & (1 << i)) {
 			if (first_qop)
 				first_qop = FALSE;
 			else
@@ -108,7 +108,7 @@
 	return str;
 }
 
-static int verify_credentials(struct digest_auth_request *auth,
+static int verify_credentials(struct digest_auth_request *request,
 			      const char *credentials)
 {
 	struct md5_context ctx;
@@ -151,9 +151,9 @@
 	md5_init(&ctx);
 	md5_update(&ctx, digest, 16);
 	md5_update(&ctx, ":", 1);
-	md5_update(&ctx, auth->nonce, strlen(auth->nonce));
+	md5_update(&ctx, request->nonce, strlen(request->nonce));
 	md5_update(&ctx, ":", 1);
-	md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
+	md5_update(&ctx, request->cnonce, strlen(request->cnonce));
 	md5_final(&ctx, digest);
 	a1_hex = binary_to_hex(digest, 16);
 
@@ -167,11 +167,12 @@
 		else
 			md5_update(&ctx, ":", 1);
 
-		if (auth->digest_uri != NULL) {
-			md5_update(&ctx, auth->digest_uri,
-				   strlen(auth->digest_uri));
+		if (request->digest_uri != NULL) {
+			md5_update(&ctx, request->digest_uri,
+				   strlen(request->digest_uri));
 		}
-		if (auth->qop == QOP_AUTH_INT || auth->qop == QOP_AUTH_CONF) {
+		if (request->qop == QOP_AUTH_INT ||
+		    request->qop == QOP_AUTH_CONF) {
 			md5_update(&ctx, ":00000000000000000000000000000000",
 				   33);
 		}
@@ -182,13 +183,15 @@
 		md5_init(&ctx);
 		md5_update(&ctx, a1_hex, 32);
 		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->nonce, strlen(auth->nonce));
+		md5_update(&ctx, request->nonce, strlen(request->nonce));
 		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->nonce_count, strlen(auth->nonce_count));
+		md5_update(&ctx, request->nonce_count,
+			   strlen(request->nonce_count));
 		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->cnonce, strlen(auth->cnonce));
+		md5_update(&ctx, request->cnonce, strlen(request->cnonce));
 		md5_update(&ctx, ":", 1);
-		md5_update(&ctx, auth->qop_value, strlen(auth->qop_value));
+		md5_update(&ctx, request->qop_value,
+			   strlen(request->qop_value));
 		md5_update(&ctx, ":", 1);
 		md5_update(&ctx, a2_hex, 32);
 		md5_final(&ctx, digest);
@@ -196,10 +199,10 @@
 
 		if (i == 0) {
 			/* verify response */
-			if (memcmp(response_hex, auth->response, 32) != 0) {
+			if (memcmp(response_hex, request->response, 32) != 0) {
 				if (verbose) {
 					struct auth_request *auth_request =
-						&auth->auth_request;
+						&request->auth_request;
 					i_info("digest-md5(%s): "
 					       "password mismatch",
 					       get_log_prefix(auth_request));
@@ -207,8 +210,9 @@
 				return FALSE;
 			}
 		} else {
-			auth->rspauth = p_strconcat(auth->pool, "rspauth=",
-						    response_hex, NULL);
+			request->rspauth =
+				p_strconcat(request->pool, "rspauth=",
+					    response_hex, NULL);
 		}
 	}
 
@@ -283,7 +287,7 @@
 	return TRUE;
 }
 
-static int auth_handle_response(struct digest_auth_request *auth,
+static int auth_handle_response(struct digest_auth_request *request,
 				char *key, char *value, const char **error)
 {
 	int i;
@@ -295,13 +299,13 @@
 			*error = "Invalid realm";
 			return FALSE;
 		}
-		if (auth->realm == NULL && *value != '\0')
-			auth->realm = p_strdup(auth->pool, value);
+		if (request->realm == NULL && *value != '\0')
+			request->realm = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
 	if (strcmp(key, "username") == 0) {
-		if (auth->username != NULL) {
+		if (request->username != NULL) {
 			*error = "username must not exist more than once";
 			return FALSE;
 		}
@@ -311,23 +315,23 @@
 			return FALSE;
 		}
 
-		auth->username = p_strdup(auth->pool, value);
+		request->username = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
 	if (strcmp(key, "nonce") == 0) {
 		/* nonce must be same */
-		if (strcmp(value, auth->nonce) != 0) {
+		if (strcmp(value, request->nonce) != 0) {
 			*error = "Invalid nonce";
 			return FALSE;
 		}
 
-		auth->nonce_found = TRUE;
+		request->nonce_found = TRUE;
 		return TRUE;
 	}
 
 	if (strcmp(key, "cnonce") == 0) {
-		if (auth->cnonce != NULL) {
+		if (request->cnonce != NULL) {
 			*error = "cnonce must not exist more than once";
 			return FALSE;
 		}
@@ -337,12 +341,12 @@
 			return FALSE;
 		}
 
-		auth->cnonce = p_strdup(auth->pool, value);
+		request->cnonce = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
 	if (strcmp(key, "nonce-count") == 0) {
-		if (auth->nonce_count != NULL) {
+		if (request->nonce_count != NULL) {
 			*error = "nonce-count must not exist more than once";
 			return FALSE;
 		}
@@ -352,7 +356,7 @@
 			return FALSE;
 		}
 
-		auth->nonce_count = p_strdup(auth->pool, value);
+		request->nonce_count = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
@@ -367,13 +371,13 @@
 			return FALSE;
 		}
 
-		auth->qop &= (1 << i);
-		if (auth->qop == 0) {
+		request->qop &= (1 << i);
+		if (request->qop == 0) {
 			*error = "Nonallowed QoP requested";
 			return FALSE;
 		} 
 
-		auth->qop_value = p_strdup(auth->pool, value);
+		request->qop_value = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
@@ -390,18 +394,18 @@
 		   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);
+		request->digest_uri = p_strdup(request->pool, value);
 		return TRUE;
 	}
 
 	if (strcmp(key, "maxbuf") == 0) {
-		if (auth->maxbuf != 0) {
+		if (request->maxbuf != 0) {
 			*error = "maxbuf must not exist more than once";
 			return FALSE;
 		}
 
-		auth->maxbuf = strtoul(value, NULL, 10);
-		if (auth->maxbuf == 0) {
+		request->maxbuf = strtoul(value, NULL, 10);
+		if (request->maxbuf == 0) {
 			*error = "Invalid maxbuf value";
 			return FALSE;
 		}
@@ -423,7 +427,7 @@
 			return FALSE;
 		}
 
-		memcpy(auth->response, value, 32);
+		memcpy(request->response, value, 32);
 		return TRUE;
 	}
 
@@ -441,7 +445,7 @@
 	return TRUE;
 }
 
-static int parse_digest_response(struct digest_auth_request *auth,
+static int parse_digest_response(struct digest_auth_request *request,
 				 const unsigned char *data, size_t size,
 				 const char **error)
 {
@@ -471,7 +475,7 @@
 	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)) {
+			if (!auth_handle_response(request, key, value, error)) {
 				failed = TRUE;
 				break;
 			}
@@ -482,22 +486,22 @@
 	}
 
 	if (!failed) {
-		if (!auth->nonce_found) {
+		if (!request->nonce_found) {
 			*error = "Missing nonce parameter";
 			failed = TRUE;
-		} else if (auth->cnonce == NULL) {
+		} else if (request->cnonce == NULL) {
 			*error = "Missing cnonce parameter";
 			failed = TRUE;
-		} else if (auth->username == NULL) {
+		} else if (request->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 (request->nonce_count == NULL)
+		request->nonce_count = p_strdup(request->pool, "00000001");
+	if (request->qop_value == NULL)
+		request->qop_value = p_strdup(request->pool, "auth");
 
 	t_pop();
 
@@ -505,148 +509,118 @@
 }
 
 static void credentials_callback(const char *result,
-				 struct auth_request *request)
+				 struct auth_request *auth_request)
 {
-	struct digest_auth_request *auth =
-		(struct digest_auth_request *) request;
-	struct auth_client_request_reply reply;
+	struct digest_auth_request *request =
+		(struct digest_auth_request *)auth_request;
 
-	if (!verify_credentials(auth, result)) {
-		mech_auth_finish(request, NULL, 0, FALSE);
+	if (!verify_credentials(request, result)) {
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
 		return;
 	}
 
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-	reply.data_size = strlen(auth->rspauth);
-	auth->authenticated = TRUE;
-
-	request->callback(&reply, auth->rspauth, request->conn);
+	auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+			       request->rspauth, strlen(request->rspauth));
 }
 
-static int
+static void
 mech_digest_md5_auth_continue(struct auth_request *auth_request,
 			      const unsigned char *data, size_t data_size,
 			      mech_callback_t *callback)
 {
-	struct digest_auth_request *auth =
+	struct digest_auth_request *request =
 		(struct digest_auth_request *)auth_request;
-	struct auth_client_request_reply reply;
 	const char *error, *realm;
 
-	/* initialize reply */
-	mech_init_auth_client_reply(&reply);
-	reply.id = auth_request->id;
-
-	if (auth->authenticated) {
+	if (request->authenticated) {
 		/* authentication is done, we were just waiting the last
 		   word from client */
 		mech_auth_finish(auth_request, NULL, 0, TRUE);
-		return TRUE;
+		return;
 	}
 
-	if (parse_digest_response(auth, data, data_size, &error)) {
+	if (parse_digest_response(request, data, data_size, &error)) {
 		auth_request->callback = callback;
 
-		realm = auth->realm != NULL ? auth->realm : default_realm;
+		realm = request->realm != NULL ? request->realm : default_realm;
 		if (realm == NULL) {
 			auth_request->user = p_strdup(auth_request->pool,
-						      auth->username);
+						      request->username);
 		} else {
 			auth_request->user = p_strconcat(auth_request->pool,
-							 auth->username, "@",
+							 request->username, "@",
 							 realm, NULL);
 		}
 
 		if (mech_fix_username(auth_request->user, &error)) {
-			passdb->lookup_credentials(&auth->auth_request,
+			passdb->lookup_credentials(auth_request,
 						PASSDB_CREDENTIALS_DIGEST_MD5,
 						credentials_callback);
-			return TRUE;
+			return;
 		}
 	}
 
-	if (error == NULL)
-                error = "Authentication failed";
-	else if (verbose) {
+	if (verbose && error != NULL) {
 		i_info("digest-md5(%s): %s",
 		       get_log_prefix(auth_request), error);
 	}
 
-	/* failed */
-	reply.result = AUTH_CLIENT_RESULT_FAILURE;
-	reply.data_size = strlen(error)+1;
-	callback(&reply, error, auth_request->conn);
-	return FALSE;
+	mech_auth_finish(auth_request, NULL, 0, FALSE);
 }
 
-static int
+static void
 mech_digest_md5_auth_initial(struct auth_request *auth_request,
-			     struct auth_client_request_new *request,
-			     const unsigned char *data __attr_unused__,
+			     const unsigned char *data, size_t data_size,
 			     mech_callback_t *callback)
 {
-	struct digest_auth_request *auth =
+	struct digest_auth_request *request =
 		(struct digest_auth_request *)auth_request;
-	struct auth_client_request_reply reply;
 	string_t *challenge;
-	size_t data_size;
 
-	if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+	if (data_size > 0) {
 		/* FIXME: support subsequent authentication? */
-		data += request->initial_resp_idx;
-		data_size = request->data_size - request->initial_resp_idx;
-
-		return auth_request->auth_continue(auth_request, data,
-						   data_size, callback);
+		mech_digest_md5_auth_continue(auth_request, data, data_size,
+					      callback);
+		return;
 	}
 
-	/* initialize reply */
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_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), auth_request->conn);
-	return TRUE;
+	challenge = get_digest_challenge(request);
+	callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+		 str_data(challenge), str_len(challenge));
 }
 
-static void mech_digest_md5_auth_free(struct auth_request *auth_request)
+static void mech_digest_md5_auth_free(struct auth_request *request)
 {
-	pool_unref(auth_request->pool);
+	pool_unref(request->pool);
 }
 
 static struct auth_request *
 mech_digest_md5_auth_new(void)
 {
-	struct digest_auth_request *auth;
+	struct digest_auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("digest_md5_auth_request", 2048);
-	auth = p_new(pool, struct digest_auth_request, 1);
-	auth->pool = pool;
+	request = p_new(pool, struct digest_auth_request, 1);
+	request->pool = pool;
 
-	auth->auth_request.refcount = 1;
-	auth->auth_request.pool = pool;
-	auth->auth_request.auth_initial = mech_digest_md5_auth_initial;
-	auth->auth_request.auth_continue = mech_digest_md5_auth_continue;
-	auth->auth_request.auth_free = mech_digest_md5_auth_free;
-	auth->qop = QOP_AUTH;
-	return &auth->auth_request;
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	request->qop = QOP_AUTH;
+	return &request->auth_request;
 }
 
 struct mech_module mech_digest_md5 = {
 	"DIGEST-MD5",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE |
+		MECH_SEC_MUTUAL_AUTH,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) TRUE,
 
-	mech_digest_md5_auth_new
+	mech_digest_md5_auth_new,
+	mech_digest_md5_auth_initial,
+	mech_digest_md5_auth_continue,
+        mech_digest_md5_auth_free
 };
--- a/src/auth/mech-login.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-login.c	Wed Oct 13 19:38:32 2004 +0300
@@ -20,98 +20,77 @@
 	mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK);
 }
 
-static int
-mech_login_auth_continue(struct auth_request *auth_request,
+static void
+mech_login_auth_continue(struct auth_request *request,
 			 const unsigned char *data, size_t data_size,
 			 mech_callback_t *callback)
 {
-	struct auth_client_request_reply reply;
 	static const char prompt2[] = "Password:";
 	const char *error;
 
-	auth_request->callback = callback;
+	request->callback = callback;
 
-	if (!auth_request->user) {
-		auth_request->user =
-			p_strndup(auth_request->pool, data, data_size);
+	if (request->user == NULL) {
+		request->user = p_strndup(request->pool, data, data_size);
 
-		if (!mech_fix_username(auth_request->user, &error)) {
+		if (!mech_fix_username(request->user, &error)) {
 			if (verbose) {
 				i_info("login(%s): %s",
-				       get_log_prefix(auth_request), error);
+				       get_log_prefix(request), error);
 			}
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
-			return TRUE;
+			mech_auth_finish(request, NULL, 0, FALSE);
+			return;
 		}
 
-		mech_init_auth_client_reply(&reply);
-		reply.id = auth_request->id;
-		reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-		reply.reply_idx = 0;
-		reply.data_size = strlen(prompt2);
-		callback(&reply, prompt2, auth_request->conn);
+		callback(request, AUTH_CLIENT_RESULT_CONTINUE,
+			 prompt2, strlen(prompt2));
 	} else {
 		char *pass = p_strndup(unsafe_data_stack_pool, data, data_size);
-
-		passdb->verify_plain(auth_request, pass, verify_callback);
-
+		passdb->verify_plain(request, pass, verify_callback);
 		safe_memset(pass, 0, strlen(pass));
 	}
-
-	return TRUE;
 }
 
-static int
-mech_login_auth_initial(struct auth_request *auth_request,
-		       struct auth_client_request_new *request,
-		       const unsigned char *data __attr_unused__,
-		       mech_callback_t *callback)
+static void
+mech_login_auth_initial(struct auth_request *request,
+			const unsigned char *data __attr_unused__,
+			size_t data_size __attr_unused__,
+			mech_callback_t *callback)
 {
-	struct auth_client_request_reply reply;
 	static const char prompt1[] = "Username:";
 
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	reply.reply_idx = 0;
-	reply.data_size = strlen(prompt1);
-	callback(&reply, prompt1, auth_request->conn);
-
-	return TRUE;
+	callback(request, AUTH_CLIENT_RESULT_CONTINUE,
+		 prompt1, strlen(prompt1));
 }
 
-static void mech_login_auth_free(struct auth_request *auth_request)
+static void mech_login_auth_free(struct auth_request *request)
 {
-	pool_unref(auth_request->pool);
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_login_auth_new(void)
 {
-	struct auth_request *auth;
+	struct auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("login_auth_request", 256);
-	auth = p_new(pool, struct auth_request, 1);
+	request = p_new(pool, struct auth_request, 1);
 
-	auth->refcount = 1;
-	auth->pool = pool;
-	auth->auth_initial = mech_login_auth_initial;
-	auth->auth_continue = mech_login_auth_continue;
-	auth->auth_free = mech_login_auth_free;
-
-	return auth;
+	request->refcount = 1;
+	request->pool = pool;
+	return request;
 }
 
 const struct mech_module mech_login = {
 	"LOGIN",
 
-	MEMBER(plaintext) TRUE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_PLAINTEXT,
 
 	MEMBER(passdb_need_plain) TRUE,
 	MEMBER(passdb_need_credentials) FALSE,
 
 	mech_login_auth_new,
+	mech_login_auth_initial,
+	mech_login_auth_continue,
+        mech_login_auth_free
 };
--- a/src/auth/mech-ntlm.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-ntlm.c	Wed Oct 13 19:38:32 2004 +0300
@@ -36,7 +36,7 @@
 lm_credentials_callback(const char *credentials,
 			struct auth_request *auth_request)
 {
-	struct ntlm_auth_request *auth =
+	struct ntlm_auth_request *request =
 		(struct ntlm_auth_request *)auth_request;
 	const unsigned char *client_response;
 	unsigned char lm_response[LM_RESPONSE_SIZE];
@@ -53,9 +53,9 @@
 					 hash, sizeof(hash));
 	hex_to_binary(credentials, hash_buffer);
 
-	client_response = ntlmssp_buffer_data(auth->response, lm_response);
+	client_response = ntlmssp_buffer_data(request->response, lm_response);
 
-	ntlmssp_v1_response(hash, auth->challenge, lm_response);
+	ntlmssp_v1_response(hash, request->challenge, lm_response);
 
 	ret = memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0;
 
@@ -66,7 +66,7 @@
 ntlm_credentials_callback(const char *credentials,
 			  struct auth_request *auth_request)
 {
-	struct ntlm_auth_request *auth =
+	struct ntlm_auth_request *request =
 		(struct ntlm_auth_request *)auth_request;
 	const unsigned char *client_response;
 	unsigned char hash[NTLMSSP_HASH_SIZE];
@@ -74,7 +74,7 @@
 	buffer_t *hash_buffer;
 	int ret;
 
-	if (credentials == NULL && !auth->ntlm2_negotiated) {
+	if (credentials == NULL && !request->ntlm2_negotiated) {
 		passdb->lookup_credentials(auth_request,
 					   PASSDB_CREDENTIALS_LANMAN,
 					   lm_credentials_callback);
@@ -85,8 +85,9 @@
 					 hash, sizeof(hash));
 	hex_to_binary(credentials, hash_buffer);
 
-	response_length = ntlmssp_buffer_length(auth->response, ntlm_response);
-	client_response = ntlmssp_buffer_data(auth->response, ntlm_response);
+	response_length =
+		ntlmssp_buffer_length(request->response, ntlm_response);
+	client_response = ntlmssp_buffer_data(request->response, ntlm_response);
 
 	if (response_length > NTLMSSP_RESPONSE_SIZE) {
 		unsigned char ntlm_v2_response[NTLMSSP_V2_RESPONSE_SIZE];
@@ -98,7 +99,7 @@
 		 * as a standalone server, not as NT domain member.
 		 */
 		ntlmssp_v2_response(auth_request->user, NULL,
-				    hash, auth->challenge, blob,
+				    hash, request->challenge, blob,
 				    response_length - NTLMSSP_V2_RESPONSE_SIZE,
 				    ntlm_v2_response);
 
@@ -107,14 +108,14 @@
 	} else {
 		unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE];
 		const unsigned char *client_lm_response =
-			ntlmssp_buffer_data(auth->response, lm_response);
+			ntlmssp_buffer_data(request->response, lm_response);
 
-		if (auth->ntlm2_negotiated)
-			ntlmssp2_response(hash, auth->challenge,
+		if (request->ntlm2_negotiated)
+			ntlmssp2_response(hash, request->challenge,
 					  client_lm_response,
 					  ntlm_response);
 		else 
-			ntlmssp_v1_response(hash, auth->challenge,
+			ntlmssp_v1_response(hash, request->challenge,
 					    ntlm_response);
 
 		ret = memcmp(ntlm_response, client_response,
@@ -124,47 +125,42 @@
 	mech_auth_finish(auth_request, NULL, 0, ret);
 }
 
-static int
+static void
 mech_ntlm_auth_continue(struct auth_request *auth_request,
 			const unsigned char *data, size_t data_size,
 			mech_callback_t *callback)
 {
-	struct ntlm_auth_request *auth =
+	struct ntlm_auth_request *request =
 		(struct ntlm_auth_request *)auth_request;
-	struct auth_client_request_reply reply;
 	const char *error;
 
 	auth_request->callback = callback;
 
-	if (!auth->challenge) {
-		const struct ntlmssp_request *request =
+	if (!request->challenge) {
+		const struct ntlmssp_request *ntlm_request =
 			(struct ntlmssp_request *)data;
 		const struct ntlmssp_challenge *message;
 		size_t message_size;
 
-		if (!ntlmssp_check_request(request, data_size, &error)) {
+		if (!ntlmssp_check_request(ntlm_request, data_size, &error)) {
 			if (verbose) {
 				i_info("ntlm(%s): invalid NTLM request, %s",
 				       get_log_prefix(auth_request),
 				       error);
 			}
 			mech_auth_finish(auth_request, NULL, 0, FALSE);
-			return TRUE;
+			return;
 		}
 
-		message = ntlmssp_create_challenge(auth->pool, request,
+		message = ntlmssp_create_challenge(request->pool, ntlm_request,
 						   &message_size);
-		auth->ntlm2_negotiated =
+		request->ntlm2_negotiated =
 			read_le32(&message->flags) & NTLMSSP_NEGOTIATE_NTLM2;
-		auth->challenge = message->challenge;
+		request->challenge = message->challenge;
 
-		mech_init_auth_client_reply(&reply);
-		reply.id = auth_request->id;
-		reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-		reply.reply_idx = 0;
-		reply.data_size = message_size;
-		callback(&reply, message, auth_request->conn);
+		auth_request->callback(auth_request,
+				       AUTH_CLIENT_RESULT_CONTINUE,
+				       message, message_size);
 	} else {
 		const struct ntlmssp_response *response =
 			(struct ntlmssp_response *)data;
@@ -177,14 +173,14 @@
 				       error);
 			}
 			mech_auth_finish(auth_request, NULL, 0, FALSE);
-			return TRUE;
+			return;
 		}
 
-		auth->response = p_malloc(auth->pool, data_size);
-		memcpy(auth->response, response, data_size);
+		request->response = p_malloc(request->pool, data_size);
+		memcpy(request->response, response, data_size);
 
 		username = p_strdup(auth_request->pool,
-				    ntlmssp_t_str(auth->response, user));
+				    ntlmssp_t_str(request->response, user));
 
 		if (!mech_fix_username(username, &error)) {
 			if (verbose) {
@@ -192,70 +188,55 @@
 				       get_log_prefix(auth_request), error);
 			}
 			mech_auth_finish(auth_request, NULL, 0, FALSE);
-			return TRUE;
+			return;
 		}
 
 		auth_request->user = username;
-
 		passdb->lookup_credentials(auth_request,
 					   PASSDB_CREDENTIALS_NTLM,
 					   ntlm_credentials_callback);
 	}
-
-	return TRUE;
-}
-
-static int
-mech_ntlm_auth_initial(struct auth_request *auth_request,
-		       struct auth_client_request_new *request,
-		       const unsigned char *data __attr_unused__,
-		       mech_callback_t *callback)
-{
-	struct auth_client_request_reply reply;
-
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	reply.reply_idx = 0;
-	reply.data_size = 0;
-	callback(&reply, "", auth_request->conn);
-
-	return TRUE;
 }
 
 static void
-mech_ntlm_auth_free(struct auth_request *auth_request)
+mech_ntlm_auth_initial(struct auth_request *auth_request,
+		       const unsigned char *data __attr_unused__,
+		       size_t data_size __attr_unused__,
+		       mech_callback_t *callback)
 {
-	pool_unref(auth_request->pool);
+	callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0);
+}
+
+static void
+mech_ntlm_auth_free(struct auth_request *request)
+{
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_ntlm_auth_new(void)
 {
-	struct ntlm_auth_request *auth;
+	struct ntlm_auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("ntlm_auth_request", 256);
-	auth = p_new(pool, struct ntlm_auth_request, 1);
-	auth->pool = pool;
+	request = p_new(pool, struct ntlm_auth_request, 1);
+	request->pool = pool;
 
-	auth->auth_request.refcount = 1;
-	auth->auth_request.pool = pool;
-	auth->auth_request.auth_initial = mech_ntlm_auth_initial;
-	auth->auth_request.auth_continue = mech_ntlm_auth_continue;
-	auth->auth_request.auth_free = mech_ntlm_auth_free;
-
-	return &auth->auth_request;
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	return &request->auth_request;
 }
 
 const struct mech_module mech_ntlm = {
 	"NTLM",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) TRUE,
 
 	mech_ntlm_auth_new,
+	mech_ntlm_auth_initial,
+	mech_ntlm_auth_continue,
+	mech_ntlm_auth_free
 };
--- a/src/auth/mech-plain.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-plain.c	Wed Oct 13 19:38:32 2004 +0300
@@ -11,8 +11,8 @@
 	mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK);
 }
 
-static int
-mech_plain_auth_continue(struct auth_request *auth_request,
+static void
+mech_plain_auth_continue(struct auth_request *request,
 			 const unsigned char *data, size_t data_size,
 			 mech_callback_t *callback)
 {
@@ -20,7 +20,7 @@
 	char *pass;
 	size_t i, count, len;
 
-	auth_request->callback = callback;
+	request->callback = callback;
 
 	/* authorization ID \0 authentication ID \0 pass.
 	   we'll ignore authorization ID for now. */
@@ -46,93 +46,74 @@
 		/* invalid input */
 		if (verbose) {
 			i_info("plain(%s): invalid input",
-			       get_log_prefix(auth_request));
+			       get_log_prefix(request));
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_finish(request, NULL, 0, FALSE);
 	} else {
 		/* split and save user/realm */
 		if (strchr(authenid, '@') == NULL && default_realm != NULL) {
-			auth_request->user = p_strconcat(auth_request->pool,
-							 authenid, "@",
-							 default_realm, NULL);
+			request->user = p_strconcat(request->pool,
+						    authenid, "@",
+						    default_realm, NULL);
 		} else {
-			auth_request->user = p_strdup(auth_request->pool,
-						      authenid);
+			request->user = p_strdup(request->pool, authenid);
 		}
 
-		if (!mech_fix_username(auth_request->user, &error)) {
+		if (!mech_fix_username(request->user, &error)) {
 			/* invalid username */
 			if (verbose) {
 				i_info("plain(%s): %s",
-				       get_log_prefix(auth_request), error);
+				       get_log_prefix(request), error);
 			}
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			mech_auth_finish(request, NULL, 0, FALSE);
 		} else {
-			passdb->verify_plain(auth_request, pass,
-					     verify_callback);
+			passdb->verify_plain(request, pass, verify_callback);
 		}
 
 		/* make sure it's cleared */
 		safe_memset(pass, 0, strlen(pass));
 	}
-	return TRUE;
-}
-
-static int
-mech_plain_auth_initial(struct auth_request *auth_request,
-			struct auth_client_request_new *request,
-			const unsigned char *data,
-			mech_callback_t *callback)
-{
-	struct auth_client_request_reply reply;
-	size_t data_size;
-
-	if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
-		data += request->initial_resp_idx;
-		data_size = request->data_size - request->initial_resp_idx;
-
-		return auth_request->auth_continue(auth_request, data,
-						   data_size, callback);
-	}
-
-	/* initialize reply */
-	memset(&reply, 0, sizeof(reply));
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	callback(&reply, NULL, auth_request->conn);
-	return TRUE;
 }
 
 static void
-mech_plain_auth_free(struct auth_request *auth_request)
+mech_plain_auth_initial(struct auth_request *request,
+			const unsigned char *data, size_t data_size,
+			mech_callback_t *callback)
 {
-	pool_unref(auth_request->pool);
+	if (data_size == 0)
+		callback(request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0);
+	else
+		mech_plain_auth_continue(request, data, data_size, callback);
+}
+
+static void
+mech_plain_auth_free(struct auth_request *request)
+{
+	pool_unref(request->pool);
 }
 
 static struct auth_request *mech_plain_auth_new(void)
 {
-        struct auth_request *auth_request;
+        struct auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("plain_auth_request", 256);
-	auth_request = p_new(pool, struct auth_request, 1);
-	auth_request->refcount = 1;
-	auth_request->pool = pool;
-	auth_request->auth_initial = mech_plain_auth_initial;
-	auth_request->auth_continue = mech_plain_auth_continue;
-        auth_request->auth_free = mech_plain_auth_free;
-	return auth_request;
+	request = p_new(pool, struct auth_request, 1);
+	request->refcount = 1;
+	request->pool = pool;
+	return request;
 }
 
 struct mech_module mech_plain = {
 	"PLAIN",
 
-	MEMBER(plaintext) TRUE,
-	MEMBER(advertise) FALSE,
+	MEMBER(flags) MECH_SEC_PLAINTEXT,
 
 	MEMBER(passdb_need_plain) TRUE,
 	MEMBER(passdb_need_credentials) FALSE,
 
-	mech_plain_auth_new
+	mech_plain_auth_new,
+	mech_plain_auth_initial,
+	mech_plain_auth_continue,
+        mech_plain_auth_free
 };
--- a/src/auth/mech-rpa.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech-rpa.c	Wed Oct 13 19:38:32 2004 +0300
@@ -64,7 +64,7 @@
 /*
  * Compute client -> server authentication response.
  */
-static void rpa_user_response(struct rpa_auth_request *auth,
+static void rpa_user_response(struct rpa_auth_request *request,
 			      unsigned char *digest)
 {
 	struct md5_context ctx;
@@ -73,22 +73,22 @@
 	memset(z, 0, sizeof(z));
 
 	md5_init(&ctx);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_update(&ctx, z, sizeof(z));
-	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
-	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
-	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
-	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
-	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
-	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->username_ucs2be, request->username_len);
+	md5_update(&ctx, request->service_ucs2be, request->service_len);
+	md5_update(&ctx, request->realm_ucs2be, request->realm_len);
+	md5_update(&ctx, request->user_challenge, request->user_challenge_len);
+	md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_final(&ctx, digest);
 }
 
 /*
  * Compute server -> client authentication response.
  */
-static void rpa_server_response(struct rpa_auth_request *auth,
+static void rpa_server_response(struct rpa_auth_request *request,
 				unsigned char *digest)
 {
 	struct md5_context ctx;
@@ -99,32 +99,32 @@
 	memset(z, 0, sizeof(z));
 
 	md5_init(&ctx);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_update(&ctx, z, sizeof(z));
-	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
-	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
-	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
-	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
-	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
-	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->service_ucs2be, request->service_len);
+	md5_update(&ctx, request->username_ucs2be, request->username_len);
+	md5_update(&ctx, request->realm_ucs2be, request->realm_len);
+	md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, request->user_challenge, request->user_challenge_len);
+	md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_final(&ctx, tmp);
 
 	for (i = 0; i < 16; i++)
-		tmp[i] = auth->session_key[i] ^ tmp[i];
+		tmp[i] = request->session_key[i] ^ tmp[i];
 
 	md5_init(&ctx);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_update(&ctx, z, sizeof(z));
-	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
-	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
-	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
-	md5_update(&ctx, auth->session_key, 16);
-	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
-	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
-	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, request->service_ucs2be, request->service_len);
+	md5_update(&ctx, request->username_ucs2be, request->username_len);
+	md5_update(&ctx, request->realm_ucs2be, request->realm_len);
+	md5_update(&ctx, request->session_key, 16);
+	md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, request->user_challenge, request->user_challenge_len);
+	md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN);
 	md5_update(&ctx, tmp, 16);
-	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, request->pwd_md5, 16);
 	md5_final(&ctx, digest);
 }
 
@@ -248,10 +248,10 @@
 }
 
 static int
-rpa_parse_token3(struct rpa_auth_request *auth, const void *data,
+rpa_parse_token3(struct rpa_auth_request *request, const void *data,
 		 size_t data_size, const char **error)
 {
-	struct auth_request *auth_request = (struct auth_request *)auth;
+	struct auth_request *auth_request = &request->auth_request;
 	const unsigned char *end = ((unsigned char *)data) + data_size;
 	const unsigned char *p;
 	unsigned int len;
@@ -277,21 +277,21 @@
 	user = t_strndup(p, len);
 	p += len;
 
-	auth_request->user = rpa_parse_username(auth->pool, user);
+	auth_request->user = rpa_parse_username(request->pool, user);
 
-	auth->username_ucs2be = ucs2be_str(auth->pool, auth_request->user,
-					   &auth->username_len);
+	request->username_ucs2be = ucs2be_str(request->pool, auth_request->user,
+					      &request->username_len);
 
 	/* Read user challenge */
-	auth->user_challenge_len = rpa_read_buffer(auth->pool, &p, end,
-						   &auth->user_challenge);
-	if (auth->user_challenge_len == 0) {
+	request->user_challenge_len = rpa_read_buffer(request->pool, &p, end,
+						      &request->user_challenge);
+	if (request->user_challenge_len == 0) {
 		*error = "invalid user challenge";
 		return FALSE;
 	}
 
 	/* Read user response */
-	len = rpa_read_buffer(auth->pool, &p, end, &auth->user_response);
+	len = rpa_read_buffer(request->pool, &p, end, &request->user_response);
 	if (len != RPA_UCHALLENGE_LEN) {
 		*error = "invalid user response";
 		return FALSE;
@@ -321,7 +321,7 @@
 }
 
 static const unsigned char *
-mech_rpa_build_token2(struct rpa_auth_request *auth,
+mech_rpa_build_token2(struct rpa_auth_request *request,
 		      const char *realms, size_t *size)
 {
 	unsigned int realms_len;
@@ -333,7 +333,7 @@
         length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN +
 		RPA_TIMESTAMP_LEN + 2 + realms_len;
 
-	buf = buffer_create_dynamic(auth->pool, length + 4);
+	buf = buffer_create_dynamic(request->pool, length + 4);
 
 	buffer_append_c(buf, ASN1_APPLICATION);
 	buffer_append_asn1_length(buf, length);
@@ -344,18 +344,19 @@
 	buffer_append_c(buf, 0);
 
 	/* Service challenge */
-	auth->service_challenge = p_malloc(auth->pool, RPA_SCHALLENGE_LEN);
-	random_fill(auth->service_challenge, RPA_SCHALLENGE_LEN);
+	request->service_challenge =
+		p_malloc(request->pool, RPA_SCHALLENGE_LEN);
+	random_fill(request->service_challenge, RPA_SCHALLENGE_LEN);
 	buffer_append_c(buf, RPA_SCHALLENGE_LEN);
-	buffer_append(buf, auth->service_challenge, RPA_SCHALLENGE_LEN);
+	buffer_append(buf, request->service_challenge, RPA_SCHALLENGE_LEN);
 
 	/* Timestamp, looks like clients accept anything we send */
 	random_fill(timestamp, sizeof(timestamp));
-	auth->service_timestamp = p_malloc(auth->pool, RPA_TIMESTAMP_LEN);
-	memcpy(auth->service_timestamp,
+	request->service_timestamp = p_malloc(request->pool, RPA_TIMESTAMP_LEN);
+	memcpy(request->service_timestamp,
 	       binary_to_hex(timestamp, sizeof(timestamp)),
 	       RPA_TIMESTAMP_LEN);
-	buffer_append(buf, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+	buffer_append(buf, request->service_timestamp, RPA_TIMESTAMP_LEN);
 
 	/* Realm list */
 	buffer_append_c(buf, realms_len >> 8);
@@ -367,29 +368,29 @@
 }
 
 static const unsigned char *
-mech_rpa_build_token4(struct rpa_auth_request *auth, size_t *size)
+mech_rpa_build_token4(struct rpa_auth_request *request, size_t *size)
 {
 	unsigned int length = sizeof(rpa_oid) + 17 + 17 + 1;
 	buffer_t *buf;
 	unsigned char server_response[16];
 
-	buf = buffer_create_dynamic(auth->pool, length + 4);
+	buf = buffer_create_dynamic(request->pool, length + 4);
 
 	buffer_append_c(buf, ASN1_APPLICATION);
 	buffer_append_asn1_length(buf, length);
 	buffer_append(buf, rpa_oid, sizeof(rpa_oid));
 
 	/* Generate random session key */
-	auth->session_key = p_malloc(auth->pool, 16);
-	random_fill(auth->session_key, 16);
+	request->session_key = p_malloc(request->pool, 16);
+	random_fill(request->session_key, 16);
 
 	/* Server authentication response */
-	rpa_server_response(auth, server_response);
+	rpa_server_response(request, server_response);
 	buffer_append_c(buf, 16);
 	buffer_append(buf, server_response, 16);
 
 	buffer_append_c(buf, 16);
-	buffer_append(buf, auth->session_key, 16);
+	buffer_append(buf, request->session_key, 16);
 
 	/* Status, 0 - success */
 	buffer_append_c(buf, 0);
@@ -402,28 +403,24 @@
 rpa_credentials_callback(const char *credentials,
 			 struct auth_request *auth_request)
 {
-	struct rpa_auth_request *auth =
+	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
 	buffer_t *hash_buffer;
 
 	if (credentials == NULL)
 		return;
 
-	auth->pwd_md5 = p_malloc(auth->pool, 16);
-
-	hash_buffer = buffer_create_data(auth->pool, auth->pwd_md5, 16);
-
+	request->pwd_md5 = p_malloc(request->pool, 16);
+	hash_buffer = buffer_create_data(request->pool, request->pwd_md5, 16);
 	hex_to_binary(credentials, hash_buffer);
 }
 
-static int
+static void
 mech_rpa_auth_phase1(struct auth_request *auth_request,
-		     const unsigned char *data, size_t data_size,
-		     mech_callback_t *callback)
+		     const unsigned char *data, size_t data_size)
 {
-	struct rpa_auth_request *auth =
+	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
-	struct auth_client_request_reply reply;
 	const unsigned char *token2;
 	size_t token2_size;
 	const char *service, *error;
@@ -434,52 +431,42 @@
 			       get_log_prefix(auth_request), error);
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
 	service = t_str_lcase(auth_request->protocol);
 
-	token2 = mech_rpa_build_token2(auth, t_strconcat(service, "@",
+	token2 = mech_rpa_build_token2(request, t_strconcat(service, "@",
 				       my_hostname, NULL), &token2_size);
 
-	auth->service_ucs2be = ucs2be_str(auth->pool, service,
-					  &auth->service_len);
-	auth->realm_ucs2be = ucs2be_str(auth->pool, my_hostname,
-					&auth->realm_len);
+	request->service_ucs2be = ucs2be_str(request->pool, service,
+					     &request->service_len);
+	request->realm_ucs2be = ucs2be_str(request->pool, my_hostname,
+					   &request->realm_len);
 
-	mech_init_auth_client_reply(&reply);
-	reply.id = auth_request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	reply.reply_idx = 0;
-	reply.data_size = token2_size;
-	callback(&reply, token2, auth_request->conn);
-
-	auth->phase = 1;
-
-	return TRUE;
+	auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+			       token2, token2_size);
+	request->phase = 1;
 }
 
-static int
+static void
 mech_rpa_auth_phase2(struct auth_request *auth_request,
-		     const unsigned char *data, size_t data_size,
-		     mech_callback_t *callback)
+		     const unsigned char *data, size_t data_size)
 {
-	struct rpa_auth_request *auth =
+	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
-	struct auth_client_request_reply reply;
 	unsigned char response[16];
 	const unsigned char *token4;
 	const char *error;
 	size_t token4_size;
 
-	if (!rpa_parse_token3(auth, data, data_size, &error)) {
+	if (!rpa_parse_token3(request, data, data_size, &error)) {
 		if (verbose) {
 			i_info("rpa(%s): invalid token 3, %s",
 			       get_log_prefix(auth_request), error);
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
 	if (!mech_fix_username(auth_request->user, &error)) {
@@ -488,41 +475,31 @@
 			       get_log_prefix(auth_request), error);
 		}
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
 	passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_RPA,
 				   rpa_credentials_callback);
-	if (auth->pwd_md5 == NULL) {
+	if (request->pwd_md5 == NULL) {
 		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
-	}
-
-	rpa_user_response(auth, response);
-	if (memcmp(response, auth->user_response, 16) != 0) {
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return TRUE;
+		return;
 	}
 
-	token4 = mech_rpa_build_token4(auth, &token4_size);
-
-	mech_init_auth_client_reply(&reply);
-	reply.id = auth_request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+	rpa_user_response(request, response);
+	if (memcmp(response, request->user_response, 16) != 0) {
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return;
+	}
 
-	reply.reply_idx = 0;
-	reply.data_size = token4_size;
-	callback(&reply, token4, auth_request->conn);
-
-	auth->phase = 2;
-
-	return TRUE;
+	token4 = mech_rpa_build_token4(request, &token4_size);
+	auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+			       token4, token4_size);
+	request->phase = 2;
 }
 
-static int
+static void
 mech_rpa_auth_phase3(struct auth_request *auth_request,
-		     const unsigned char *data, size_t data_size,
-		     mech_callback_t *callback __attr_unused__)
+		     const unsigned char *data, size_t data_size)
 {
 	static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 };
 	int ret = TRUE;
@@ -537,90 +514,80 @@
 	}
 
 	mech_auth_finish(auth_request, NULL, 0, ret);
-	return TRUE;
 }
 
-static int
+static void
 mech_rpa_auth_continue(struct auth_request *auth_request,
-			const unsigned char *data, size_t data_size,
-			mech_callback_t *callback)
+		       const unsigned char *data, size_t data_size,
+		       mech_callback_t *callback)
 {
-	struct rpa_auth_request *auth =
+	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
 
 	auth_request->callback = callback;
 
-	switch (auth->phase) {
-		case 0:	return mech_rpa_auth_phase1(auth_request, data,
-						    data_size, callback);
-		case 1:	return mech_rpa_auth_phase2(auth_request, data,
-						    data_size, callback);
-		case 2:	return mech_rpa_auth_phase3(auth_request, data,
-						    data_size, callback);
+	switch (request->phase) {
+	case 0:
+		mech_rpa_auth_phase1(auth_request, data, data_size);
+		break;
+	case 1:
+		mech_rpa_auth_phase2(auth_request, data, data_size);
+		break;
+	case 2:
+		mech_rpa_auth_phase3(auth_request, data, data_size);
+		break;
+	default:
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		break;
 	}
-
-	mech_auth_finish(auth_request, NULL, 0, FALSE);
-	return TRUE;
 }
 
-static int
+static void
 mech_rpa_auth_initial(struct auth_request *auth_request,
-		      struct auth_client_request_new *request,
 		      const unsigned char *data __attr_unused__,
+		      size_t data_size __attr_unused__,
 		      mech_callback_t *callback)
 {
-	struct auth_client_request_reply reply;
-
-	mech_init_auth_client_reply(&reply);
-	reply.id = request->id;
-	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-	reply.reply_idx = 0;
-	reply.data_size = 0;
-	callback(&reply, "", auth_request->conn);
-
-	return TRUE;
+	callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0);
 }
 
 static void
 mech_rpa_auth_free(struct auth_request *auth_request)
 {
-	struct rpa_auth_request *auth =
+	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
 
-	if (auth->pwd_md5 != NULL)
-		safe_memset(auth->pwd_md5, 0, 16);
+	if (request->pwd_md5 != NULL)
+		safe_memset(request->pwd_md5, 0, 16);
 
 	pool_unref(auth_request->pool);
 }
 
 static struct auth_request *mech_rpa_auth_new(void)
 {
-	struct rpa_auth_request *auth;
+	struct rpa_auth_request *request;
 	pool_t pool;
 
 	pool = pool_alloconly_create("rpa_auth_request", 256);
-	auth = p_new(pool, struct rpa_auth_request, 1);
-	auth->pool = pool;
-	auth->phase = 0;
+	request = p_new(pool, struct rpa_auth_request, 1);
+	request->pool = pool;
+	request->phase = 0;
 
-	auth->auth_request.refcount = 1;
-	auth->auth_request.pool = pool;
-	auth->auth_request.auth_initial = mech_rpa_auth_initial;
-	auth->auth_request.auth_continue = mech_rpa_auth_continue;
-	auth->auth_request.auth_free = mech_rpa_auth_free;
-
-	return &auth->auth_request;
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	return &request->auth_request;
 }
 
 const struct mech_module mech_rpa = {
 	"RPA",
 
-	MEMBER(plaintext) FALSE,
-	MEMBER(advertise) TRUE,
+	MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
 
 	MEMBER(passdb_need_plain) FALSE,
 	MEMBER(passdb_need_credentials) TRUE,
 
 	mech_rpa_auth_new,
+	mech_rpa_auth_initial,
+	mech_rpa_auth_continue,
+	mech_rpa_auth_free
 };
--- a/src/auth/mech.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech.c	Wed Oct 13 19:38:32 2004 +0300
@@ -14,14 +14,14 @@
 #include <stdlib.h>
 
 struct mech_module_list *mech_modules;
+buffer_t *mech_handshake;
+
 const char *const *auth_realms;
 const char *default_realm;
 const char *anonymous_username;
 char username_chars[256], username_translation[256];
 int ssl_require_client_cert;
 
-static struct auth_client_request_reply failure_reply;
-
 static buffer_t *auth_failures_buf;
 static struct timeout *to_auth_failures;
 
@@ -32,6 +32,23 @@
 	list = i_new(struct mech_module_list, 1);
 	list->module = *module;
 
+	str_printfa(mech_handshake, "MECH\t%s", module->mech_name);
+	if ((module->flags & MECH_SEC_PRIVATE) != 0)
+		str_append(mech_handshake, "\tprivate");
+	if ((module->flags & MECH_SEC_ANONYMOUS) != 0)
+		str_append(mech_handshake, "\tanonymous");
+	if ((module->flags & MECH_SEC_PLAINTEXT) != 0)
+		str_append(mech_handshake, "\tplaintext");
+	if ((module->flags & MECH_SEC_DICTIONARY) != 0)
+		str_append(mech_handshake, "\tdictionary");
+	if ((module->flags & MECH_SEC_ACTIVE) != 0)
+		str_append(mech_handshake, "\tactive");
+	if ((module->flags & MECH_SEC_FORWARD_SECRECY) != 0)
+		str_append(mech_handshake, "\tforward-secrecy");
+	if ((module->flags & MECH_SEC_MUTUAL_AUTH) != 0)
+		str_append(mech_handshake, "\tmutual-auth");
+	str_append_c(mech_handshake, '\n');
+
 	list->next = mech_modules;
 	mech_modules = list;
 }
@@ -62,7 +79,7 @@
 	return str;
 }
 
-static struct mech_module *mech_module_find(const char *name)
+struct mech_module *mech_module_find(const char *name)
 {
 	struct mech_module_list *list;
 
@@ -73,183 +90,48 @@
 	return NULL;
 }
 
-void mech_request_new(struct auth_client_connection *conn,
-		      struct auth_client_request_new *request,
-		      const unsigned char *data,
-		      mech_callback_t *callback)
+struct auth_request *auth_request_new(struct mech_module *mech)
 {
-        struct mech_module *mech;
-	struct auth_request *auth_request;
-	size_t ip_size = 1;
+	struct auth_request *request;
 
-	if (request->ip_family == AF_INET)
-		ip_size = 4;
-	else if (request->ip_family != 0)
-		ip_size = sizeof(auth_request->local_ip.ip);
-	else
-		ip_size = 0;
+	request = mech->auth_new();
+	if (request == NULL)
+		return NULL;
 
-	/* make sure data is NUL-terminated */
-	if (request->data_size <= ip_size*2 || request->initial_resp_idx == 0 ||
-	    request->mech_idx >= request->data_size ||
-	    request->protocol_idx >= request->data_size ||
-	    request->initial_resp_idx > request->data_size ||
-	    data[request->initial_resp_idx-1] != '\0') {
-		i_error("BUG: Auth client %u sent corrupted request",
-			conn->pid);
-		failure_reply.id = request->id;
-		callback(&failure_reply, NULL, conn);
-		return;
-	}
+	request->mech = mech;
+	request->created = ioloop_time;
+	return request;
+}
 
-	mech = mech_module_find((const char *)data + request->mech_idx);
-	if (mech == NULL) {
-		/* unsupported mechanism */
-		i_error("BUG: Auth client %u requested unsupported "
-			"auth mechanism %s", conn->pid,
-			(const char *)data + request->mech_idx);
-		failure_reply.id = request->id;
-		callback(&failure_reply, NULL, conn);
-		return;
+void auth_request_destroy(struct auth_request *request)
+{
+	if (request->conn != NULL) {
+		hash_remove(request->conn->auth_requests,
+			    POINTER_CAST(request->id));
 	}
-
-	auth_request = mech->auth_new();
-	if (auth_request == NULL)
-		return;
+	auth_request_unref(request);
+}
 
-	auth_request->created = ioloop_time;
-	auth_request->conn = conn;
-	auth_request->id = request->id;
-	auth_request->protocol =
-		p_strdup(auth_request->pool,
-			 (const char *)data + request->protocol_idx);
-
-	if (request->ip_family != 0) {
-		auth_request->local_ip.family = request->ip_family;
-		auth_request->remote_ip.family = request->ip_family;
-
-		memcpy(&auth_request->local_ip.ip, data, ip_size);
-		memcpy(&auth_request->remote_ip.ip, data + ip_size, ip_size);
-	}
-
-	if (ssl_require_client_cert &&
-	    (request->flags & AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT) == 0) {
-		/* we fail without valid certificate */
-		if (verbose) {
-			i_info("ssl-cert-check(%s): "
-			       "Client didn't present valid SSL certificate",
-			       get_log_prefix(auth_request));
-		}
-		auth_request_unref(auth_request);
-
-		failure_reply.id = request->id;
-		callback(&failure_reply, NULL, conn);
+void mech_auth_finish(struct auth_request *request,
+		      const void *data, size_t data_size, int success)
+{
+	if (!success) {
+		/* failure. don't announce it immediately to avoid
+		   a) timing attacks, b) flooding */
+		buffer_append(auth_failures_buf, &request, sizeof(request));
 		return;
 	}
 
-	hash_insert(conn->auth_requests, POINTER_CAST(request->id),
-		    auth_request);
-
-	if (!auth_request->auth_initial(auth_request, request, data, callback))
-		mech_request_free(auth_request, request->id);
-}
-
-void mech_request_continue(struct auth_client_connection *conn,
-			   struct auth_client_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(auth_request,
-						 data, request->data_size,
-						 callback))
-			mech_request_free(auth_request, request->id);
-	}
-}
-
-void mech_request_free(struct auth_request *auth_request, unsigned int id)
-{
-	if (auth_request->conn != NULL) {
-		hash_remove(auth_request->conn->auth_requests,
-			    POINTER_CAST(id));
-	}
-	auth_request_unref(auth_request);
-}
-
-void mech_init_auth_client_reply(struct auth_client_request_reply *reply)
-{
-	memset(reply, 0, sizeof(*reply));
-
-	reply->username_idx = (uint32_t)-1;
-	reply->reply_idx = (uint32_t)-1;
-}
-
-void *mech_auth_success(struct auth_client_request_reply *reply,
-			struct auth_request *auth_request,
-			const void *data, size_t data_size)
-{
-	buffer_t *buf;
-
-	buf = buffer_create_dynamic(pool_datastack_create(), 256);
-
-	reply->username_idx = 0;
-	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);
-
-	if (data_size == 0)
-		reply->reply_idx = (uint32_t)-1;
-	else {
-		reply->reply_idx = buffer_get_used_size(buf);
-		buffer_append(buf, data, data_size);
+	if (request->conn != NULL) {
+		request->callback(request, AUTH_CLIENT_RESULT_SUCCESS,
+				  data, data_size);
 	}
 
-	reply->result = AUTH_CLIENT_RESULT_SUCCESS;
-	reply->data_size = buffer_get_used_size(buf);
-	return buffer_get_modifyable_data(buf, NULL);
-}
-
-void mech_auth_finish(struct auth_request *auth_request,
-		      const void *data, size_t data_size, int success)
-{
-	struct auth_client_request_reply reply;
-	void *reply_data;
-	int free_request;
-
-	if (!success) {
-		/* failure. don't announce it immediately to avoid
-		   a) timing attacks, b) flooding */
-		buffer_append(auth_failures_buf,
-			      &auth_request, sizeof(auth_request));
-		return;
-	}
-
-	memset(&reply, 0, sizeof(reply));
-	reply.id = auth_request->id;
-	reply.result = AUTH_CLIENT_RESULT_SUCCESS;
-
-	if (auth_request->conn == NULL) {
-		/* client is already gone */
-		free_request = TRUE;
-	} else {
-		/* get this before callback because it can destroy connection */
-		free_request = AUTH_MASTER_IS_DUMMY(auth_request->conn->master);
-
-		reply_data = mech_auth_success(&reply, auth_request,
-					       data, data_size);
-		auth_request->callback(&reply, reply_data, auth_request->conn);
-	}
-
-	if (free_request) {
+	if (request->conn == NULL ||
+	    AUTH_MASTER_IS_DUMMY(request->conn->master)) {
 		/* we don't have master process, the request is no longer
 		   needed */
-		mech_request_free(auth_request, auth_request->id);
+		auth_request_destroy(request);
 	}
 }
 
@@ -285,7 +167,7 @@
 	if (--request->refcount > 0)
 		return TRUE;
 
-	request->auth_free(request);
+	request->mech->auth_free(request);
 	return FALSE;
 }
 
@@ -358,22 +240,18 @@
 void auth_failure_buf_flush(void)
 {
 	struct auth_request **auth_request;
-	struct auth_client_request_reply reply;
 	size_t i, size;
 
 	auth_request = buffer_get_modifyable_data(auth_failures_buf, &size);
 	size /= sizeof(*auth_request);
 
-	memset(&reply, 0, sizeof(reply));
-	reply.result = AUTH_CLIENT_RESULT_FAILURE;
-
 	for (i = 0; i < size; i++) {
-		reply.id = auth_request[i]->id;
 		if (auth_request[i]->conn != NULL) {
-			auth_request[i]->callback(&reply, NULL,
-						  auth_request[i]->conn);
+			auth_request[i]->callback(auth_request[i],
+						  AUTH_CLIENT_RESULT_FAILURE,
+						  NULL, 0);
 		}
-		mech_request_free(auth_request[i], reply.id);
+		auth_request_destroy(auth_request[i]);
 	}
 	buffer_set_used_size(auth_failures_buf, 0);
 }
@@ -397,10 +275,8 @@
 	const char *const *mechanisms;
 	const char *env;
 
-        mech_modules = NULL;
-
-	memset(&failure_reply, 0, sizeof(failure_reply));
-	failure_reply.result = AUTH_CLIENT_RESULT_FAILURE;
+	mech_modules = NULL;
+	mech_handshake = str_new(default_pool, 512);
 
 	anonymous_username = getenv("ANONYMOUS_USERNAME");
 	if (anonymous_username != NULL && *anonymous_username == '\0')
@@ -493,4 +369,6 @@
 	mech_unregister_module(&mech_ntlm);
 	mech_unregister_module(&mech_rpa);
 	mech_unregister_module(&mech_anonymous);
+
+	str_free(mech_handshake);
 }
--- a/src/auth/mech.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/mech.h	Wed Oct 13 19:38:32 2004 +0300
@@ -4,11 +4,18 @@
 #include "network.h"
 #include "auth-client-interface.h"
 
+enum auth_client_result {
+	AUTH_CLIENT_RESULT_CONTINUE = 1,
+	AUTH_CLIENT_RESULT_SUCCESS,
+	AUTH_CLIENT_RESULT_FAILURE
+};
+
+struct auth_request;
 struct auth_client_connection;
 
-typedef void mech_callback_t(struct auth_client_request_reply *reply,
-			     const void *data,
-			     struct auth_client_connection *conn);
+typedef void mech_callback_t(struct auth_request *request,
+			     enum auth_client_result result,
+			     const void *reply, size_t reply_size);
 
 struct auth_request {
 	int refcount;
@@ -16,34 +23,35 @@
 	pool_t pool;
 	char *user;
 
+	struct mech_module *mech;
 	struct auth_client_connection *conn;
+
 	unsigned int id;
 	time_t created;
 
-	char *protocol;
+	const char *protocol;
 	struct ip_addr local_ip, remote_ip;
 	mech_callback_t *callback;
 
-	int (*auth_initial)(struct auth_request *auth_request,
-                            struct auth_client_request_new *request,
-			    const unsigned char *data,
-			    mech_callback_t *callback);
-	int (*auth_continue)(struct auth_request *auth_request,
-			     const unsigned char *data, size_t data_size,
-			     mech_callback_t *callback);
-	void (*auth_free)(struct auth_request *auth_request);
+	unsigned int accept_input:1;
 	/* ... mechanism specific data ... */
 };
 
 struct mech_module {
 	const char *mech_name;
 
-	unsigned int plaintext:1;
-	unsigned int advertise:1;
+        enum mech_security_flags flags;
 	unsigned int passdb_need_plain:1;
 	unsigned int passdb_need_credentials:1;
 
 	struct auth_request *(*auth_new)(void);
+	void (*auth_initial)(struct auth_request *request,
+			     const unsigned char *data, size_t data_size,
+			     mech_callback_t *callback);
+	void (*auth_continue)(struct auth_request *request,
+			      const unsigned char *data, size_t data_size,
+			      mech_callback_t *callback);
+	void (*auth_free)(struct auth_request *request);
 };
 
 struct mech_module_list {
@@ -53,6 +61,8 @@
 };
 
 extern struct mech_module_list *mech_modules;
+extern buffer_t *mech_handshake;
+
 extern const char *const *auth_realms;
 extern const char *default_realm;
 extern const char *anonymous_username;
@@ -61,28 +71,17 @@
 
 void mech_register_module(struct mech_module *module);
 void mech_unregister_module(struct mech_module *module);
+struct mech_module *mech_module_find(const char *name);
 
 const string_t *auth_mechanisms_get_list(void);
 
-void mech_request_new(struct auth_client_connection *conn,
-		      struct auth_client_request_new *request,
-		      const unsigned char *data,
-		      mech_callback_t *callback);
-void mech_request_continue(struct auth_client_connection *conn,
-			   struct auth_client_request_continue *request,
-			   const unsigned char *data,
-			   mech_callback_t *callback);
-void mech_request_free(struct auth_request *auth_request, unsigned int id);
-
-void mech_init_auth_client_reply(struct auth_client_request_reply *reply);
-void *mech_auth_success(struct auth_client_request_reply *reply,
-			struct auth_request *auth_request,
-			const void *data, size_t data_size);
-void mech_auth_finish(struct auth_request *auth_request,
+void mech_auth_finish(struct auth_request *request,
 		      const void *data, size_t data_size, int success);
 
 int mech_fix_username(char *username, const char **error_r);
 
+struct auth_request *auth_request_new(struct mech_module *mech);
+void auth_request_destroy(struct auth_request *request);
 void auth_request_ref(struct auth_request *request);
 int auth_request_unref(struct auth_request *request);
 
--- a/src/auth/userdb.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/auth/userdb.h	Wed Oct 13 19:38:32 2004 +0300
@@ -13,7 +13,7 @@
 	gid_t gid;
 };
 
-typedef void userdb_callback_t(struct user_data *user, void *context);
+typedef void userdb_callback_t(const struct user_data *user, void *context);
 
 struct userdb_module {
 	void (*preinit)(const char *args);
--- a/src/imap-login/client-authenticate.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/imap-login/client-authenticate.c	Wed Oct 13 19:38:32 2004 +0300
@@ -26,10 +26,10 @@
 		/* a) transport is secured
 		   b) auth mechanism isn't plaintext
 		   c) we allow insecure authentication
-		        - but don't advertise AUTH=PLAIN, as RFC 2595 requires
 		*/
-		if (mech[i].advertise &&
-		    (secured || !mech[i].plaintext)) {
+		if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
+		    (secured || !disable_plaintext_auth ||
+		     (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
 			str_append_c(str, ' ');
 			str_append(str, "AUTH=");
 			str_append(str, mech[i].name);
@@ -42,9 +42,7 @@
 static void client_auth_input(void *context)
 {
 	struct imap_client *client = context;
-	buffer_t *buf;
 	char *line;
-	size_t linelen, bufsize;
 
 	if (!client_read(client))
 		return;
@@ -67,25 +65,15 @@
 		return;
 	}
 
-	linelen = strlen(line);
-	buf = buffer_create_static_hard(pool_datastack_create(), linelen);
-
-	if (base64_decode(line, linelen, NULL, buf) < 0) {
-		/* failed */
-		sasl_server_auth_cancel(&client->common, "Invalid base64 data");
-	} else if (client->common.auth_request == NULL) {
+	if (client->common.auth_request == NULL) {
 		sasl_server_auth_cancel(&client->common,
 					"Don't send unrequested data");
 	} else {
-		auth_client_request_continue(client->common.auth_request,
-					     buf->data, buf->used);
+		auth_client_request_continue(client->common.auth_request, line);
 	}
 
 	/* clear sensitive data */
-	safe_memset(line, 0, linelen);
-
-	bufsize = buffer_get_used_size(buf);
-	safe_memset(buffer_free_without_data(buf), 0, bufsize);
+	safe_memset(line, 0, strlen(line));
 }
 
 static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
@@ -162,7 +150,7 @@
 		return FALSE;
 
 	client_ref(client);
-	sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL, 0,
+	sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL,
 			       sasl_callback);
 	if (!client->common.authenticating)
 		return 1;
@@ -178,7 +166,7 @@
 int cmd_login(struct imap_client *client, struct imap_arg *args)
 {
 	const char *user, *pass;
-	string_t *plain_login;
+	string_t *plain_login, *base64;
 
 	/* two arguments: username and password */
 	if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING)
@@ -212,10 +200,13 @@
 	buffer_append_c(plain_login, '\0');
 	buffer_append(plain_login, pass, strlen(pass));
 
+	base64 = buffer_create_dynamic(pool_datastack_create(),
+        			MAX_BASE64_ENCODED_SIZE(plain_login->used));
+	base64_encode(plain_login->data, plain_login->used, base64);
+
 	client_ref(client);
 	sasl_server_auth_begin(&client->common, "IMAP", "PLAIN",
-			       plain_login->data, plain_login->used,
-			       sasl_callback);
+			       str_c(base64), sasl_callback);
 	if (!client->common.authenticating)
 		return 1;
 
--- a/src/lib-auth/auth-client.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/lib-auth/auth-client.h	Wed Oct 13 19:38:32 2004 +0300
@@ -7,10 +7,14 @@
 struct auth_client;
 struct auth_request;
 
+enum auth_request_flags {
+	AUTH_REQUEST_FLAG_SECURED		= 0x01,
+	AUTH_REQUEST_FLAG_VALID_CLIENT_CERT	= 0x02,
+};
+
 struct auth_mech_desc {
 	char *name;
-	unsigned int plaintext:1;
-	unsigned int advertise:1;
+        enum mech_security_flags flags;
 };
 
 struct auth_connect_id {
@@ -21,18 +25,16 @@
 struct auth_request_info {
 	const char *mech;
 	const char *protocol;
-	enum auth_client_request_new_flags flags;
+	enum auth_request_flags flags;
 
 	struct ip_addr local_ip, remote_ip;
 
-	const unsigned char *initial_resp_data;
-	size_t initial_resp_size;
+	const char *initial_resp_base64;
 };
 
-/* reply is NULL if auth connection died */
-typedef void auth_request_callback_t(struct auth_request *request,
-				     struct auth_client_request_reply *reply,
-				     const unsigned char *data, void *context);
+typedef void auth_request_callback_t(struct auth_request *request, int status,
+				     const char *data_base64,
+				     const char *const *args, void *context);
 
 typedef void auth_connect_notify_callback_t(struct auth_client *client,
 					    int connected, void *context);
@@ -69,7 +71,7 @@
 /* Continue authentication. Call when
    reply->result == AUTH_CLIENT_REQUEST_CONTINUE */
 void auth_client_request_continue(struct auth_request *request,
-				  const unsigned char *data, size_t data_size);
+				  const char *data_base64);
 
 /* Abort ongoing authentication request. */
 void auth_client_request_abort(struct auth_request *request);
--- a/src/lib-auth/auth-server-connection.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/lib-auth/auth-server-connection.c	Wed Oct 13 19:38:32 2004 +0300
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 Timo Sirainen */
+/* Copyright (C) 2003-2004 Timo Sirainen */
 
 #include "lib.h"
 #include "buffer.h"
@@ -12,13 +12,7 @@
 #include "auth-server-request.h"
 
 #include <unistd.h>
-
-/* Maximum size for an auth reply. 50kB should be more than enough. */
-#define MAX_INBUF_SIZE (1024*50)
-
-#define MAX_OUTBUF_SIZE \
-	(sizeof(struct auth_client_request_continue) + \
-	 AUTH_CLIENT_MAX_REQUEST_DATA_SIZE)
+#include <stdlib.h>
 
 static void auth_server_connection_unref(struct auth_server_connection *conn);
 
@@ -40,54 +34,81 @@
 	}
 }
 
-static void auth_handle_handshake(struct auth_server_connection *conn,
-				  struct auth_client_handshake_reply *handshake,
-				  const unsigned char *data)
+static int
+auth_client_input_mech(struct auth_server_connection *conn, const char *args)
 {
-	struct auth_client_handshake_mech_desc handshake_mech_desc;
+	const char *const *list;
 	struct auth_mech_desc mech_desc;
-	buffer_t *buf;
-	unsigned int i;
 
-	if (handshake->data_size == 0 || data[handshake->data_size-1] != '\0' ||
-	    handshake->mech_count * sizeof(handshake_mech_desc) >=
-	    handshake->data_size)  {
-		i_error("BUG: Auth server sent corrupted handshake");
-		auth_server_connection_destroy(conn, FALSE);
-		return;
+	if (conn->handshake_received) {
+		i_error("BUG: Authentication server already sent handshake");
+		return FALSE;
+	}
+
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL) {
+		i_error("BUG: Authentication server sent broken MECH line");
+		return FALSE;
 	}
 
-	buf = buffer_create_dynamic(conn->pool, sizeof(mech_desc) *
-				    handshake->mech_count);
-	for (i = 0; i < handshake->mech_count; i++) {
-		memcpy(&handshake_mech_desc,
-		       data + sizeof(handshake_mech_desc) * i,
-		       sizeof(handshake_mech_desc));
+	memset(&mech_desc, 0, sizeof(mech_desc));
+	mech_desc.name = p_strdup(conn->pool, list[0]);
+
+	if (strcmp(mech_desc.name, "PLAIN") == 0)
+		conn->has_plain_mech = TRUE;
 
-		if (handshake_mech_desc.name_idx >= handshake->data_size) {
-			i_error("BUG: Auth server sent corrupted handshake");
-			auth_server_connection_destroy(conn, FALSE);
-			return;
-		}
+	for (list++; *list != NULL; list++) {
+		if (strcmp(*list, "private") == 0)
+			mech_desc.flags |= MECH_SEC_PRIVATE;
+		else if (strcmp(*list, "anonymous") == 0)
+			mech_desc.flags |= MECH_SEC_ANONYMOUS;
+		else if (strcmp(*list, "plaintext") == 0)
+			mech_desc.flags |= MECH_SEC_PLAINTEXT;
+		else if (strcmp(*list, "dictionary") == 0)
+			mech_desc.flags |= MECH_SEC_DICTIONARY;
+		else if (strcmp(*list, "active") == 0)
+			mech_desc.flags |= MECH_SEC_ACTIVE;
+		else if (strcmp(*list, "forward-secrecy") == 0)
+			mech_desc.flags |= MECH_SEC_FORWARD_SECRECY;
+		else if (strcmp(*list, "mutual-auth") == 0)
+			mech_desc.flags |= MECH_SEC_MUTUAL_AUTH;
+	}
+	buffer_append(conn->auth_mechs_buf, &mech_desc, sizeof(mech_desc));
+	return TRUE;
+}
 
-		mech_desc.name = p_strdup(conn->pool, (const char *)data +
-					  handshake_mech_desc.name_idx);
-		mech_desc.plaintext = handshake_mech_desc.plaintext;
-		mech_desc.advertise = handshake_mech_desc.advertise;
-		buffer_append(buf, &mech_desc, sizeof(mech_desc));
-
-		if (strcmp(mech_desc.name, "PLAIN") == 0)
-			conn->has_plain_mech = TRUE;
+static int
+auth_client_input_spid(struct auth_server_connection *conn, const char *args)
+{
+	if (conn->handshake_received) {
+		i_error("BUG: Authentication server already sent handshake");
+		return FALSE;
 	}
 
-	conn->server_pid = handshake->server_pid;
-	conn->connect_uid = handshake->connect_uid;
+	conn->server_pid = (unsigned int)strtoul(args, NULL, 10);
+	return TRUE;
+}
+
+static int
+auth_client_input_cuid(struct auth_server_connection *conn, const char *args)
+{
+	if (conn->handshake_received) {
+		i_error("BUG: Authentication server already sent handshake");
+		return FALSE;
+	}
+
+	conn->connect_uid = (unsigned int)strtoul(args, NULL, 10);
+	return TRUE;
+}
+
+static int auth_client_input_done(struct auth_server_connection *conn)
+{
+	conn->available_auth_mechs = conn->auth_mechs_buf->data;
 	conn->available_auth_mechs_count =
-		buffer_get_used_size(buf) / sizeof(mech_desc);
-	conn->available_auth_mechs = buffer_free_without_data(buf);
+		conn->auth_mechs_buf->used / sizeof(struct auth_mech_desc);
+
 	conn->handshake_received = TRUE;
-
-        conn->client->conn_waiting_handshake_count--;
+	conn->client->conn_waiting_handshake_count--;
 	update_available_auth_mechs(conn);
 
 	if (conn->client->connect_notify_callback != NULL &&
@@ -95,14 +116,14 @@
 		conn->client->connect_notify_callback(conn->client, TRUE,
 				conn->client->connect_notify_context);
 	}
+	return TRUE;
 }
 
 static void auth_client_input(void *context)
 {
 	struct auth_server_connection *conn = context;
-	struct auth_client_handshake_reply handshake;
-	const unsigned char *data;
-	size_t size;
+	const char *line;
+	int ret;
 
 	switch (i_stream_read(conn->input)) {
 	case 0:
@@ -114,50 +135,37 @@
 	case -2:
 		/* buffer full - can't happen unless auth is buggy */
 		i_error("BUG: Auth server sent us more than %d bytes of data",
-			MAX_INBUF_SIZE);
+			AUTH_CLIENT_MAX_LINE_LENGTH);
 		auth_server_connection_destroy(conn, FALSE);
 		return;
 	}
 
-	if (!conn->handshake_received) {
-		data = i_stream_get_data(conn->input, &size);
-		if (size < sizeof(handshake))
-			return;
-
-		memcpy(&handshake, data, sizeof(handshake));
-		if (size < sizeof(handshake) + handshake.data_size)
-			return;
-
-		conn->refcount++;
-		auth_handle_handshake(conn, &handshake,
-				      data + sizeof(handshake));
-		i_stream_skip(conn->input, sizeof(handshake) +
-			      handshake.data_size);
-		auth_server_connection_unref(conn);
-		return;
-	}
+	conn->refcount++;
+	while ((line = i_stream_next_line(conn->input)) != NULL) {
+		if (strncmp(line, "OK\t", 3) == 0)
+			ret = auth_client_input_ok(conn, line + 3);
+		else if (strncmp(line, "CONT\t", 5) == 0)
+			ret = auth_client_input_cont(conn, line + 5);
+        	else if (strncmp(line, "FAIL\t", 5) == 0)
+			ret = auth_client_input_fail(conn, line + 5);
+		else if (strncmp(line, "MECH\t", 5) == 0)
+			ret = auth_client_input_mech(conn, line + 5);
+		else if (strncmp(line, "SPID\t", 5) == 0)
+			ret = auth_client_input_spid(conn, line + 5);
+		else if (strncmp(line, "CUID\t", 5) == 0)
+			ret = auth_client_input_cuid(conn, line + 5);
+		else if (strcmp(line, "DONE") == 0)
+			ret = auth_client_input_done(conn);
+		else {
+			/* ignore unknown command */
+			ret = TRUE;
+		}
 
-	if (!conn->reply_received) {
-		data = i_stream_get_data(conn->input, &size);
-		if (size < sizeof(conn->reply))
-			return;
-
-		memcpy(&conn->reply, data, sizeof(conn->reply));
-		i_stream_skip(conn->input, sizeof(conn->reply));
-		conn->reply_received = TRUE;
+		if (!ret) {
+			auth_server_connection_destroy(conn, FALSE);
+			break;
+		}
 	}
-
-	data = i_stream_get_data(conn->input, &size);
-	if (size < conn->reply.data_size)
-		return;
-
-	/* we've got a full reply */
-	conn->refcount++;
-	conn->reply_received = FALSE;
-
-	auth_server_request_handle_reply(conn, &conn->reply, data);
-	i_stream_skip(conn->input, conn->reply.data_size);
-
 	auth_server_connection_unref(conn);
 }
 
@@ -165,7 +173,6 @@
 auth_server_connection_new(struct auth_client *client, const char *path)
 {
 	struct auth_server_connection *conn;
-	struct auth_client_handshake_request handshake;
 	pool_t pool;
 	int fd;
 
@@ -192,21 +199,19 @@
 		conn->ext_input_io =
 			client->ext_input_add(fd, auth_client_input, conn);
 	}
-	conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
-					   FALSE);
+	conn->input = i_stream_create_file(fd, default_pool,
+					   AUTH_CLIENT_MAX_LINE_LENGTH, FALSE);
 	conn->output = o_stream_create_file(fd, default_pool, (size_t)-1,
 					    FALSE);
 	conn->requests = hash_create(default_pool, pool, 100, NULL, NULL);
+	conn->auth_mechs_buf = buffer_create_dynamic(default_pool, 256);
 
 	conn->next = client->connections;
 	client->connections = conn;
 
-	/* send our handshake */
-	memset(&handshake, 0, sizeof(handshake));
-	handshake.client_pid = client->pid;
-
         client->conn_waiting_handshake_count++;
-	if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) {
+	if (o_stream_send_str(conn->output,
+			      t_strdup_printf("CPID\t%u\n", client->pid)) < 0) {
 		errno = conn->output->stream_errno;
 		i_warning("Error sending handshake to auth server: %m");
 		auth_server_connection_destroy(conn, TRUE);
@@ -252,7 +257,7 @@
 	conn->fd = -1;
 
 	auth_server_requests_remove_all(conn);
-        auth_server_connection_unref(conn);
+	auth_server_connection_unref(conn);
 
 	if (reconnect)
 		auth_client_connect_missing_servers(client);
@@ -270,6 +275,7 @@
 	i_assert(conn->refcount == 0);
 
 	hash_destroy(conn->requests);
+	buffer_free(conn->auth_mechs_buf);
 
 	i_stream_unref(conn->input);
 	o_stream_unref(conn->output);
--- a/src/lib-auth/auth-server-connection.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/lib-auth/auth-server-connection.h	Wed Oct 13 19:38:32 2004 +0300
@@ -40,14 +40,13 @@
 	unsigned int server_pid;
 	unsigned int connect_uid;
 
+	buffer_t *auth_mechs_buf;
 	const struct auth_mech_desc *available_auth_mechs;
 	unsigned int available_auth_mechs_count;
-        struct auth_client_request_reply reply;
 
         struct hash_table *requests;
 
 	unsigned int handshake_received:1;
-	unsigned int reply_received:1;
 	unsigned int has_plain_mech:1;
 };
 
--- a/src/lib-auth/auth-server-request.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/lib-auth/auth-server-request.c	Wed Oct 13 19:38:32 2004 +0300
@@ -1,31 +1,31 @@
-/* Copyright (C) 2003 Timo Sirainen */
+/* Copyright (C) 2003-2004 Timo Sirainen */
 
 #include "lib.h"
-#include "buffer.h"
+#include "str.h"
 #include "hash.h"
 #include "ostream.h"
 #include "auth-client.h"
 #include "auth-server-connection.h"
 #include "auth-server-request.h"
 
+#include <stdlib.h>
+
 struct auth_request {
         struct auth_server_connection *conn;
 
 	unsigned int id;
 
 	char *mech, *protocol;
-	enum auth_client_request_new_flags flags;
+        enum auth_request_flags flags;
 	struct ip_addr local_ip, remote_ip;
 
-	unsigned char *initial_resp_data;
-	size_t initial_resp_size;
+	char *initial_resp_base64;
 
 	auth_request_callback_t *callback;
 	void *context;
 
         struct auth_server_connection *next_conn;
-	unsigned char *plaintext_data; /* for resending to other servers */
-        size_t plaintext_data_size;
+	char *plaintext_data; /* for resending to other servers */
 
 	unsigned int init_sent:1;
 	unsigned int retrying:1;
@@ -33,6 +33,7 @@
 
 static int auth_server_send_new_request(struct auth_server_connection *conn,
 					struct auth_request *request);
+static void auth_client_request_free(struct auth_request *request);
 
 static struct auth_server_connection *
 get_next_plain_server(struct auth_server_connection *conn)
@@ -47,8 +48,7 @@
 }
 
 static void
-auth_server_request_check_retry(struct auth_request *request,
-				const unsigned char *data, size_t data_size)
+auth_server_request_check_retry(struct auth_request *request, const char *data)
 {
 	if (strcmp(request->mech, "PLAIN") == 0 &&
 	    request->plaintext_data == NULL && request->conn != NULL) {
@@ -56,9 +56,7 @@
 		if (request->next_conn != NULL) {
 			/* plaintext authentication - save the data so we can
 			   try it for the next */
-			request->plaintext_data = i_malloc(data_size);
-			memcpy(request->plaintext_data, data, data_size);
-			request->plaintext_data_size = data_size;
+			request->plaintext_data = i_strdup(data);
 
 			hash_insert(request->next_conn->requests,
 				    POINTER_CAST(request->id), request);
@@ -72,51 +70,28 @@
 static int auth_server_send_new_request(struct auth_server_connection *conn,
 					struct auth_request *request)
 {
-	struct auth_client_request_new auth_request;
-	buffer_t *buf;
-	size_t size;
+	string_t *str;
 	ssize_t ret;
 
-	memset(&auth_request, 0, sizeof(auth_request));
-	auth_request.type = AUTH_CLIENT_REQUEST_NEW;
-	auth_request.id = request->id;
-	auth_request.flags = request->flags;
-
-	if (request->local_ip.family == request->remote_ip.family)
-		auth_request.ip_family = request->local_ip.family;
-
 	t_push();
-	buf = buffer_create_dynamic(pool_datastack_create(), 256);
-	buffer_set_used_size(buf, sizeof(auth_request));
+	str = t_str_new(512);
 
-	if (auth_request.ip_family != 0) {
-		size = IPADDR_IS_V4(&request->local_ip) ? 4 :
-			sizeof(request->local_ip.ip);
-		buffer_append(buf, &request->local_ip.ip, size);
-		buffer_append(buf, &request->remote_ip.ip, size);
-	}
-
-	auth_request.mech_idx =
-		buffer_get_used_size(buf) - sizeof(auth_request);
-	buffer_append(buf, request->mech, strlen(request->mech)+1);
+	str_printfa(str, "AUTH\t%u\t%s\tproto=%s",
+		    request->id, request->mech, request->protocol);
+	if ((request->flags & AUTH_REQUEST_FLAG_SECURED) != 0)
+		str_append(str, "\tsecured");
+	if ((request->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0)
+		str_append(str, "\tvalid-client-cert");
 
-	auth_request.protocol_idx =
-		buffer_get_used_size(buf) - sizeof(auth_request);
-	buffer_append(buf, request->protocol, strlen(request->protocol)+1);
-
-	auth_request.initial_resp_idx =
-		buffer_get_used_size(buf) - sizeof(auth_request);
-	buffer_append(buf, request->initial_resp_data,
-		      request->initial_resp_size);
+	if (request->local_ip.family != 0)
+		str_printfa(str, "\tlip=%s", net_ip2addr(&request->local_ip));
+	if (request->remote_ip.family != 0)
+		str_printfa(str, "\trip=%s", net_ip2addr(&request->remote_ip));
+	if (request->initial_resp_base64 != NULL)
+		str_printfa(str, "\tresp=%s", request->initial_resp_base64);
+	str_append_c(str, '\n');
 
-	auth_request.data_size =
-		buffer_get_used_size(buf) - sizeof(auth_request);
-
-	memcpy(buffer_get_space_unsafe(buf, 0, sizeof(auth_request)),
-	       &auth_request, sizeof(auth_request));
-
-	ret = o_stream_send(conn->output, buffer_get_data(buf, NULL),
-			    buffer_get_used_size(buf));
+	ret = o_stream_send(conn->output, str_data(str), str_len(str));
 	t_pop();
 
 	if (ret < 0) {
@@ -126,63 +101,123 @@
 		return FALSE;
 	}
 
-	auth_server_request_check_retry(request, request->initial_resp_data,
-					request->initial_resp_size);
+	auth_server_request_check_retry(request, request->initial_resp_base64);
 	return TRUE;
 }
 
 static void auth_server_send_continue(struct auth_server_connection *conn,
 				      struct auth_request *request,
-				      const unsigned char *data, size_t size)
+				      const char *data_base64)
 {
-	struct auth_client_request_continue auth_request;
-        struct const_iovec iov[2];
+	struct const_iovec iov[3];
+	const char *prefix;
+
+	prefix = t_strdup_printf("CONT\t%u\t", request->id);
 
-	/* send continued request to auth */
-	auth_request.type = AUTH_CLIENT_REQUEST_CONTINUE;
-	auth_request.id = request->id;
-	auth_request.data_size = size;
+	iov[0].iov_base = prefix;
+	iov[0].iov_len = strlen(prefix);
+	iov[1].iov_base = data_base64;
+	iov[1].iov_len = strlen(data_base64);
+	iov[2].iov_base = "\n";
+	iov[2].iov_len = 1;
 
-	iov[0].iov_base = &auth_request;
-	iov[0].iov_len = sizeof(auth_request);
-	iov[1].iov_base = data;
-	iov[1].iov_len = size;
-
-	if (o_stream_sendv(conn->output, iov, 2) < 0) {
+	if (o_stream_sendv(conn->output, iov, 3) < 0) {
 		errno = conn->output->stream_errno;
 		i_warning("Error sending continue request to auth server: %m");
 		auth_server_connection_destroy(conn, TRUE);
 	}
 }
 
-void auth_server_request_handle_reply(struct auth_server_connection *conn,
-				      struct auth_client_request_reply *reply,
-				      const unsigned char *data)
+int auth_client_input_ok(struct auth_server_connection *conn, const char *args)
+{
+	const char *const *list, *const *args_list, *data_base64;
+	struct auth_request *request;
+	unsigned int id;
+
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL) {
+		i_error("BUG: Authentication server sent broken OK line");
+		return FALSE;
+	}
+
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+
+	request = hash_lookup(conn->requests, POINTER_CAST(id));
+	if (request == NULL) {
+		/* We've already destroyed the request */
+		return TRUE;
+	}
+
+	hash_remove(request->conn->requests, POINTER_CAST(id));
+	if (request->next_conn != NULL)
+		hash_remove(request->next_conn->requests, POINTER_CAST(id));
+	request->conn = conn;
+	request->next_conn = NULL;
+
+	data_base64 = NULL;
+	for (args_list = ++list; *list != NULL; list++) {
+		if (strncmp(*list, "resp=", 5) == 0) {
+			data_base64 = *list + 5;
+			break;
+		}
+	}
+
+	request->callback(request, 1, data_base64, args_list, request->context);
+	auth_client_request_free(request);
+	return TRUE;
+}
+
+int auth_client_input_cont(struct auth_server_connection *conn,
+			   const char *args)
+{
+	struct auth_request *request;
+	const char *data;
+	unsigned int id;
+
+	data = strchr(args, '\t');
+	if (data++ == NULL) {
+		i_error("BUG: Authentication server sent broken CONT line");
+		return FALSE;
+	}
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+	request = hash_lookup(conn->requests, POINTER_CAST(id));
+	if (request == NULL) {
+		/* We've already destroyed the request */
+		return TRUE;
+	}
+
+	if (request->retrying) {
+		auth_server_send_continue(conn, request,
+					  request->plaintext_data);
+	}
+	request->callback(request, 0, data, NULL, request->context);
+	return TRUE;
+}
+
+int auth_client_input_fail(struct auth_server_connection *conn,
+			   const char *args)
 {
 	struct auth_request *request;
         struct auth_server_connection *next;
+	const char *error;
+	unsigned int id;
 
-	request = hash_lookup(conn->requests, POINTER_CAST(reply->id));
+	error = strchr(args, '\t');
+	if (error != NULL)
+		error++;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+	request = hash_lookup(conn->requests, POINTER_CAST(id));
 	if (request == NULL) {
 		/* We've already destroyed the request */
-		return;
+		return TRUE;
 	}
 
-	switch (reply->result) {
-	case AUTH_CLIENT_RESULT_SUCCESS:
-		hash_remove(request->conn->requests, POINTER_CAST(request->id));
-		if (request->next_conn != NULL) {
-			hash_remove(request->next_conn->requests,
-				    POINTER_CAST(request->id));
-		}
-		request->conn = conn;
-		request->next_conn = NULL;
-		break;
-	case AUTH_CLIENT_RESULT_FAILURE:
-		hash_remove(conn->requests, POINTER_CAST(request->id));
-		if (!request->retrying)
-			break;
-
+	hash_remove(conn->requests, POINTER_CAST(request->id));
+	if (request->retrying) {
 		next = request->next_conn == NULL ? NULL :
 			get_next_plain_server(request->next_conn);
 
@@ -193,33 +228,22 @@
 		if (next == NULL) {
 			if (request->conn != NULL) {
 				/* the other one hasn't replied yet */
-				return;
+				return TRUE;
 			}
 			request->conn = conn;
-			break;
-		}
-
-		hash_insert(next->requests, POINTER_CAST(request->id), request);
-		request->next_conn = next;
+		} else {
+			hash_insert(next->requests, POINTER_CAST(request->id),
+				    request);
+			request->next_conn = next;
 
-		auth_server_send_new_request(next, request);
-		return;
-	case AUTH_CLIENT_RESULT_CONTINUE:
-		if (!request->retrying)
-			break;
-
-		auth_server_send_continue(conn, request,
-					  request->plaintext_data,
-					  request->plaintext_data_size);
-		return;
+			auth_server_send_new_request(next, request);
+			return TRUE;
+		}
 	}
 
-	request->callback(request, reply, data, request->context);
-
-	if (reply->result != AUTH_CLIENT_RESULT_CONTINUE) {
-		i_free(request->plaintext_data);
-		i_free(request);
-	}
+	request->callback(request, -1, error, NULL, request->context);
+	auth_client_request_free(request);
+	return TRUE;
 }
 
 static void request_hash_remove(struct auth_server_connection *conn,
@@ -227,7 +251,7 @@
 {
 	if (request->conn == conn) {
 		if (request->next_conn == NULL) {
-			request->callback(request, NULL, NULL,
+			request->callback(request, -1, NULL, NULL,
 					  request->context);
 			request->conn = NULL;
 		} else {
@@ -285,13 +309,9 @@
 	request->remote_ip = request_info->remote_ip;
 	request->id = ++client->request_id_counter;
 
-	if (request_info->initial_resp_size != 0) {
-		request->initial_resp_size = request_info->initial_resp_size;
-		request->initial_resp_data =
-			i_malloc(request_info->initial_resp_size);
-		memcpy(request->initial_resp_data,
-		       request_info->initial_resp_data,
-		       request_info->initial_resp_size);
+	if (request_info->initial_resp_base64 != NULL) {
+		request->initial_resp_base64 =
+			i_strdup(request_info->initial_resp_base64);
 	}
 	
 	if (request->id == 0) {
@@ -309,11 +329,19 @@
 }
 
 void auth_client_request_continue(struct auth_request *request,
-				  const unsigned char *data, size_t data_size)
+                                  const char *data_base64)
 {
-	auth_server_send_continue(request->conn, request, data, data_size);
+	auth_server_send_continue(request->conn, request, data_base64);
+	auth_server_request_check_retry(request, data_base64);
+}
 
-	auth_server_request_check_retry(request, data, data_size);
+static void auth_client_request_free(struct auth_request *request)
+{
+	i_free(request->initial_resp_base64);
+	i_free(request->plaintext_data);
+	i_free(request->mech);
+	i_free(request->protocol);
+	i_free(request);
 }
 
 void auth_client_request_abort(struct auth_request *request)
@@ -324,13 +352,8 @@
 	if (request->next_conn != NULL)
 		hash_remove(request->next_conn->requests, id);
 
-	request->callback(request, NULL, NULL, request->context);
-
-	i_free(request->initial_resp_data);
-	i_free(request->plaintext_data);
-	i_free(request->mech);
-	i_free(request->protocol);
-	i_free(request);
+	request->callback(request, -1, NULL, NULL, request->context);
+	auth_client_request_free(request);
 }
 
 unsigned int auth_client_request_get_id(struct auth_request *request)
--- a/src/lib-auth/auth-server-request.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/lib-auth/auth-server-request.h	Wed Oct 13 19:38:32 2004 +0300
@@ -1,9 +1,11 @@
 #ifndef __AUTH_SERVER_REQUEST_H
 #define __AUTH_SERVER_REQUEST_H
 
-void auth_server_request_handle_reply(struct auth_server_connection *conn,
-				      struct auth_client_request_reply *reply,
-				      const unsigned char *data);
+int auth_client_input_ok(struct auth_server_connection *conn, const char *args);
+int auth_client_input_cont(struct auth_server_connection *conn,
+			   const char *args);
+int auth_client_input_fail(struct auth_server_connection *conn,
+			   const char *args);
 
 void auth_server_requests_remove_all(struct auth_server_connection *conn);
 
--- a/src/login-common/sasl-server.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/login-common/sasl-server.c	Wed Oct 13 19:38:32 2004 +0300
@@ -12,16 +12,16 @@
 /* Used only for string sanitization while verbose_auth is set. */
 #define MAX_MECH_NAME 64
 
-static enum auth_client_request_new_flags
+static enum auth_request_flags
 client_get_auth_flags(struct client *client)
 {
-        enum auth_client_request_new_flags auth_flags = 0;
+        enum auth_request_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;
+		auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT;
+	if (client->secured)
+		auth_flags |= AUTH_REQUEST_FLAG_SECURED;
 	return auth_flags;
 }
 
@@ -35,43 +35,22 @@
 			      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)
+static void authenticate_callback(struct auth_request *request, int status,
+				  const char *data_base64,
+				  const char *const *args, void *context)
 {
 	struct client *client = context;
-	buffer_t *buf;
-	const char *user, *error;
+	const char *error;
 
 	if (!client->authenticating) {
 		/* client aborted */
-		i_assert(reply == NULL);
+		i_assert(status < 0);
 		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:
+	switch (status) {
+	case 0:
+		/* continue */
 		if (client->auth_request != NULL) {
 			i_assert(client->auth_request == request);
 		} else {
@@ -80,34 +59,30 @@
 			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();
+				      data_base64);
 		break;
-	case AUTH_CLIENT_RESULT_SUCCESS:
+	case 1:
 		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);
+		for (; *args != NULL; args++) {
+			if (strncmp(*args, "user=", 5) == 0) {
+				i_free(client->virtual_user);
+				client->virtual_user = i_strdup(*args + 5);
+			}
+		}
 
 		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:
+	case -1:
 		client->auth_request = NULL;
 
 		/* see if we have error message */
-		if (reply->data_size > 0 && data[reply->data_size-1] == '\0') {
+		if (data_base64 != NULL) {
 			error = t_strconcat("Authentication failed: ",
-					    (const char *)data, NULL);
+					    (const char *)data_base64, NULL);
 		} else {
 			error = NULL;
 		}
@@ -118,8 +93,7 @@
 
 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,
+			    const char *initial_resp_base64,
 			    sasl_server_callback_t *callback)
 {
 	struct auth_request_info info;
@@ -137,7 +111,8 @@
 		return;
 	}
 
-	if (!client->secured && mech->plaintext && disable_plaintext_auth) {
+	if (!client->secured && disable_plaintext_auth &&
+	    (mech->flags & MECH_SEC_PLAINTEXT) != 0) {
 		sasl_server_auth_cancel(client,
 					"Plaintext authentication disabled.");
 		return;
@@ -149,8 +124,7 @@
 	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;
+	info.initial_resp_base64 = initial_resp_base64;
 
 	client->auth_request =
 		auth_client_request_new(auth_client, NULL, &info,
--- a/src/login-common/sasl-server.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/login-common/sasl-server.h	Wed Oct 13 19:38:32 2004 +0300
@@ -14,8 +14,7 @@
 
 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,
+			    const char *initial_resp_base64,
 			    sasl_server_callback_t *callback);
 void sasl_server_auth_cancel(struct client *client, const char *reason);
 
--- a/src/master/auth-process.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/auth-process.c	Wed Oct 13 19:38:32 2004 +0300
@@ -7,6 +7,7 @@
 #include "network.h"
 #include "istream.h"
 #include "ostream.h"
+#include "str.h"
 #include "restrict-access.h"
 #include "restrict-process-size.h"
 #include "auth-process.h"
@@ -18,8 +19,8 @@
 #include <syslog.h>
 #include <sys/stat.h>
 
-#define MAX_INBUF_SIZE \
-	(sizeof(struct auth_master_reply) + AUTH_MASTER_MAX_REPLY_DATA_SIZE)
+#define MAX_INBUF_SIZE 8192
+#define MAX_OUTBUF_SIZE 65536
 
 struct auth_process_group {
 	struct auth_process_group *next;
@@ -41,8 +42,6 @@
 	struct istream *input;
 	struct ostream *output;
 
-	struct auth_master_reply auth_reply;
-
 	struct hash_table *requests;
 
 	unsigned int external:1;
@@ -56,64 +55,19 @@
 
 static void auth_process_destroy(struct auth_process *p);
 
-static int handle_reply(struct auth_process *process,
-			struct auth_master_reply *reply,
-			const unsigned char *data)
-{
-	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;
-	}
-
-	/* 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;
-	}
-
-	/* 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->chroot_idx >= reply->data_size)
-		reply->chroot_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;
-}
-
 void auth_process_request(struct auth_process *process, unsigned int login_pid,
 			  unsigned int login_id, void *context)
 {
-	struct auth_master_request req;
+	string_t *str;
 	ssize_t ret;
 
-	req.tag = ++auth_tag;
-	req.id = login_id;
-	req.client_pid = login_pid;
+	t_push();
+	str = t_str_new(256);
+	str_printfa(str, "REQUEST\t%u\t%u\t%u\n",
+		    ++auth_tag, login_pid, login_id);
 
-	ret = o_stream_send(process->output, &req, sizeof(req));
-	if ((size_t)ret != sizeof(req)) {
+	ret = o_stream_send(process->output, str_data(str), str_len(str));
+	if (ret != (ssize_t)str_len(str)) {
 		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
@@ -125,76 +79,147 @@
 				  "killing..", dec2str(process->pid));
 		}
 		auth_process_destroy(process);
-		return;
+	} else {
+		hash_insert(process->requests, POINTER_CAST(auth_tag), context);
+	}
+	t_pop();
+}
+
+static int
+auth_process_input_user(struct auth_process *process, const char *args)
+{
+	void *context;
+	const char *const *list;
+	unsigned int id;
+
+	/* <id> <userid> [..] */
+
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL || list[1] == NULL) {
+		i_error("BUG: Auth process %s sent corrupted USER line",
+			dec2str(process->pid));
+		return FALSE;
+	}
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+
+	context = hash_lookup(process->requests, POINTER_CAST(id));
+	if (context == NULL) {
+		i_error("BUG: Auth process %s sent unrequested reply with ID "
+			"%u", dec2str(process->pid), id);
+		return FALSE;
+	}
+
+	auth_master_callback(list[1], list + 2, context);
+	hash_remove(process->requests, POINTER_CAST(id));
+	return TRUE;
+}
+
+static int
+auth_process_input_notfound(struct auth_process *process, const char *args)
+{
+	void *context;
+	unsigned int id;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+	context = hash_lookup(process->requests, POINTER_CAST(id));
+	if (context == NULL) {
+		i_error("BUG: Auth process %s sent unrequested reply with ID "
+			"%u", dec2str(process->pid), id);
+		return FALSE;
 	}
 
-	hash_insert(process->requests, POINTER_CAST(req.tag), context);
+	auth_master_callback(NULL, NULL, context);
+	hash_remove(process->requests, POINTER_CAST(id));
+	return TRUE;
+}
+
+static int
+auth_process_input_spid(struct auth_process *process, const char *args)
+{
+	unsigned int pid;
+
+	if (process->initialized) {
+		i_error("BUG: Authentication server re-handshaking");
+		return FALSE;
+	}
+
+	pid = (unsigned int)strtoul(args, NULL, 10);
+	if (pid == 0) {
+		i_error("BUG: Authentication server said it's PID 0");
+		return FALSE;
+	}
+
+	process->pid = pid;
+	process->initialized = TRUE;
+	return TRUE;
+}
+
+static int
+auth_process_input_fail(struct auth_process *process, const char *args)
+{
+	void *context;
+ 	const char *error;
+	unsigned int id;
+
+	error = strchr(args, '\t');
+	if (error != NULL)
+		error++;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+	context = hash_lookup(process->requests, POINTER_CAST(id));
+	if (context == NULL) {
+		i_error("BUG: Auth process %s sent unrequested reply with ID "
+			"%u", dec2str(process->pid), id);
+		return FALSE;
+	}
+
+	auth_master_callback(NULL, NULL, context);
+	hash_remove(process->requests, POINTER_CAST(id));
+	return TRUE;
 }
 
 static void auth_process_input(void *context)
 {
-	struct auth_process *p = context;
-	const unsigned char *data;
-	size_t size;
+	struct auth_process *process = context;
+	const char *line;
+	int ret;
 
-	switch (i_stream_read(p->input)) {
+	switch (i_stream_read(process->input)) {
 	case 0:
 		return;
 	case -1:
 		/* disconnected */
-		auth_process_destroy(p);
+		auth_process_destroy(process);
 		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);
+			"bytes of data", dec2str(process->pid),
+			(int)MAX_INBUF_SIZE);
+		auth_process_destroy(process);
 		return;
 	}
 
-	if (!p->initialized) {
-		struct auth_master_handshake_reply rec;
-
-		data = i_stream_get_data(p->input, &size);
-		if (size < sizeof(rec))
-			return;
-
-		memcpy(&rec, data, sizeof(rec));
-		i_stream_skip(p->input, sizeof(rec));
-
-		if (rec.server_pid == 0) {
-			i_fatal("Auth process sent invalid initialization "
-				"notification");
-		}
-
-		p->pid = rec.server_pid;
-		p->initialized = TRUE;
-	}
+	while ((line = i_stream_next_line(process->input)) != NULL) {
+		t_push();
+		if (strncmp(line, "USER\t", 5) == 0)
+			ret = auth_process_input_user(process, line + 5);
+		else if (strncmp(line, "NOTFOUND\t", 9) == 0)
+			ret = auth_process_input_notfound(process, line + 9);
+		else if (strncmp(line, "FAIL\t", 5) == 0)
+			ret = auth_process_input_fail(process, line + 5);
+		else if (strncmp(line, "SPID\t", 5) == 0)
+			ret = auth_process_input_spid(process, line + 5);
+		else
+			ret = TRUE;
 
-	for (;;) {
-		if (!p->in_auth_reply) {
-			data = i_stream_get_data(p->input, &size);
-			if (size < sizeof(p->auth_reply))
-				break;
-
-			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 (size < p->auth_reply.data_size)
-			break;
-
-		/* reply is now read */
-		if (!handle_reply(p, &p->auth_reply, data)) {
-			auth_process_destroy(p);
+		if (!ret) {
+			auth_process_destroy(process);
 			break;
 		}
-
-		p->in_auth_reply = FALSE;
-		i_stream_skip(p->input, p->auth_reply.data_size);
+		t_pop();
 	}
 }
 
@@ -213,8 +238,7 @@
 	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_master_request)*100,
+	p->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
 					 FALSE);
 	p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL);
 
--- a/src/master/auth-process.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/auth-process.h	Wed Oct 13 19:38:32 2004 +0300
@@ -1,8 +1,8 @@
 #ifndef __AUTH_PROCESS_H
 #define __AUTH_PROCESS_H
 
-void auth_master_callback(struct auth_master_reply *reply,
-			  const unsigned char *data, void *context);
+void auth_master_callback(const char *user, const char *const *args,
+			  void *context);
 
 /* Find process for given id */
 struct auth_process *auth_process_find(unsigned int pid);
--- a/src/master/common.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/common.h	Wed Oct 13 19:38:32 2004 +0300
@@ -7,8 +7,6 @@
 #include "hash.h"
 #include "master-settings.h"
 
-#include "../auth/auth-master-interface.h"
-
 enum process_type {
 	PROCESS_TYPE_UNKNOWN,
 	PROCESS_TYPE_AUTH,
--- a/src/master/login-process.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/login-process.c	Wed Oct 13 19:38:32 2004 +0300
@@ -71,14 +71,14 @@
 	i_free(group);
 }
 
-void auth_master_callback(struct auth_master_reply *reply,
-			  const unsigned char *data, void *context)
+void auth_master_callback(const char *user, const char *const *args,
+			  void *context)
 {
 	struct login_auth_request *request = context;
 	struct master_login_reply master_reply;
 	ssize_t ret;
 
-	if (reply == NULL || !reply->success)
+	if (user == NULL)
 		master_reply.success = FALSE;
 	else {
 		struct login_group *group = request->process->group;
@@ -87,8 +87,7 @@
 		master_reply.success =
 			create_mail_process(group, request->fd,
 					    &request->local_ip,
-					    &request->remote_ip,
-					    reply, (const char *) data);
+					    &request->remote_ip, user, args);
 		t_pop();
 	}
 
--- a/src/master/mail-process.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/mail-process.c	Wed Oct 13 19:38:32 2004 +0300
@@ -312,15 +312,18 @@
 int create_mail_process(struct login_group *group, int socket,
 			const struct ip_addr *local_ip,
 			const struct ip_addr *remote_ip,
-			struct auth_master_reply *reply, const char *data)
+			const char *user, const char *const *args)
 {
 	struct settings *set = group->set;
 	const struct var_expand_table *var_expand_table;
-	const char *addr, *mail, *user, *chroot_dir, *home_dir, *full_home_dir;
+	const char *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
+	const char *system_user;
 	char title[1024];
 	struct log_io *log;
 	string_t *str;
 	pid_t pid;
+	uid_t uid;
+	gid_t gid;
 	int i, err, ret, log_fd;
 
 	// FIXME: per-group
@@ -329,14 +332,25 @@
 		return FALSE;
 	}
 
-	if (!validate_uid_gid(set, reply->uid, reply->gid,
-			      data + reply->virtual_user_idx))
-		return FALSE;
+	mail = home_dir = chroot_dir = system_user = "";
+	uid = gid = 0;
+	for (; *args != NULL; args++) {
+		if (strncmp(*args, "home=", 5) == 0)
+			home_dir = *args + 5;
+		else if (strncmp(*args, "mail=", 5) == 0)
+			mail = *args + 5;
+		else if (strncmp(*args, "chroot=", 7) == 0)
+			chroot_dir = *args + 7;
+		else if (strncmp(*args, "system_user=", 12) == 0)
+			system_user = *args + 12;
+		else if (strncmp(*args, "uid=", 4) == 0)
+			uid = (uid_t)strtoul(*args + 4, NULL, 10);
+		else if (strncmp(*args, "gid=", 4) == 0)
+			gid = (gid_t)strtoul(*args + 4, NULL, 10);
+	}
 
-	user = data + reply->virtual_user_idx;
-	mail = data + reply->mail_idx;
-	home_dir = data + reply->home_idx;
-	chroot_dir = data + reply->chroot_idx;
+	if (!validate_uid_gid(set, uid, gid, user))
+		return FALSE;
 
 	if (*chroot_dir == '\0' && set->mail_chroot != NULL)
 		chroot_dir = set->mail_chroot;
@@ -395,8 +409,7 @@
 
 	/* setup environment - set the most important environment first
 	   (paranoia about filling up environment without noticing) */
-	restrict_access_set_env(data + reply->system_user_idx,
-				reply->uid, reply->gid, chroot_dir,
+	restrict_access_set_env(system_user, uid, gid, chroot_dir,
 				set->first_valid_gid, set->last_valid_gid,
 				set->mail_extra_groups);
 
@@ -410,10 +423,10 @@
 		/* NOTE: if home directory is NFS-mounted, we might not
 		   have access to it as root. Change the effective UID
 		   temporarily to make it work. */
-		if (reply->uid != master_uid && seteuid(reply->uid) < 0)
-			i_fatal("seteuid(%s) failed: %m", dec2str(reply->uid));
+		if (uid != master_uid && seteuid(uid) < 0)
+			i_fatal("seteuid(%s) failed: %m", dec2str(uid));
 		ret = chdir(full_home_dir);
-		if (reply->uid != master_uid && seteuid(master_uid) < 0)
+		if (uid != master_uid && seteuid(master_uid) < 0)
 			i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
 
 		/* If user's home directory doesn't exist and we're not
@@ -421,7 +434,7 @@
 		   could be stored elsewhere. */
 		if (ret < 0 && (errno != ENOENT || *chroot_dir != '\0')) {
 			i_fatal("chdir(%s) failed with uid %s: %m",
-				full_home_dir, dec2str(reply->uid));
+				full_home_dir, dec2str(uid));
 		}
 	}
 	if (ret < 0) {
@@ -435,7 +448,7 @@
 
 	env_put("LOGGED_IN=1");
 	env_put(t_strconcat("HOME=", home_dir, NULL));
-	env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL));
+	env_put(t_strconcat("USER=", user, NULL));
 
 	addr = net_ip2addr(remote_ip);
 	env_put(t_strconcat("IP=", addr, NULL));
@@ -446,8 +459,7 @@
 		if (addr == NULL)
 			addr = "??";
 
-		i_snprintf(title, sizeof(title), "[%s %s]",
-			   data + reply->virtual_user_idx, addr);
+		i_snprintf(title, sizeof(title), "[%s %s]", user, addr);
 	}
 
 	/* make sure we don't leak syslog fd, but do it last so that
--- a/src/master/mail-process.h	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/master/mail-process.h	Wed Oct 13 19:38:32 2004 +0300
@@ -9,7 +9,7 @@
 int create_mail_process(struct login_group *group, int socket,
 			const struct ip_addr *local_ip,
 			const struct ip_addr *remote_ip,
-			struct auth_master_reply *reply, const char *data);
+			const char *user, const char *const *args);
 
 void mail_process_destroyed(pid_t pid);
 
--- a/src/pop3-login/client-authenticate.c	Wed Oct 13 15:32:54 2004 +0300
+++ b/src/pop3-login/client-authenticate.c	Wed Oct 13 19:38:32 2004 +0300
@@ -30,10 +30,10 @@
 		/* a) transport is secured
 		   b) auth mechanism isn't plaintext
 		   c) we allow insecure authentication
-		        - but don't advertise AUTH=PLAIN, as RFC 2595 requires
 		*/
-		if (mech[i].advertise &&
-		    (client->secured || !mech[i].plaintext)) {
+		if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
+		    (client->secured || disable_plaintext_auth ||
+		     (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
 			str_append_c(str, ' ');
 			str_append(str, mech[i].name);
 		}
@@ -50,9 +50,7 @@
 static void client_auth_input(void *context)
 {
 	struct pop3_client *client = context;
-	buffer_t *buf;
 	char *line;
-	size_t linelen, bufsize;
 
 	if (!client_read(client))
 		return;
@@ -68,25 +66,15 @@
 		return;
 	}
 
-	linelen = strlen(line);
-	buf = buffer_create_static_hard(pool_datastack_create(), linelen);
-
-	if (base64_decode(line, linelen, NULL, buf) < 0) {
-		/* failed */
-		sasl_server_auth_cancel(&client->common, "Invalid base64 data");
-	} else if (client->common.auth_request == NULL) {
+	if (client->common.auth_request == NULL) {
 		sasl_server_auth_cancel(&client->common,
 					"Don't send unrequested data");
 	} else {
-		auth_client_request_continue(client->common.auth_request,
-					     buf->data, buf->used);
+		auth_client_request_continue(client->common.auth_request, line);
 	}
 
 	/* clear sensitive data */
-	safe_memset(line, 0, linelen);
-
-	bufsize = buffer_get_used_size(buf);
-	safe_memset(buffer_free_without_data(buf), 0, bufsize);
+	safe_memset(line, 0, strlen(line));
 }
 
 static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
@@ -112,7 +100,8 @@
 		}
 
 		/* get back to normal client input. */
-		io_remove(client->io);
+		if (client->io != NULL)
+			io_remove(client->io);
 		client->io = io_add(client->common.fd, IO_READ,
 				    client_input, client);
 		break;
@@ -149,8 +138,6 @@
 {
 	const struct auth_mech_desc *mech;
 	const char *mech_name, *p;
-	string_t *buf;
-	size_t argslen;
 
 	if (*args == '\0') {
 		/* Old-style SASL discovery, used by MS Outlook */
@@ -158,9 +145,10 @@
 		client_send_line(client, "+OK");
 		mech = auth_client_get_available_mechs(auth_client, &count);
 		for (i = 0; i < count; i++) {
-			if (mech[i].advertise) {
+			if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
+			    (client->secured || disable_plaintext_auth ||
+			     (mech[i].flags & MECH_SEC_PLAINTEXT) == 0))
 		 		client_send_line(client, mech[i].name);
-			}
 		}
  		client_send_line(client, ".");
  		return TRUE;
@@ -176,18 +164,9 @@
 		args = p+1;
 	}
 
-	argslen = strlen(args);
-	buf = buffer_create_static_hard(pool_datastack_create(), argslen);
-
-	if (base64_decode(args, argslen, NULL, buf) < 0) {
-		/* failed */
-		client_send_line(client, "-ERR Invalid base64 data.");
-		return TRUE;
-	}
-
 	client_ref(client);
 	sasl_server_auth_begin(&client->common, "POP3", mech_name,
-			       buf->data, buf->used, sasl_callback);
+			       args, sasl_callback);
 	if (!client->common.authenticating)
 		return TRUE;
 
@@ -220,7 +199,7 @@
 
 int cmd_pass(struct pop3_client *client, const char *args)
 {
-	string_t *plain_login;
+	string_t *plain_login, *base64;
 
 	if (client->last_user == NULL) {
 		client_send_line(client, "-ERR No username given.");
@@ -234,10 +213,13 @@
 	str_append_c(plain_login, '\0');
 	str_append(plain_login, args);
 
+	base64 = buffer_create_dynamic(pool_datastack_create(),
+        			MAX_BASE64_ENCODED_SIZE(plain_login->used));
+	base64_encode(plain_login->data, plain_login->used, base64);
+
 	client_ref(client);
 	sasl_server_auth_begin(&client->common, "POP3", "PLAIN",
-			       plain_login->data, plain_login->used,
-			       sasl_callback);
+			       str_c(base64), sasl_callback);
 	if (!client->common.authenticating)
 		return TRUE;
 
@@ -251,7 +233,7 @@
 
 int cmd_apop(struct pop3_client *client, const char *args)
 {
-	buffer_t *apop_data;
+	buffer_t *apop_data, *base64;
 	const char *p;
 
 	if (client->apop_challenge == NULL) {
@@ -291,9 +273,13 @@
 		return TRUE;
 	}
 
+	base64 = buffer_create_dynamic(pool_datastack_create(),
+        			MAX_BASE64_ENCODED_SIZE(apop_data->used));
+	base64_encode(apop_data->data, apop_data->used, base64);
+
 	client_ref(client);
 	sasl_server_auth_begin(&client->common, "POP3", "APOP",
-			       apop_data->data, apop_data->used, sasl_callback);
+			       str_c(base64), sasl_callback);
 	if (!client->common.authenticating)
 		return TRUE;