? src/auth/mech-gssapi.c
? src/login-common/integrity-proxy-gssapi.c
? src/login-common/integrity-proxy.c
? src/login-common/integrity-proxy.h
Index: configure.in
===================================================================
RCS file: /home/cvs/dovecot/configure.in,v
retrieving revision 1.172
diff -u -r1.172 configure.in
--- configure.in	21 Jun 2004 14:51:04 -0000	1.172
+++ configure.in	12 Jul 2004 14:13:55 -0000
@@ -88,6 +88,15 @@
 	fi,
 	want_pam=yes)
 
+AC_ARG_WITH(gssapi_krb5,
+[  --with-gssapi-krb5      Build with GSSAPI Kerberos 5 support (default)],
+	if test x$withval = xno; then
+		want_gssapi_krb5=no
+	else
+		want_gssapi_krb5=yes
+	fi,
+	want_gssapi_krb5=yes)
+
 AC_ARG_WITH(checkpassword,
 [  --with-checkpassword    Build with checkpassword support (default)],
 	if test x$withval = xno; then
@@ -994,6 +1003,17 @@
 	])
 fi
 
+if test $want_gssapi_krb5 = yes; then
+	AC_CHECK_LIB(gssapi_krb5, gss_acquire_cred, [
+		AC_CHECK_HEADER(gssapi/gssapi.h, [
+			AC_DEFINE(USE_GSSAPI,,
+				  Define if you want to use GSSAPI)
+			AUTH_LIBS="$AUTH_LIBS -lgssapi_krb5"
+			have_gssapi_krb5=yes
+		])
+	])
+fi
+
 if test $want_checkpassword = yes; then
         AC_DEFINE(USERDB_CHECKPASSWORD,, Build with checkpassword userdb support)
         AC_DEFINE(PASSDB_CHECKPASSWORD,, Build with checkpassword passdb support)
@@ -1264,6 +1284,7 @@
 echo "Install prefix ...................... : $prefix"
 echo "File offsets ........................ : ${offt_bits}bit"
 echo "Building with SSL support ........... : $have_ssl"
+echo "Building with GSSAPI (Kerberos 5) ... : $have_gssapi_krb5"
 echo "Building with IPv6 support .......... : $want_ipv6"
 echo "Building with pop3 server ........... : $want_pop3d"
 echo "Building with user database modules . :$userdb"
Index: src/auth/Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.28
diff -u -r1.28 Makefile.am
--- src/auth/Makefile.am	2 Jul 2004 22:03:37 -0000	1.28
+++ src/auth/Makefile.am	12 Jul 2004 14:13:55 -0000
@@ -31,6 +31,7 @@
 	mech-plain.c \
 	mech-cram-md5.c \
 	mech-digest-md5.c \
+	mech-gssapi.c \
 	mech-apop.c \
 	mycrypt.c \
 	passdb.c \
Index: src/auth/auth-client-interface.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/auth-client-interface.h,v
retrieving revision 1.7
diff -u -r1.7 auth-client-interface.h
--- src/auth/auth-client-interface.h	2 Jul 2004 22:03:37 -0000	1.7
+++ src/auth/auth-client-interface.h	12 Jul 2004 14:13:55 -0000
@@ -81,6 +81,8 @@
 	/* variable width data, indexes into data[].
 	   Ignore if it points outside data_size. */
 	uint32_t username_idx; /* NUL-terminated */
+	uint32_t integrity_mech_name_idx; /* Mechanism integrity data */
+	uint32_t integrity_mech_data_idx; /* Mechanism integrity data */
 	uint32_t reply_idx; /* last, non-NUL terminated */
 
 	uint32_t data_size;
Index: src/auth/mech.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/mech.c,v
retrieving revision 1.27
diff -u -r1.27 mech.c
--- src/auth/mech.c	2 Jul 2004 22:03:37 -0000	1.27
+++ src/auth/mech.c	12 Jul 2004 14:13:55 -0000
@@ -31,6 +31,10 @@
 
 	list = i_new(struct mech_module_list, 1);
 	list->module = *module;
+	if (module->auth_factory_new)
+		list->factory = module->auth_factory_new ();
+	else
+		list->factory = NULL;
 
 	list->next = mech_modules;
 	mech_modules = list;
@@ -44,6 +48,8 @@
 		if (strcmp((*pos)->module.mech_name, module->mech_name) == 0) {
 			list = *pos;
 			*pos = (*pos)->next;
+			if (list->factory)
+				list->module.auth_factory_free (list->factory);
 			i_free(list);
 			break;
 		}
@@ -62,15 +68,21 @@
 	return str;
 }
 
-static struct mech_module *mech_module_find(const char *name)
+static void mech_module_find(const char *name,
+			     struct mech_module **module,
+			     struct auth_request_factory **factory)
 {
 	struct mech_module_list *list;
 
 	for (list = mech_modules; list != NULL; list = list->next) {
-		if (strcasecmp(list->module.mech_name, name) == 0)
-			return &list->module;
+		if (strcasecmp(list->module.mech_name, name) == 0) {
+			*module = &list->module;
+			*factory = list->factory;
+			return;
+		}
 	}
-	return NULL;
+	*module = NULL;
+	*factory = NULL;
 }
 
 void mech_request_new(struct auth_client_connection *conn,
@@ -78,7 +90,8 @@
 		      const unsigned char *data,
 		      mech_callback_t *callback)
 {
-        struct mech_module *mech;
+	struct mech_module *mech;
+	struct auth_request_factory *factory;
 	struct auth_request *auth_request;
 	size_t ip_size = 1;
 
@@ -102,7 +115,8 @@
 		return;
 	}
 
-	mech = mech_module_find((const char *)data + request->mech_idx);
+	mech_module_find((const char *)data + request->mech_idx,
+			 &mech, &factory);
 	if (mech == NULL) {
 		/* unsupported mechanism */
 		i_error("BUG: Auth client %u requested unsupported "
@@ -113,6 +127,9 @@
 		return;
 	}
 
+	if (factory)
+		auth_request = factory->auth_new (factory);
+	else
 #ifdef USE_CYRUS_SASL2
 	if (set_use_cyrus_sasl)
 		auth_request = mech_cyrus_sasl_new(conn, request, callback);
@@ -198,9 +215,11 @@
 	reply->reply_idx = (size_t)-1;
 }
 
-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_success_extended(struct auth_client_request_reply *reply,
+				 struct auth_request *auth_request,
+				 const char *mech_name,
+				 const void *data, size_t data_size,
+				 const void *context, size_t ctx_len)
 {
 	buffer_t *buf;
 
@@ -209,6 +228,16 @@
 	reply->username_idx = 0;
 	buffer_append(buf, auth_request->user, strlen(auth_request->user)+1);
 
+	if (mech_name && ctx_len > 0) {
+		reply->integrity_mech_name_idx = buffer_get_used_size(buf);
+		buffer_append(buf, mech_name, strlen(mech_name)+1);
+		reply->integrity_mech_data_idx = buffer_get_used_size(buf);
+		buffer_append(buf, context, ctx_len);
+	} else {
+		reply->integrity_mech_name_idx = 0;
+		reply->integrity_mech_data_idx = 0;
+	}
+
 	if (data_size == 0)
 		reply->reply_idx = (size_t)-1;
 	else {
@@ -221,8 +250,21 @@
 	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)
+void *mech_auth_success(struct auth_client_request_reply *reply,
+			struct auth_request *auth_request,
+			const void *data, size_t data_size)
+{
+	return mech_auth_success_extended(reply, auth_request,
+					  NULL,
+					  data, data_size,
+					  NULL, 0);
+}
+
+void mech_auth_finish_extended(struct auth_request *auth_request,
+			       const char *mech_name,
+			       const void *data, size_t data_size,
+			       const void *context, size_t ctx_size,
+			       int success)
 {
 	struct auth_client_request_reply reply;
 	void *reply_data;
@@ -239,7 +281,9 @@
 	reply.id = auth_request->id;
 	reply.result = AUTH_CLIENT_RESULT_SUCCESS;
 
-	reply_data = mech_auth_success(&reply, auth_request, data, data_size);
+	reply_data = mech_auth_success_extended(&reply, auth_request, mech_name,
+						data, data_size, context,
+						ctx_size);
 	auth_request->callback(&reply, reply_data, auth_request->conn);
 
 	if (AUTH_MASTER_IS_DUMMY(auth_request->conn->master)) {
@@ -249,6 +293,14 @@
 	}
 }
 
+void mech_auth_finish(struct auth_request *auth_request,
+		      const void *data, size_t data_size, int success)
+{
+	mech_auth_finish_extended(auth_request, NULL,
+				  data, data_size, NULL, 0,
+				  success);
+}
+
 int mech_is_valid_username(const char *username)
 {
 	const unsigned char *p;
@@ -387,6 +439,9 @@
 extern struct mech_module mech_apop;
 extern struct mech_module mech_cram_md5;
 extern struct mech_module mech_digest_md5;
+#ifdef USE_GSSAPI
+extern struct mech_module mech_gssapi;
+#endif
 extern struct mech_module mech_anonymous;
 
 void mech_init(void)
@@ -418,6 +473,10 @@
 			mech_register_module(&mech_cram_md5);
 		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
 			mech_register_module(&mech_digest_md5);
+#ifdef USE_GSSAPI
+		else if (strcasecmp(*mechanisms, "GSSAPI") == 0)
+			mech_register_module(&mech_gssapi);
+#endif
 		else if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) {
 			if (anonymous_username == NULL) {
 				i_fatal("ANONYMOUS listed in mechanisms, "
@@ -477,5 +536,8 @@
 	mech_unregister_module(&mech_apop);
 	mech_unregister_module(&mech_cram_md5);
 	mech_unregister_module(&mech_digest_md5);
+#ifdef USE_GSSAPI
+	mech_unregister_module(&mech_gssapi);
+#endif
 	mech_unregister_module(&mech_anonymous);
 }
Index: src/auth/mech.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/mech.h,v
retrieving revision 1.19
diff -u -r1.19 mech.h
--- src/auth/mech.h	31 May 2004 20:10:02 -0000	1.19
+++ src/auth/mech.h	12 Jul 2004 14:13:55 -0000
@@ -15,6 +15,7 @@
 
 	pool_t pool;
 	char *user;
+	char *authenticating_mech;
 
 	struct auth_client_connection *conn;
 	unsigned int id;
@@ -35,6 +36,10 @@
 	/* ... mechanism specific data ... */
 };
 
+struct auth_request_factory {
+	struct auth_request *(*auth_new)(struct auth_request_factory *factory);
+};
+
 struct mech_module {
 	const char *mech_name;
 
@@ -44,12 +49,15 @@
 	unsigned int passdb_need_credentials:1;
 
 	struct auth_request *(*auth_new)(void);
+	struct auth_request_factory *(*auth_factory_new)(void);
+	void (*auth_factory_free)(struct auth_request_factory *auth_factory);
 };
 
 struct mech_module_list {
 	struct mech_module_list *next;
 
 	struct mech_module module;
+	struct auth_request_factory *factory;
 };
 
 extern struct mech_module_list *mech_modules;
@@ -78,8 +86,19 @@
 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_success_extended(struct auth_client_request_reply *reply,
+				 struct auth_request *auth_request,
+				 const char *mech_name,
+				 const void *data, size_t data_size,
+				 const void *context, size_t ctx_len);
 void mech_auth_finish(struct auth_request *auth_request,
 		      const void *data, size_t data_size, int success);
+void mech_auth_finish_extended(struct auth_request *auth_request,
+			       const char *mech_name,
+			       const void *data, size_t data_size,
+			       const void *context, size_t ctx_size,
+			       int success);
+
 
 int mech_is_valid_username(const char *username);
 
Index: src/imap-login/client-authenticate.c
===================================================================
RCS file: /home/cvs/dovecot/src/imap-login/client-authenticate.c,v
retrieving revision 1.22
diff -u -r1.22 client-authenticate.c
--- src/imap-login/client-authenticate.c	6 Jul 2004 05:55:46 -0000	1.22
+++ src/imap-login/client-authenticate.c	12 Jul 2004 14:13:56 -0000
@@ -48,6 +48,7 @@
 	if (client->common.auth_request != NULL) {
 		auth_client_request_abort(client->common.auth_request);
 		client->common.auth_request = NULL;
+		return;
 	}
 
 	client_send_tagline(client, msg != NULL ?
@@ -221,8 +222,8 @@
 		client_send_auth_data(client, data, reply->data_size);
 		break;
 	default:
-		/* success, we should be able to log in. if we fail, just
-		   disconnect the client. */
+		/* success, we should be able to log in. If we fail,
+		   just disconnect the client. */
                 client->authenticating = FALSE;
 		client_send_tagline(client, "OK Logged in.");
 		client_unref(client);
Index: src/imap-login/client.c
===================================================================
RCS file: /home/cvs/dovecot/src/imap-login/client.c,v
retrieving revision 1.26
diff -u -r1.26 client.c
--- src/imap-login/client.c	31 May 2004 18:04:47 -0000	1.26
+++ src/imap-login/client.c	12 Jul 2004 14:13:56 -0000
@@ -16,7 +16,7 @@
 #include "ssl-proxy.h"
 
 /* max. size of one parameter in line */
-#define MAX_INBUF_SIZE 512
+#define MAX_INBUF_SIZE 1024
 
 #define MAX_OUTBUF_SIZE 1024
 
Index: src/lib/mempool.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib/mempool.h,v
retrieving revision 1.15
diff -u -r1.15 mempool.h
--- src/lib/mempool.h	6 Jan 2004 06:09:26 -0000	1.15
+++ src/lib/mempool.h	12 Jul 2004 14:13:56 -0000
@@ -2,6 +2,7 @@
 #define __MEMPOOL_H
 
 #include "macros.h"
+#include <stddef.h>
 
 /* Memory allocated and reallocated (the new data in it) in pools is always
    zeroed, it will cost only a few CPU cycles and may well save some debug
Index: src/login-common/Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/login-common/Makefile.am,v
retrieving revision 1.5
diff -u -r1.5 Makefile.am
--- src/login-common/Makefile.am	22 Aug 2003 02:42:13 -0000	1.5
+++ src/login-common/Makefile.am	12 Jul 2004 14:13:56 -0000
@@ -8,6 +8,8 @@
 
 liblogin_common_a_SOURCES = \
 	auth-common.c \
+	integrity-proxy.c \
+	integrity-proxy-gssapi.c \
 	main.c \
 	master.c \
 	ssl-proxy.c \
@@ -18,5 +20,6 @@
 	auth-common.h \
 	client-common.h \
 	common.h \
+	integrity-proxy.h \
 	master.h \
 	ssl-proxy.h
Index: src/login-common/auth-common.c
===================================================================
RCS file: /home/cvs/dovecot/src/login-common/auth-common.c,v
retrieving revision 1.5
diff -u -r1.5 auth-common.c
--- src/login-common/auth-common.c	22 Aug 2003 02:42:13 -0000	1.5
+++ src/login-common/auth-common.c	12 Jul 2004 14:13:56 -0000
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "ioloop.h"
+#include "str.h"
 #include "client-common.h"
 #include "auth-client.h"
 #include "auth-common.h"
@@ -20,6 +21,56 @@
 	return t_strndup(data + idx, stop);
 }
 
+static int setup_integrity_mech(struct client *client,
+				struct auth_client_request_reply *reply,
+				const unsigned char *data,
+				const char **error_r)
+{
+	const char *mech;
+	size_t mech_data_len;
+
+	/* Sanity checks on the indices */
+	if (!(reply->integrity_mech_name_idx > reply->username_idx
+	      && reply->integrity_mech_data_idx > reply->integrity_mech_name_idx
+	      && reply->reply_idx > reply->integrity_mech_data_idx
+	      && reply->data_size > reply->integrity_mech_data_idx))
+		return 0;
+	
+	i_info("Setting up integrity protection");
+
+	mech = t_strndup(data + reply->integrity_mech_name_idx,
+			 reply->integrity_mech_data_idx - reply->integrity_mech_name_idx);
+	if (!mech) {
+		*error_r = "Couldn't determine integrity proxy.";
+		i_warning("%s", *error_r);
+		return -1;
+	}
+
+	if (reply->reply_idx == (size_t)-1)
+		mech_data_len = reply->data_size - reply->integrity_mech_data_idx;
+	else
+		mech_data_len = reply->reply_idx - reply->integrity_mech_data_idx;
+
+	if (client->io != NULL) {
+		io_remove(client->io);
+		client->io = NULL;
+	}
+
+	client->integrity_proxy = integrity_proxy_new(mech,
+						      data + reply->integrity_mech_data_idx,
+						      mech_data_len,
+						      client->fd);
+	if (!client->integrity_proxy) {
+		*error_r = "Integrity proxy initialization failed.";
+		i_warning("%s", *error_r);
+		return -1;
+	}
+
+	client->fd = client->integrity_proxy->fd;
+
+	return 0;
+}
+
 int auth_callback(struct auth_request *request,
 		  struct auth_client_request_reply *reply,
 		  const unsigned char *data, struct client *client,
@@ -55,6 +106,11 @@
 		i_free(client->virtual_user);
 		client->virtual_user = i_strdup(user);
 
+		if (setup_integrity_mech(client, reply,
+					 data,
+					 error_r) < 0)
+			return -1;
+
 		master_request_login(client, master_callback,
 			auth_client_request_get_server_pid(request),
 			auth_client_request_get_id(request));
@@ -64,6 +120,7 @@
 			io_remove(client->io);
 			client->io = NULL;
 		}
+
 		return 1;
 
 	case AUTH_CLIENT_RESULT_FAILURE:
Index: src/login-common/client-common.h
===================================================================
RCS file: /home/cvs/dovecot/src/login-common/client-common.h,v
retrieving revision 1.6
diff -u -r1.6 client-common.h
--- src/login-common/client-common.h	31 May 2004 18:04:47 -0000	1.6
+++ src/login-common/client-common.h	12 Jul 2004 14:13:56 -0000
@@ -3,12 +3,15 @@
 
 #include "network.h"
 #include "master.h"
+#include "integrity-proxy.h"
 
 struct client {
 	struct ip_addr local_ip;
 	struct ip_addr ip;
 	struct ssl_proxy *proxy;
 
+	struct integrity_proxy *integrity_proxy;
+
 	int fd;
 	struct io *io;
 
Index: src/login-common/main.c
===================================================================
RCS file: /home/cvs/dovecot/src/login-common/main.c,v
retrieving revision 1.18
diff -u -r1.18 main.c
--- src/login-common/main.c	23 Jun 2004 17:48:35 -0000	1.18
+++ src/login-common/main.c	12 Jul 2004 14:13:56 -0000
@@ -209,6 +209,7 @@
 	if (io_listen != NULL) io_remove(io_listen);
 	if (io_ssl_listen != NULL) io_remove(io_ssl_listen);
 
+	integrity_proxy_deinit();
 	ssl_proxy_deinit();
 
 	auth_client_free(auth_client);
@@ -253,6 +254,8 @@
 		master_fd = master_connect(group_name);
 	}
 
+	integrity_proxy_init();
+
 	name = strrchr(argv[0], '/');
 	drop_privileges();
 
Index: src/master/login-process.c
===================================================================
RCS file: /home/cvs/dovecot/src/master/login-process.c,v
retrieving revision 1.56
diff -u -r1.56 login-process.c
--- src/master/login-process.c	16 Jun 2004 02:04:02 -0000	1.56
+++ src/master/login-process.c	12 Jul 2004 14:13:57 -0000
@@ -421,6 +421,8 @@
 					set->login_max_logging_users));
 	}
 
+	env_put(t_strconcat("MECHANISMS=", set->login_integrity_mechanisms, NULL));
+
 	env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid)));
 }
 
Index: src/master/master-settings.c
===================================================================
RCS file: /home/cvs/dovecot/src/master/master-settings.c,v
retrieving revision 1.58
diff -u -r1.58 master-settings.c
--- src/master/master-settings.c	10 Jul 2004 17:24:09 -0000	1.58
+++ src/master/master-settings.c	12 Jul 2004 14:13:57 -0000
@@ -227,6 +227,7 @@
 	MEMBER(login_processes_count) 3,
 	MEMBER(login_max_processes_count) 128,
 	MEMBER(login_max_logging_users) 256,
+	MEMBER(login_integrity_mechanisms) "gssapi",
 
 	/* mail */
 	MEMBER(valid_chroot_dirs) NULL,
Index: src/master/master-settings.h
===================================================================
RCS file: /home/cvs/dovecot/src/master/master-settings.h,v
retrieving revision 1.38
diff -u -r1.38 master-settings.h
--- src/master/master-settings.h	10 Jul 2004 17:24:09 -0000	1.38
+++ src/master/master-settings.h	12 Jul 2004 14:13:57 -0000
@@ -45,6 +45,7 @@
 	unsigned int login_processes_count;
 	unsigned int login_max_processes_count;
 	unsigned int login_max_logging_users;
+	const char *login_integrity_mechanisms;
 
 	/* mail */
 	const char *valid_chroot_dirs;
--- /dev/null	2004-06-24 13:58:40.000000000 -0400
+++ src/auth/mech-gssapi.c	2004-07-11 19:57:29.000000000 -0400
@@ -0,0 +1,404 @@
+/* Copyright (C) 2004 Colin Walters */
+
+/* GSSAPI SASL authentication, see RFC-2743 */
+
+#include "common.h"
+#include "buffer.h"
+#include "hex-binary.h"
+#include "str.h"
+#include "mech.h"
+#include "hostpid.h"
+
+#ifdef USE_GSSAPI
+
+#include <stdlib.h>
+#include <gssapi/gssapi.h>
+
+#if 0
+static const char *hexstring(const unsigned char *data, size_t len)
+{
+	string_t *str;
+	size_t i;
+
+	str = str_new(default_pool, len*2);
+	for (i = 0; i < len; i++) {
+		str_printfa(str, "%02X", data[i]);
+	}
+	return str_data(str);
+}
+#endif
+
+enum gssapi_sasl_state {
+	SASL_GSSAPI_STATE_CTX_LOOP,
+	SASL_GSSAPI_STATE_FINAL_TOKEN,
+	SASL_GSSAPI_STATE_FINAL_NEGOTIATE,
+	SASL_GSSAPI_STATE_COMPLETE
+};
+
+struct gssapi_auth_request {
+	struct auth_request auth_request;
+
+	pool_t pool;
+
+	enum gssapi_sasl_state state;
+	gss_name_t client;
+	gss_ctx_id_t context;
+
+	struct gssapi_auth_request_factory *factory;
+};
+
+struct gssapi_auth_request_factory {
+	struct auth_request_factory auth_factory;
+
+	gss_cred_id_t cred;
+};
+
+static void log_gssapi_error(gss_ctx_id_t context,
+			     const char *func_name,
+			     OM_uint32 maj_stat,
+			     OM_uint32 min_stat)
+{
+	OM_uint32 tmp_min_stat, tmp_maj_stat;
+	gss_buffer_desc gssbuf;
+	string_t *logbuf;
+	OM_uint32 msg_context;
+
+	logbuf = str_new(default_pool, 128);	
+	
+	msg_context = 0;
+	/* The GSSAPI error api is way overkill. */ 
+	tmp_maj_stat = gss_display_status(&tmp_min_stat,
+					  maj_stat,
+					  GSS_C_GSS_CODE,
+					  GSS_C_NULL_OID,
+					  &msg_context,
+					  &gssbuf);
+	if (GSS_ERROR(tmp_maj_stat)) {
+		str_append(logbuf, "UNKNOWN (");
+	} else {
+		str_append_n(logbuf, gssbuf.value, gssbuf.length);
+		str_append(logbuf, " (");
+		maj_stat = gss_release_buffer(&min_stat, &gssbuf);
+	}
+		
+	msg_context = 0;
+	tmp_maj_stat = gss_display_status(&tmp_min_stat,
+					  min_stat,
+					  GSS_C_MECH_CODE,
+					  GSS_C_NULL_OID,
+					  &msg_context,
+					  &gssbuf);
+	if (GSS_ERROR(tmp_maj_stat)) {
+		str_append(logbuf, "UNKNOWN)");
+	} else {
+		str_append_n(logbuf, gssbuf.value, gssbuf.length);
+		maj_stat = gss_release_buffer(&min_stat, &gssbuf);
+		str_append_c(logbuf, ')');
+	}
+	
+	i_warning("%s failed: %s", func_name, str_data(logbuf));
+	str_free(logbuf);
+}
+
+static int
+mech_gssapi_auth_continue(struct auth_request *auth_request,
+			  const unsigned char *data, size_t data_size,
+			  mech_callback_t *callback)
+{
+	struct gssapi_auth_request *auth =
+		(struct gssapi_auth_request *)auth_request;
+	struct auth_client_request_reply reply;
+	const char *reply_data = NULL;
+	OM_uint32 min_stat, maj_stat;
+	gss_buffer_desc inbuf, outbuf;
+
+	/* initialize reply */
+	mech_init_auth_client_reply(&reply);
+	reply.id = auth_request->id;
+
+	auth_request->callback = callback;
+
+	switch (auth->state) {
+	case SASL_GSSAPI_STATE_CTX_LOOP:
+	{
+		inbuf.value = (char*) data;
+		inbuf.length = data_size;
+		i_info("gssapi: in ctx loop");
+		maj_stat = gss_accept_sec_context(&min_stat,
+						  &(auth->context),
+						  auth->factory->cred,
+						  &inbuf,
+						  GSS_C_NO_CHANNEL_BINDINGS,
+						  &auth->client,
+						  NULL,
+						  &outbuf, NULL, NULL, NULL);
+		if (GSS_ERROR(maj_stat)) {
+			log_gssapi_error(auth->context, "gss_accept_sec_context",
+					 maj_stat, min_stat);
+			reply_data = "Couldn't accept initial context";
+			reply.data_size = strlen(reply_data)+1;
+			reply.result = AUTH_CLIENT_RESULT_FAILURE;
+			callback(&reply, reply_data, auth_request->conn);
+			return FALSE;
+		} else {
+			if (maj_stat == GSS_S_COMPLETE) {
+				auth->state = SASL_GSSAPI_STATE_FINAL_TOKEN;
+				i_info("gssapi: initial context accept completed");
+			} else
+				i_info("gssapi: continuing initial context acceptance");
+				
+			/* If not complete, then keep looping */
+			reply.data_size = outbuf.length;
+			reply_data = outbuf.value;
+			reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+			callback(&reply, reply_data, auth_request->conn);
+			maj_stat = gss_release_buffer(&min_stat, &outbuf);
+			if (maj_stat != GSS_S_COMPLETE)
+				return FALSE;
+			return TRUE;
+		}
+		break;
+	}
+	case SASL_GSSAPI_STATE_FINAL_TOKEN:
+	{
+		char ret[4];
+		ret[0] = 0x07;
+		ret[1] = 0xFF;
+		ret[2] = 0xFF;
+		ret[3] = 0xFF;
+		inbuf.length = 4;
+		inbuf.value = ret;
+		i_info("gssapi: wrapping final token");
+		maj_stat = gss_wrap(&min_stat,
+				    auth->context, 0,
+				    GSS_C_QOP_DEFAULT,
+				    &inbuf, NULL, &outbuf);
+		if (GSS_ERROR(maj_stat)) {
+			log_gssapi_error(auth->context, "gss_wrap",
+					 maj_stat, min_stat);
+			reply_data = "Couldn't complete layer negotiation: gss_wrap failure";
+			reply.data_size = strlen(reply_data)+1;
+			reply.result = AUTH_CLIENT_RESULT_FAILURE;
+			callback(&reply, reply_data, auth_request->conn);
+			return FALSE;
+		} else {
+			auth->state = SASL_GSSAPI_STATE_FINAL_NEGOTIATE;
+			reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+			reply.data_size = outbuf.length;
+			callback(&reply, outbuf.value, auth_request->conn);
+			maj_stat = gss_release_buffer(&min_stat, &outbuf);
+			return TRUE;
+		}
+	}
+	break;
+	case SASL_GSSAPI_STATE_FINAL_NEGOTIATE:
+		inbuf.value = (char*) data;
+		inbuf.length = data_size;
+		i_info("gssapi: completing final negotiation");
+		maj_stat = gss_unwrap(&min_stat,
+				      auth->context, 
+				      &inbuf, &outbuf, NULL, NULL);
+		if (GSS_ERROR(maj_stat)) {
+			log_gssapi_error(auth->context, "gss_unwrap",
+					 maj_stat, min_stat);
+			reply_data = "Couldn't complete final negotiation: gss_unwrap failure";
+			reply.data_size = strlen(reply_data)+1;
+			reply.result = AUTH_CLIENT_RESULT_FAILURE;
+			callback(&reply, reply_data, auth_request->conn);
+			return FALSE;
+		} else if (outbuf.length <= 4) {
+			reply_data = "Invalid response length";
+			reply.data_size = strlen(reply_data)+1;
+			reply.result = AUTH_CLIENT_RESULT_FAILURE;
+			callback(&reply, reply_data, auth_request->conn);
+			return FALSE;
+		} else {
+			gss_buffer_desc exportbuf;
+			buffer_t *buf;
+			size_t buflen;
+			const char *bufdata;
+
+			auth->state = SASL_GSSAPI_STATE_COMPLETE;
+
+			auth_request->user = p_strndup(auth->pool,
+						       ((unsigned char*) outbuf.value)+4,
+						       outbuf.length-4);
+			i_info("gssapi: got user %s", auth_request->user);
+
+			if (((unsigned char *) outbuf.value)[0] == 0x1) {
+				/* The client only supports authentication */
+				maj_stat = gss_delete_sec_context(&min_stat,
+								  &auth->context,
+								  &exportbuf);
+				if (GSS_ERROR(maj_stat)) {
+					log_gssapi_error(auth->context, "gss_delete_sec_context",
+							 maj_stat, min_stat);
+				}
+				maj_stat = gss_release_buffer(&min_stat, &outbuf);
+				mech_auth_finish(auth_request, NULL, 0, TRUE);
+				return TRUE;
+			}
+			
+			/* Prepare to transfer security context to
+			 * login process, along with QOP flags
+			 * and max transmit packet size.
+			 */
+			maj_stat = gss_export_sec_context(&min_stat,
+							  &auth->context,
+							  &exportbuf);
+			if (GSS_ERROR(maj_stat)) {
+				log_gssapi_error(auth->context, "gss_export_sec_context",
+						 maj_stat, min_stat);
+				reply_data = "Couldn't export security context";
+				reply.data_size = strlen(reply_data)+1;
+				reply.result = AUTH_CLIENT_RESULT_FAILURE;
+				callback(&reply, reply_data, auth_request->conn);
+				return FALSE;
+			}
+#if 0
+			i_info("exporting context: %s", hexstring(exportbuf.value, exportbuf.length));
+#endif
+
+			buf = buffer_create_dynamic(pool_datastack_create(),
+						    4 + exportbuf.length,
+						    (size_t)-1);
+			buffer_append(buf, outbuf.value, 4);
+			buffer_append(buf, exportbuf.value, exportbuf.length);
+
+			bufdata = buffer_get_data(buf, &buflen);
+			mech_auth_finish_extended(auth_request, "GSSAPI",
+						  NULL, 0,
+						  bufdata, buflen, TRUE);
+
+			maj_stat = gss_release_buffer(&min_stat, &outbuf);
+			maj_stat = gss_release_buffer(&min_stat, &exportbuf);
+			return TRUE;
+		}
+		break;
+	default:
+		/* failed */
+		i_info("gssapi: internal error");
+		reply_data = "Internal error";
+		reply.result = AUTH_CLIENT_RESULT_FAILURE;
+		reply.data_size = strlen(reply_data)+1;
+		callback(&reply, reply_data, auth_request->conn);
+		return FALSE;
+	}
+}
+
+static int
+mech_gssapi_auth_initial(struct auth_request *auth_request,
+			 struct auth_client_request_new *request,
+			 const unsigned char *data,
+			 mech_callback_t *callback)
+{
+/* 	struct gssapi_auth_request *auth = */
+/* 		(struct gssapi_auth_request *)auth_request; */
+	struct auth_client_request_reply reply;
+	size_t data_size;
+
+	if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+		/* 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);
+	}
+
+	/* initialize reply */
+	mech_init_auth_client_reply(&reply);
+	reply.id = request->id;
+	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+	/* Empty reply */
+	callback(&reply, NULL, auth_request->conn);
+	return TRUE;
+}
+
+static void mech_gssapi_auth_free(struct auth_request *auth_request)
+{
+	pool_unref(auth_request->pool);
+}
+
+static struct auth_request *
+mech_gssapi_auth_new_from_factory(struct auth_request_factory *factory)
+{
+	struct gssapi_auth_request *auth;
+	pool_t pool;
+
+	pool = pool_alloconly_create("gssapi_auth_request", 2048);
+	auth = p_new(pool, struct gssapi_auth_request, 1);
+	auth->pool = pool;
+	auth->factory = (struct gssapi_auth_request_factory *) factory;
+
+	auth->context = GSS_C_NO_CONTEXT;
+	auth->client = NULL;
+
+	auth->auth_request.refcount = 1;
+	auth->auth_request.pool = pool;
+	auth->auth_request.auth_initial = mech_gssapi_auth_initial;
+	auth->auth_request.auth_continue = mech_gssapi_auth_continue;
+	auth->auth_request.auth_free = mech_gssapi_auth_free;
+	return &auth->auth_request;
+}
+
+static struct auth_request_factory *
+mech_gssapi_auth_factory_new(void)
+{
+	struct gssapi_auth_request_factory *factory;
+	OM_uint32 min_stat, maj_stat;
+	gss_name_t server;
+	gss_buffer_desc buf;
+	string_t *service;
+
+	factory = i_malloc(sizeof (struct gssapi_auth_request_factory));
+
+	service = t_str_new(25);
+	str_append(service, "imap@");
+	str_append(service, my_hostname);
+	buf.length = str_len(service);
+	buf.value = (void*) str_c(service);
+
+	maj_stat = gss_import_name(&min_stat, &buf,
+				   GSS_C_NT_HOSTBASED_SERVICE,
+				   &server);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(GSS_C_NO_CONTEXT, "gss_import_name",
+				 maj_stat, min_stat);
+		i_info("gssapi: Couldn't import name \"%s\"", str_c(service));
+		str_free(service);
+		return NULL;
+	}
+	i_info("gssapi: Imported name \"%s\"", str_c(service));
+	str_free(service);
+
+	maj_stat = gss_acquire_cred(&min_stat, server, 0,
+				    GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+				    &factory->cred, NULL, NULL);
+	gss_release_name(&min_stat, &server);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(GSS_C_NO_CONTEXT, "gss_acquire_cred",
+				 maj_stat, min_stat);
+		i_info("gssapi: Couldn't acquire credentials");		
+		return NULL;
+	}
+	i_info("gssapi: Acquired credentials");
+
+	factory->auth_factory.auth_new = mech_gssapi_auth_new_from_factory;
+	return &factory->auth_factory;
+}
+
+struct mech_module mech_gssapi = {
+	"GSSAPI",
+
+	MEMBER(plaintext) FALSE,
+	MEMBER(advertise) TRUE,
+
+	MEMBER(passdb_need_plain) FALSE,
+	MEMBER(passdb_need_credentials) FALSE,
+
+	NULL,
+	mech_gssapi_auth_factory_new	
+};
+
+#endif
--- /dev/null	2004-06-24 13:58:40.000000000 -0400
+++ src/login-common/integrity-proxy.h	2004-07-10 20:38:07.000000000 -0400
@@ -0,0 +1,33 @@
+/* Copyright (C) 2004 Colin Walters
+ */
+
+#include "mempool.h"
+
+#ifndef __INTEGRITY_PROXY_H
+#define __INTEGRITY_PROXY_H
+
+struct integrity_proxy_factory
+{
+	const char *proxy_name;
+	void (*init)(struct integrity_proxy_factory *factory);
+	struct integrity_proxy *(*new)(struct integrity_proxy_factory *factory,
+				       const unsigned char *context, size_t len, int fd);
+	void (*free)(struct integrity_proxy *proxy);
+	void (*destroy)(struct integrity_proxy_factory *factory);
+};
+
+struct integrity_proxy
+{
+	pool_t pool;
+	int fd;
+};
+
+struct integrity_proxy * integrity_proxy_new (const char *type,
+					      const unsigned char *context,
+					      size_t len,
+					      int fd);
+
+void integrity_proxy_init(void);
+void integrity_proxy_deinit(void);
+
+#endif
--- /dev/null	2004-06-24 13:58:40.000000000 -0400
+++ src/login-common/integrity-proxy.c	2004-07-10 20:38:41.000000000 -0400
@@ -0,0 +1,110 @@
+/* Copyright (C) 2004 Colin Walters
+ * Based partially on mech.c, Copyright (C) 2004 Timo Sirainen
+ */
+
+#include "common.h"
+#include "integrity-proxy.h"
+#include "str.h"
+#include "var-expand.h"
+
+#include <stdlib.h>
+
+struct integrity_proxy_factory_list {
+	struct integrity_proxy_factory_list *next;
+	
+	struct integrity_proxy_factory *factory;
+};
+
+struct integrity_proxy_factory_list *integrity_proxy_factories;
+
+static void integrity_proxy_factory_register(struct integrity_proxy_factory *factory)
+{
+	struct integrity_proxy_factory_list *list;
+
+	list = i_new(struct integrity_proxy_factory_list, 1);
+	list->factory = factory;
+	factory->init(factory);
+	list->next = integrity_proxy_factories;
+	integrity_proxy_factories = list;
+}
+
+static void integrity_proxy_factory_unregister(struct integrity_proxy_factory *factory)
+{
+	struct integrity_proxy_factory_list **pos, *list;
+
+	for (pos = &integrity_proxy_factories; *pos != NULL; pos = &(*pos)->next) {
+		if (strcmp((*pos)->factory->proxy_name, factory->proxy_name) == 0) {
+			list = *pos;
+			*pos = (*pos)->next;
+			list->factory->destroy(list->factory);
+			i_free(list);
+			break;
+		}
+	}
+}
+
+static struct integrity_proxy_factory *integrity_proxy_factory_find(const char *name)
+{
+	struct integrity_proxy_factory_list *list;
+
+	for (list = integrity_proxy_factories; list != NULL; list = list->next) {
+		if (strcasecmp(list->factory->proxy_name, name) == 0) {
+			return list->factory;
+		}
+	}
+	return NULL;
+}
+
+struct integrity_proxy * integrity_proxy_new(const char *mech,
+					     const unsigned char *ctx,
+					     size_t len, int fd)
+{
+	struct integrity_proxy_factory *factory;
+
+	factory = integrity_proxy_factory_find(mech);
+	if (factory == NULL) {
+		return NULL;
+	}
+
+	return factory->new(factory, ctx, len, fd);
+}
+
+#ifdef USE_GSSAPI
+extern struct integrity_proxy_factory integrity_proxy_factory_gssapi;
+#endif
+
+void integrity_proxy_init(void)
+{
+	const char *const *proxies;
+	const char *env;
+
+        integrity_proxy_factories = NULL;
+
+	/* register proxies */
+	env = getenv("MECHANISMS");
+	if (env == NULL || *env == '\0')
+		i_fatal("MECHANISMS environment is unset");
+
+	proxies = t_strsplit_spaces(env, " ");
+	while (*proxies != NULL) {
+#ifdef USE_GSSAPI
+		if (strcasecmp(*proxies, "GSSAPI") == 0) {
+			integrity_proxy_factory_register(&integrity_proxy_factory_gssapi);
+#else
+		if (0) {
+#endif
+		} else {
+			i_fatal("Unknown integrity proxy '%s'",
+				*proxies);
+		}
+
+		proxies++;
+	}
+}
+
+void integrity_proxy_deinit(void)
+{
+#ifdef USE_GSSAPI
+	integrity_proxy_factory_unregister(&integrity_proxy_factory_gssapi);
+#endif
+}
--- /dev/null	2004-06-24 13:58:40.000000000 -0400
+++ src/login-common/integrity-proxy-gssapi.c	2004-07-11 20:35:09.000000000 -0400
@@ -0,0 +1,576 @@
+/* Copyright (C) 2004 Colin Walters
+ * Influenced by ssl-proxy-openssl.c, Copyright (C) 2002 Timo Sirainen
+ */
+
+#include "common.h"
+#include "ioloop.h"
+#include "network.h"
+#include "hash.h"
+#include "buffer.h"
+#include "str.h"
+#include "integrity-proxy.h"
+#include <unistd.h>
+
+#ifdef USE_GSSAPI
+
+#include <gssapi/gssapi.h>
+
+struct integrity_proxy_gssapi {
+	struct integrity_proxy proxy;
+
+	int refcount;
+
+	int fd_secure, fd_plain;
+	struct io *io_secure_read, *io_secure_write, *io_plain_read, *io_plain_write;
+
+	OM_uint32 max_plain_input_size;
+
+	unsigned int secure_size_bytes_to_read;
+	unsigned char size_bytes[4];
+	unsigned int secure_msg_bytes_to_read;
+	buffer_t *secure_input_buf;
+
+	buffer_t *plain_output_buf;
+
+	buffer_t *secure_output_buf;
+
+	gss_ctx_id_t context;
+
+	int destroyed:1;
+};
+
+static int integrity_proxy_gssapi_initialized = 0;
+static struct hash_table *gssapi_integrity_proxies;
+
+static void integrity_proxy_gssapi_factory_init(struct integrity_proxy_factory *factory);
+static void integrity_proxy_gssapi_factory_destroy(struct integrity_proxy_factory *factory);
+static struct integrity_proxy *integrity_proxy_gssapi_new(struct integrity_proxy_factory *factory,
+							  const unsigned char *context,
+							  size_t len,
+							  int fd);
+static void integrity_proxy_gssapi_free(struct integrity_proxy *proxy);
+static void plain_read(void *context);
+static void plain_write(void *context);
+static void secure_write(void *context);
+static void secure_read(void *context);
+static void integrity_proxy_gssapi_destroy(struct integrity_proxy_gssapi *proxy);
+static int integrity_proxy_gssapi_unref(struct integrity_proxy_gssapi *proxy);
+static void secure_block_input(struct integrity_proxy_gssapi *proxy, int block);
+static void plain_block_input(struct integrity_proxy_gssapi *proxy, int block);
+
+static void integrity_proxy_gssapi_factory_init(struct integrity_proxy_factory *factory
+						__attr_unused__)
+{
+	integrity_proxy_gssapi_initialized = 1;
+        gssapi_integrity_proxies = hash_create(default_pool, default_pool, 0, NULL, NULL);
+}
+
+static void integrity_proxy_gssapi_factory_destroy(struct integrity_proxy_factory *factory
+						   __attr_unused__)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	if (!integrity_proxy_gssapi_initialized)
+		return;
+
+	iter = hash_iterate_init(gssapi_integrity_proxies);
+	while (hash_iterate(iter, &key, &value))
+		integrity_proxy_gssapi_destroy(value);
+	hash_iterate_deinit(iter);
+	hash_destroy(gssapi_integrity_proxies);
+}
+
+static void log_gssapi_error(gss_ctx_id_t context,
+			     const char *func_name,
+			     OM_uint32 maj_stat,
+			     OM_uint32 min_stat)
+{
+	OM_uint32 tmp_min_stat, tmp_maj_stat;
+	gss_buffer_desc gssbuf;
+	string_t *logbuf;
+	OM_uint32 msg_context;
+
+	logbuf = str_new(default_pool, 128);	
+	
+	msg_context = 0;
+	/* The GSSAPI error api is way overkill. */ 
+	tmp_maj_stat = gss_display_status(&tmp_min_stat,
+					  maj_stat,
+					  GSS_C_GSS_CODE,
+					  GSS_C_NULL_OID,
+					  &msg_context,
+					  &gssbuf);
+	if (GSS_ERROR(tmp_maj_stat)) {
+		str_append(logbuf, "UNKNOWN (");
+	} else {
+		str_append_n(logbuf, gssbuf.value, gssbuf.length);
+		str_append(logbuf, " (");
+		maj_stat = gss_release_buffer(&min_stat, &gssbuf);
+	}
+		
+	msg_context = 0;
+	tmp_maj_stat = gss_display_status(&tmp_min_stat,
+					  min_stat,
+					  GSS_C_MECH_CODE,
+					  GSS_C_NULL_OID,
+					  &msg_context,
+					  &gssbuf);
+	if (GSS_ERROR(tmp_maj_stat)) {
+		str_append(logbuf, "UNKNOWN)");
+	} else {
+		str_append_n(logbuf, gssbuf.value, gssbuf.length);
+		maj_stat = gss_release_buffer(&min_stat, &gssbuf);
+		str_append_c(logbuf, ')');
+	}
+	
+	i_warning("%s failed: %s", func_name, str_data(logbuf));
+	str_free(logbuf);
+}
+
+static struct integrity_proxy *integrity_proxy_gssapi_new(struct integrity_proxy_factory *factory __attr_unused__,
+							  const unsigned char *context,
+							  size_t len,
+							  int fd)
+{
+	struct integrity_proxy_gssapi *gss;
+	pool_t pool;
+	int sfd[2];
+	gss_buffer_desc buf;
+	OM_uint32 min_stat, maj_stat;
+	gss_qop_t qop;
+	uint32_t max_client_bufsize;
+
+	pool = pool_alloconly_create("integrity_proxy_gssapi", 2048);
+
+	gss = p_new(pool, struct integrity_proxy_gssapi, 1);
+	gss->proxy.pool = pool;
+	gss->refcount = 2;
+
+	if (len <= 4) {
+		i_error("too short context in integrity_proxy_gssapi_new");
+		return NULL;
+	}
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) {
+		i_error("socketpair() failed: %m");
+		return NULL;
+	}
+
+	net_set_nonblock(sfd[0], TRUE);
+	net_set_nonblock(sfd[1], TRUE);
+	net_set_nonblock(fd, TRUE);
+
+	gss->fd_secure = fd;
+	gss->fd_plain = sfd[0];
+	gss->proxy.fd = sfd[1];
+
+	qop = context[0];
+	max_client_bufsize = (context[1] << 16) + (context[2] << 8) + context[3];
+	i_info("qop %d max_client_bufsize %d", qop, max_client_bufsize);
+	/* Sanity. */
+	if (max_client_bufsize == 0)
+		max_client_bufsize = 1024;
+
+	buf.value = (void *) (context+4);
+	buf.length = len-4;
+	maj_stat = gss_import_sec_context(&min_stat,
+					  &buf,
+					  &gss->context);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(gss->context,
+				 "gss_import_sec_context",
+				 maj_stat, min_stat);
+		close(sfd[0]);
+		close(sfd[1]);
+		pool_unref(pool);
+		return NULL;
+	}
+
+	maj_stat = gss_wrap_size_limit(&min_stat,
+				       gss->context,
+				       1,
+				       GSS_C_QOP_DEFAULT,
+				       max_client_bufsize,
+				       &gss->max_plain_input_size);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(gss->context,
+				 "gss_wrap_size_limit",
+				 maj_stat, min_stat);
+		gss->max_plain_input_size = 1024;
+	}
+
+	i_info("created new integrity proxy");
+
+	hash_insert(gssapi_integrity_proxies, gss, gss);
+
+	gss->secure_size_bytes_to_read = 4;
+
+	gss->secure_input_buf = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+	gss->secure_output_buf = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+	gss->plain_output_buf = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+	secure_block_input(gss, FALSE);
+	plain_block_input(gss, FALSE);
+        main_ref();
+	
+	return &gss->proxy;
+}
+
+static void integrity_proxy_gssapi_free(struct integrity_proxy *proxy)
+{
+	struct integrity_proxy_gssapi *gss = (struct integrity_proxy_gssapi *) proxy;
+	if (!gss->destroyed)
+		integrity_proxy_gssapi_destroy(gss);
+
+	integrity_proxy_gssapi_unref(gss);
+}
+
+static void secure_block_input(struct integrity_proxy_gssapi *proxy,
+			       int block)
+{
+	if (block) {
+		if (proxy->io_secure_read != NULL) {
+			io_remove(proxy->io_secure_read);
+			proxy->io_secure_read = NULL;
+		}
+	} else {
+		if (proxy->io_secure_read == NULL) {
+			proxy->io_secure_read = io_add(proxy->fd_secure, IO_READ,
+						       secure_read, proxy);
+		}
+	}
+}
+		
+static void secure_block_output(struct integrity_proxy_gssapi *proxy,
+				int block)
+{
+	if (block) {
+		if (proxy->io_secure_write != NULL) {
+			io_remove(proxy->io_secure_write);
+			proxy->io_secure_write = NULL;
+		}
+	} else {
+		if (proxy->io_secure_write == NULL) {
+			proxy->io_secure_write = io_add(proxy->fd_secure, IO_WRITE,
+							secure_write, proxy);
+		}
+	}
+}
+
+static void plain_block_input(struct integrity_proxy_gssapi *proxy, int block)
+{
+	if (block) {
+		if (proxy->io_plain_read != NULL) {
+			io_remove(proxy->io_plain_read);
+			proxy->io_plain_read = NULL;
+		}
+	} else {
+		if (proxy->io_plain_read == NULL) {
+			proxy->io_plain_read = io_add(proxy->fd_plain, IO_READ,
+						      plain_read, proxy);
+		}
+	}
+}
+
+static void plain_read(void *context)
+{
+	struct integrity_proxy_gssapi *proxy = context;
+	buffer_t *tmp_buf;
+	size_t buflen;
+	ssize_t ret;
+	OM_uint32 min_stat, maj_stat;
+	gss_buffer_desc inbuf, outbuf;
+
+	i_info("plain read");
+
+	proxy->refcount++;
+
+	tmp_buf = buffer_create_dynamic(pool_datastack_create(), 4096, (size_t)-1);
+	while (buffer_get_used_size(tmp_buf) < proxy->max_plain_input_size &&
+	       !proxy->destroyed) {
+		unsigned char tmp_secure_output_buf[1024];
+		ret = net_receive(proxy->fd_plain,
+				  tmp_secure_output_buf,
+				  sizeof(tmp_secure_output_buf));
+		if (ret <= 0) {
+			if (ret < 0) {
+				i_warning("net_receive in plain_read failed: %m");
+				integrity_proxy_gssapi_destroy(proxy);
+			}
+			break;
+		} else {
+			buffer_append(tmp_buf, tmp_secure_output_buf, ret);
+		}
+	}
+
+
+	inbuf.value = (void*) buffer_get_data(tmp_buf, &buflen);
+	inbuf.length = buflen;
+	i_info("read %d plain bytes", buflen);
+
+	if (buflen > 0) {
+		maj_stat = gss_wrap(&min_stat,
+				    proxy->context, 
+				    1,
+				    GSS_C_QOP_DEFAULT,
+				    &inbuf,
+				    NULL,
+				    &outbuf);
+
+		if (GSS_ERROR(maj_stat)) {
+			log_gssapi_error(proxy->context, "gss_wrap",
+					 maj_stat, min_stat);
+			integrity_proxy_gssapi_destroy(proxy);
+		}
+
+		/* Store the size in network byte order */
+		buffer_append_c(proxy->secure_output_buf,
+				outbuf.length & (0xFF000000));
+		buffer_append_c(proxy->secure_output_buf,
+				outbuf.length & (0x00FF0000));
+		buffer_append_c(proxy->secure_output_buf,
+				outbuf.length & (0x0000FF00));
+		buffer_append_c(proxy->secure_output_buf,
+				outbuf.length & (0x000000FF));
+		buffer_append(proxy->secure_output_buf,
+			      outbuf.value, outbuf.length);
+		i_info("transformed plain bytes to %d encrypted bytes", outbuf.length);
+		maj_stat = gss_release_buffer(&min_stat, &outbuf);
+		if (GSS_ERROR(maj_stat)) {
+			log_gssapi_error(proxy->context, "gss_release_buffer",
+					 maj_stat, min_stat);
+		}
+		secure_write(proxy);
+	}
+
+	integrity_proxy_gssapi_unref(proxy);
+}
+
+static void plain_write(void *context)
+{
+	struct integrity_proxy_gssapi *proxy = context;
+	ssize_t ret;
+	const unsigned char *bufdata;
+	size_t bufsize;
+
+	i_info("plain write");
+
+	proxy->refcount++;
+
+	bufdata = buffer_get_data(proxy->plain_output_buf, &bufsize);
+	ret = net_transmit(proxy->fd_plain, bufdata, bufsize);
+	if (ret < 0) {
+		i_warning("net_transmit in plain_write failed: %m");
+		integrity_proxy_gssapi_destroy(proxy);
+	} else if (ret > 0) {
+		buffer_delete(proxy->plain_output_buf, 0, ret);
+		i_info("wrote %d plain bytes", ret);
+
+		if (buffer_get_used_size(proxy->plain_output_buf) > 0) {
+			if (proxy->io_plain_write == NULL) {
+				proxy->io_plain_write =
+					io_add(proxy->fd_plain, IO_WRITE,
+					       plain_write, proxy);
+			}
+		} else {
+			if (proxy->io_plain_write != NULL) {
+				io_remove(proxy->io_plain_write);
+                                proxy->io_plain_write = NULL;
+			}
+		}
+
+		secure_block_input(proxy, FALSE);
+	}
+
+	integrity_proxy_gssapi_unref(proxy);
+}
+
+static void handle_msg_completion(struct integrity_proxy_gssapi *proxy)
+{
+	OM_uint32 min_stat, maj_stat;
+	gss_buffer_desc inbuf, outbuf;
+	size_t bufsize;
+	
+	inbuf.value = (void*) buffer_get_data(proxy->secure_input_buf, &bufsize);
+	inbuf.length = bufsize;
+	
+	maj_stat = gss_unwrap(&min_stat,
+			      proxy->context, 
+			      &inbuf, &outbuf,
+			      NULL, NULL);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(proxy->context, "gss_unwrap", maj_stat, min_stat);
+		integrity_proxy_gssapi_destroy(proxy);
+		return;
+	}
+	i_info("token complete, unwrapped to %d plain bytes", outbuf.length);
+	buffer_delete(proxy->secure_input_buf, 0, bufsize);
+
+	buffer_append(proxy->plain_output_buf, outbuf.value, outbuf.length);
+	maj_stat = gss_release_buffer(&min_stat, &outbuf);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(proxy->context, "gss_release_buffer",
+				 maj_stat, min_stat);
+	}
+
+	plain_write(proxy);
+
+	proxy->secure_size_bytes_to_read = 4;
+	proxy->secure_msg_bytes_to_read = 0;
+}
+
+static void secure_read(void *context)
+{
+	int ret;
+	int did_read_size_bytes; 
+	struct integrity_proxy_gssapi *proxy = context;
+
+	proxy->refcount++;
+
+	did_read_size_bytes = 0;
+	while (proxy->secure_size_bytes_to_read > 0
+	       && !proxy->destroyed) {
+		unsigned int offset;
+
+		i_assert(proxy->secure_size_bytes_to_read <= sizeof(proxy->size_bytes));
+
+		offset = sizeof(proxy->size_bytes) - proxy->secure_size_bytes_to_read;
+		ret = net_receive(proxy->fd_secure,
+				  proxy->size_bytes + offset,
+				  proxy->secure_size_bytes_to_read);
+
+		if (ret <= 0) {
+			if (ret < 0) {
+				i_warning("net_receive in secure_read failed: %m");
+				integrity_proxy_gssapi_destroy(proxy);
+			}
+			break;
+		} else {
+			proxy->secure_size_bytes_to_read -= ret;
+			did_read_size_bytes = 1;
+		}
+	}
+
+	if (did_read_size_bytes && proxy->secure_size_bytes_to_read == 0
+	    && !proxy->destroyed) {
+		/* Transition to reading the message content */
+		proxy->secure_msg_bytes_to_read =
+			(proxy->size_bytes[0] << 24)
+			+ (proxy->size_bytes[1] << 16)
+			+ (proxy->size_bytes[2] << 8)
+			+ (proxy->size_bytes[3]);
+		i_info("reading secure token of length %d bytes",
+		       proxy->secure_msg_bytes_to_read);
+	}
+
+	while (proxy->secure_size_bytes_to_read == 0
+	       && proxy->secure_msg_bytes_to_read > 0
+	       && !proxy->destroyed) {
+		unsigned char tmp_secure_input_buf[1024];
+		size_t bytes_to_read;
+
+		bytes_to_read = I_MIN(sizeof(tmp_secure_input_buf),
+				      proxy->secure_msg_bytes_to_read);
+
+		ret = net_receive(proxy->fd_secure,
+				  tmp_secure_input_buf,
+				  bytes_to_read);
+		if (ret <= 0) {
+			if (ret < 0) {
+				i_warning("net_receive in secure_read failed: %m");
+				integrity_proxy_gssapi_destroy(proxy);
+			}
+			break;
+		} else {
+			buffer_append(proxy->secure_input_buf,
+				      tmp_secure_input_buf,
+				      ret);
+			i_info("read %d secure bytes", ret);
+			proxy->secure_msg_bytes_to_read -= ret;
+			if (proxy->secure_msg_bytes_to_read == 0)
+				handle_msg_completion(proxy);
+		}
+	}
+
+	integrity_proxy_gssapi_unref(proxy);
+}
+
+static void secure_write(void *context)
+{
+	int ret;
+	struct integrity_proxy_gssapi *proxy = context;
+	const unsigned char *bufdata;
+	size_t bufsize;
+
+	i_info("secure write");
+	
+	bufdata = (void*) buffer_get_data(proxy->secure_output_buf, &bufsize);
+	
+	ret = net_transmit(proxy->fd_secure, bufdata, bufsize);
+	if (ret < 0) {
+		integrity_proxy_gssapi_destroy(proxy);
+		i_warning("net_transmit failed: %m");
+	} else {
+		i_info("wrote %d secure bytes", ret);
+		buffer_delete(proxy->secure_output_buf, 0, ret);
+		secure_block_output(proxy, (bufsize - ret) <= 0);
+		plain_block_input(proxy, FALSE);
+	}
+}
+
+static int integrity_proxy_gssapi_unref(struct integrity_proxy_gssapi *proxy)
+{
+	if (--proxy->refcount > 0)
+		return TRUE;
+	i_assert(proxy->refcount == 0);
+	
+	pool_unref(proxy->proxy.pool);
+	main_unref();
+
+	return FALSE;
+}
+
+static void integrity_proxy_gssapi_destroy(struct integrity_proxy_gssapi *proxy)
+{
+	OM_uint32 min_stat, maj_stat;
+
+	i_info("destroying integrity proxy");
+
+	hash_remove(gssapi_integrity_proxies, proxy);
+
+	(void)net_disconnect(proxy->fd_secure);
+	(void)net_disconnect(proxy->fd_plain);
+
+	if (proxy->io_secure_read != NULL)
+		io_remove(proxy->io_secure_read);
+	if (proxy->io_secure_write != NULL)
+		io_remove(proxy->io_secure_write);
+	if (proxy->io_plain_read != NULL)
+		io_remove(proxy->io_plain_read);
+	if (proxy->io_plain_write != NULL)
+		io_remove(proxy->io_plain_write);
+
+	maj_stat = gss_delete_sec_context(&min_stat,
+					  &proxy->context,
+					  NULL);
+	if (GSS_ERROR(maj_stat)) {
+		log_gssapi_error(GSS_C_NO_CONTEXT,
+				 "gss_delete_sec_context",
+				 maj_stat, min_stat);
+	}
+
+	buffer_free(proxy->plain_output_buf);
+	buffer_free(proxy->secure_input_buf);
+	buffer_free(proxy->secure_output_buf);
+}
+
+struct integrity_proxy_factory integrity_proxy_factory_gssapi = {
+	"GSSAPI",
+
+	integrity_proxy_gssapi_factory_init,
+	integrity_proxy_gssapi_new,
+	integrity_proxy_gssapi_free,
+	integrity_proxy_gssapi_factory_destroy,
+};
+
+#endif
