Patches to imap-3.3 (in pine 3.90) to add the AUTHENTICATE command
with the KERBEROS_V4 mechanism.

Some assembley required:

The appropriate osdep.c has to be modified to include log_krb.c
instead of log_std.c

The appropriate makefile has to be modified to include the Kerberos libraries


diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/acte.h ./ANSI/c-client/acte.h
*** /usr/tmp/pine3.90/imap/ANSI/c-client/acte.h	Sun Aug 28 19:02:59 1994
--- ./ANSI/c-client/acte.h	Sun Aug 28 18:07:20 1994
***************
*** 0 ****
--- 1,52 ----
+ /* acte.h -- Interface for IMAP AUTHENTICATE mechanisms 
+  *
+  *	(C) Copyright 1994 by Carnegie Mellon University
+  *
+  *                      All Rights Reserved
+  *
+  * Permission to use, copy, modify, and distribute this software and its 
+  * documentation for any purpose and without fee is hereby granted, 
+  * provided that the above copyright notice appear in all copies and that
+  * both that copyright notice and this permission notice appear in 
+  * supporting documentation, and that the name of CMU not be
+  * used in advertising or publicity pertaining to distribution of the
+  * software without specific, written prior permission.  
+  * 
+  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+  * SOFTWARE.
+  *
+  */
+ 
+ /* Client-side authentication mechanism */
+ struct acte_client {
+     char *auth_type;
+     int (*start)();
+     int (*auth)();
+     void (*query_state)();
+     void (*free_state)();
+ };
+ 
+ /* Server-side authentication mechanism */
+ struct acte_server {
+     char *auth_type;
+     int (*start)();
+     int (*auth)();
+     void (*query_state)();
+     void (*free_state)();
+ };
+ 
+ /* Protection mechanisms */
+ #define ACTE_PROT_NONE 1
+ #define ACTE_PROT_INTEGRITY 2
+ #define ACTE_PROT_PRIVACY 4
+ #define ACTE_PROT_ANY (ACTE_PROT_NONE|ACTE_PROT_INTEGRITY|ACTE_PROT_PRIVACY)
+ 
+ #define ACTE_FAIL 1		/* Authentication failed */
+ 
+ #define ACTE_DONE 3		/* Server has authenticated user */
+ 
diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/acte_krb.c ./ANSI/c-client/acte_krb.c
*** /usr/tmp/pine3.90/imap/ANSI/c-client/acte_krb.c	Sun Aug 28 19:02:57 1994
--- ./ANSI/c-client/acte_krb.c	Sun Aug 28 18:07:24 1994
***************
*** 0 ****
--- 1,831 ----
+ /* acte_krb.c -- KERBEROS_V4 authentication routines for IMAP.
+  *
+  *	(C) Copyright 1994 by Carnegie Mellon University
+  *
+  *                      All Rights Reserved
+  *
+  * Permission to use, copy, modify, and distribute this software and its 
+  * documentation for any purpose and without fee is hereby granted, 
+  * provided that the above copyright notice appear in all copies and that
+  * both that copyright notice and this permission notice appear in 
+  * supporting documentation, and that the name of CMU not be
+  * used in advertising or publicity pertaining to distribution of the
+  * software without specific, written prior permission.  
+  * 
+  * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+  * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+  * SOFTWARE.
+  *
+  */
+ #include <stdio.h>
+ #include <string.h>
+ #include <netdb.h>
+ #include <sys/types.h>
+ #include <sys/param.h>
+ #include <netinet/in.h>
+ #include <krb.h>
+ 
+ #include "acte.h"
+ 
+ extern char *malloc();
+ extern char *lcase();
+ extern char *krb_get_phost(), *krb_realmofhost();
+ 
+ static char *srvtab = "";	/* Srvtab filename */
+ 
+ /* Maximum number of bytes of overhead the protection mechanisms use */
+ #define PROTECTION_OVERHEAD 31
+ 
+ /* Private state used by this mechanism */
+ struct krb_state {
+     /* common */
+     int authstepno;
+     des_cblock session;	/* Our session key */
+     des_key_schedule schedule; /* Schedule for our session key */
+     long challenge;
+     char user[MAX_K_NAME_SZ+1];
+     int protallowed;
+     int maxbufsize;
+     struct sockaddr_in localaddr, remoteaddr;
+     long prot_time_sec;
+     char prot_time_5ms;
+     /* client */
+     char instance[INST_SZ];
+     char realm[REALM_SZ];
+     /* server */
+     int (*authproc)();
+     AUTH_DAT kdata;
+ };
+ 
+ /*
+  * Free the space used by an opaque state pointer
+  */
+ static void
+ krb_free_state(state)
+ void *state;
+ {
+     memset((char *)state, 0, sizeof(struct krb_state));
+     free((char *) state);
+ }
+ 
+ static char *krb_en_integrity(), *krb_en_privacy();
+ static char *krb_de_integrity(), *krb_de_privacy();
+ 
+ /*
+  * Query public values of the state pointer after authentiation
+  * complete.  Fills in buffers pointed to by the following arguments:
+  *
+  * user       -- IMAP userid authenticated as
+  * protlevel  -- bitmask for selected protection mechanism
+  * encodefunc -- if nonzero, protection mechanism function to encode
+  *               outgoing data with.
+  * decodefunc -- if nonzero, protection mechanism function to decode
+  *               incoming data with.
+  * maxplain   -- The maximum number of bytes that may be encoded by
+  *                the encodefunc at one time
+  */
+ static void 
+ krb_query_state(state, user, protlevel, encodefunc, decodefunc, maxplain)
+ void *state;
+ char **user;
+ int *protlevel;
+ char *(**encodefunc)();
+ char *(**decodefunc)();
+ int *maxplain;
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+ 
+     *user = kstate->user;
+     *protlevel = kstate->protallowed;
+ 
+     switch (kstate->protallowed) {
+     case ACTE_PROT_NONE:
+ 	*encodefunc = *decodefunc = 0;
+ 	*maxplain = 0;
+ 	return;
+ 
+     case ACTE_PROT_INTEGRITY:
+ 	*encodefunc = krb_en_integrity;
+ 	*decodefunc = krb_de_integrity;
+ 	*maxplain = kstate->maxbufsize - PROTECTION_OVERHEAD;
+ 	return;
+ 
+     case ACTE_PROT_PRIVACY:
+ 	*encodefunc = krb_en_privacy;
+ 	*decodefunc = krb_de_privacy;
+ 	*maxplain = kstate->maxbufsize - PROTECTION_OVERHEAD;
+ 	return;
+ 
+     default:
+ 	abort();
+     }
+ }
+ 
+ /*
+  * Start the client side of an authentication exchange.
+  */
+ static int krb_client_start(host, user, protallowed, maxbufsize,
+ 			    localaddr, remoteaddr, state)
+ char *host;			/* Name of server host */
+ char *user;			/* (optional) user to log in as */
+ int protallowed;		/* Protection mechanisms allowed */
+ int maxbufsize;			/* Maximum ciphertext input buffer size */
+ struct sockaddr *localaddr;	/* Network address of local side */
+ struct sockaddr *remoteaddr;	/* Network address of remote side */
+ void **state;			/* On success, filled in with state ptr */
+ {
+     struct hostent *host_name;
+     char userbuf[MAX_K_NAME_SZ+1];
+     char instance[INST_SZ];
+     char realm[REALM_SZ];
+     char uinst[INST_SZ];
+     char urealm[INST_SZ];
+     KTEXT_ST authent;
+     CREDENTIALS cr;
+     struct krb_state *kstate;
+ 
+     protallowed &= ACTE_PROT_NONE|ACTE_PROT_INTEGRITY|ACTE_PROT_PRIVACY;
+     if (!localaddr || !remoteaddr) {
+ 	protallowed &= ACTE_PROT_NONE;
+     }
+     if (!protallowed) {
+ 	return ACTE_FAIL;
+     }
+     if (maxbufsize > 0xffffff) maxbufsize = 0xffffff;
+ 
+     /* Canonicalize hostname */
+     host_name = gethostbyname(host);
+     if (!host_name) {
+ 	return ACTE_FAIL;
+     }
+ 
+     strcpy(realm, krb_realmofhost(host_name->h_name));
+     strcpy(instance, krb_get_phost(host_name->h_name));
+ 
+     /* Fetch imap.hostname service key */
+     (void) krb_mk_req(&authent, "imap", instance, realm, 0);
+     memset(&authent, 0, sizeof(authent));
+ 
+     if (krb_get_cred("imap", instance, realm, &cr)) {
+ 	return ACTE_FAIL;
+     }
+     
+     if (!user || !user[0]) {
+ 	user = userbuf;
+ 	if (krb_get_tf_fullname(TKT_FILE, user, uinst, urealm)) {
+ 	    memset(&cr, 0, sizeof(cr));
+ 	    return ACTE_FAIL;
+ 	}
+ 	if (uinst[0]) {
+ 	    strcat(user, ".");
+ 	    strcat(user, uinst);
+ 	}
+ 	if (strcmp(urealm, realm) != 0) {
+ 	    strcat(user, "@");
+ 	    strcat(user, urealm);
+ 	}
+     }
+     else if (strlen(user) > MAX_K_NAME_SZ) {
+ 	return ACTE_FAIL;
+     }
+ 
+     kstate = (struct krb_state *)malloc(sizeof(struct krb_state));
+     if (!kstate) return ACTE_FAIL;
+     memset((char *)kstate, 0, sizeof(*kstate));
+     kstate->authstepno = 0;
+     memcpy(kstate->session, cr.session, sizeof(des_cblock));
+     des_key_sched(kstate->session, kstate->schedule);
+     strcpy(kstate->user, user);
+     kstate->protallowed = protallowed;
+     kstate->maxbufsize = maxbufsize;
+     if (localaddr && remoteaddr) {
+ 	kstate->localaddr = *(struct sockaddr_in *)localaddr;
+ 	kstate->remoteaddr = *(struct sockaddr_in *)remoteaddr;
+     }
+     strcpy(kstate->instance, instance);
+     strcpy(kstate->realm, realm);
+ 
+     memset(&cr, 0, sizeof(cr));
+     *state = (void *)kstate;
+     return 0;
+ }
+ 
+ /*
+  * Perform client-side authentication protocol exchange
+  * Returns ACTE_DONE if authentication can be complete after
+  * sending our client reply.
+  */
+ static int krb_client_auth(state, inputlen, input, outputlen, output)
+ void *state;			/* State of exchange */
+ int inputlen;			/* Length of server response */
+ char *input;			/* Server response data */
+ int *outputlen;			/* Set to length of client reply */
+ char **output;			/* Set to point to client reply data */
+ {
+     static KTEXT_ST authent;
+     struct krb_state *kstate = (struct krb_state *)state;
+     int code;
+     int maxbufsize;
+ 
+     switch (kstate->authstepno++) {
+     case 0:
+ 	/* Server gave us challenge, respond with ticket+authenticator */
+ 	if (inputlen < 4) {
+ 	    kstate->authstepno = -1;
+ 	    return ACTE_FAIL;
+ 	}
+ 	kstate->challenge = ntohl(*(int *)input);
+ 
+ 	code = krb_mk_req(&authent, "imap", kstate->instance, kstate->realm,
+ 			  kstate->challenge);
+ 	if (code) {
+ 	    kstate->authstepno = -1;
+ 	    return ACTE_FAIL;
+ 	}
+ 	*outputlen = authent.length;
+ 	*output = authent.dat;
+ 	return 0;
+ 
+     case 1:
+ 	/*
+ 	 * Server gave us mutual auth reply+available protection mechanisms.
+ 	 * Respond with challenge, desired protection mechanism, userid
+ 	 */
+ 	if (inputlen < 8) {
+ 	    kstate->authstepno = -1;
+ 	    return ACTE_FAIL;
+ 	}
+ 	des_ecb_encrypt(input, input, kstate->schedule, 0);
+ 	if (ntohl(*(int *)input) != kstate->challenge + 1) {
+ 	    /* Server failed to mutually authenticte */
+ 	    kstate->authstepno = -1;
+ 	    return ACTE_FAIL;
+ 	}	    
+ 	maxbufsize = ntohl(*(int *)(input+4)) & 0xfffff;
+ 	kstate->protallowed &= input[4];
+ 	if (maxbufsize <= PROTECTION_OVERHEAD) {
+ 	    /* Protection buffer too small */
+ 	    kstate->protallowed &= ACTE_PROT_NONE;
+ 	}
+ 	if (kstate->protallowed & ACTE_PROT_PRIVACY) {
+ 	    kstate->protallowed = ACTE_PROT_PRIVACY;
+ 	}
+ 	else if (kstate->protallowed & ACTE_PROT_INTEGRITY) {
+ 	    kstate->protallowed = ACTE_PROT_INTEGRITY;
+ 	}
+ 	else if (kstate->protallowed & ACTE_PROT_NONE) {
+ 	    kstate->protallowed = ACTE_PROT_NONE;
+ 	}
+ 	else {
+ 	    /* No mutually agreeable protection mechanism */
+ 	    kstate->authstepno = -1;
+ 	    return ACTE_FAIL;
+ 	}
+ 
+ 	*(int *)authent.dat = htonl(kstate->challenge);
+ 	*(int *)(authent.dat+4) = htonl(kstate->maxbufsize);
+ 	authent.dat[4] = kstate->protallowed;
+ 	strcpy(&authent.dat[8], kstate->user);
+ 	authent.length = 8+strlen(kstate->user);
+ 	do {
+ 	    authent.dat[authent.length++] = '\0';
+ 	} while (authent.length & 7);
+ 	des_pcbc_encrypt(authent.dat, authent.dat, authent.length,
+ 			 kstate->schedule, kstate->session, 1);
+ 	*output = authent.dat;
+ 	*outputlen = authent.length;
+ 	if (maxbufsize < kstate->maxbufsize) kstate->maxbufsize = maxbufsize;
+ 	return ACTE_DONE;
+ 
+     default:
+ 	kstate->authstepno = -1;
+ 	return ACTE_FAIL;
+     }
+ }
+ 
+ /* Exported definition of client-side authentication mechanism */
+ struct acte_client krb_acte_client = {
+     "KERBEROS_V4",
+     krb_client_start,
+     krb_client_auth,
+     krb_query_state,
+     krb_free_state
+ };
+ 
+ /*
+  * Start the server side of an authentication exchange
+  */
+ static int
+ krb_server_start(authproc, protallowed, maxbufsize,
+ 		 localaddr, remoteaddr, outputlen, output, state, reply)
+ int (*authproc)();		/* (optional) function to decide
+ 				 * authoriztion to log in as given user
+ 				 */
+ int protallowed;		/* Protection mechanisms allowed */
+ int maxbufsize;			/* Maximum ciphertext input buffer size */
+ struct sockaddr *localaddr;	/* Network address of local side */
+ struct sockaddr *remoteaddr;	/* Network address of remote side */
+ int *outputlen;			/* Set to length of initial reply */
+ char **output;			/* Set to point to initial reply data */
+ void **state;			/* On success, filled in with state ptr */
+ char **reply;			/* On failure, filled in with ptr to reason */
+ {
+     static char outputbuf[4];
+     struct krb_state *kstate;
+ 
+     protallowed &= ACTE_PROT_NONE|ACTE_PROT_INTEGRITY|ACTE_PROT_PRIVACY;
+     if (!localaddr || !remoteaddr) {
+ 	protallowed &= ACTE_PROT_NONE;
+     }
+     if (!protallowed) {
+ 	*reply = "No suitable protection mechanism";
+ 	return ACTE_FAIL;
+     }
+     if (maxbufsize > 0xffffff) maxbufsize = 0xffffff;
+ 
+     kstate = (struct krb_state *)malloc(sizeof(struct krb_state));
+     if (!kstate) {
+ 	*reply = "Out of memory";
+ 	return ACTE_FAIL;
+     }
+     memset((char *)kstate, 0, sizeof(*kstate));
+     kstate->authstepno = 0;
+     kstate->challenge = time(0) ^ getpid();
+     kstate->protallowed = protallowed;
+     kstate->maxbufsize = maxbufsize;
+     if (localaddr && remoteaddr) {
+ 	kstate->localaddr = *(struct sockaddr_in *)localaddr;
+ 	kstate->remoteaddr = *(struct sockaddr_in *)remoteaddr;
+     }
+     kstate->authproc = authproc;
+ 
+     *(int *)outputbuf = htonl(kstate->challenge);
+     *output = outputbuf;
+     *outputlen = 4;
+     *state = (void *)kstate;
+     
+     return 0;
+ }
+ 
+ /*
+  * Perform server-side authentication protocol exchange.
+  * Returns 0 to continue exchange, ACTE_FAIL on failure, and ACTE_DONE
+  * if user is now successfully authenticated
+  */
+ static int krb_server_auth(state, inputlen, input, outputlen, output, reply)
+ void *state;			/* State of exchange */
+ int inputlen;			/* Length of client response */
+ char *input;			/* Client response data */
+ int *outputlen;			/* Set to length of server reply */
+ char **output;			/* Set to point to server reply data */
+ char **reply;			/* On failure, filled in with ptr to reason */
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+     static char outputbuf[8];
+     KTEXT_ST authent;
+     int code;
+     char instance[INST_SZ];
+     char realm[REALM_SZ];
+     int protallowed;
+     int maxbufsize;
+     char clientname[MAX_K_NAME_SZ+1];
+ 
+     switch (kstate->authstepno++) {
+     case 0:
+ 	/*
+ 	 * Client gave us ticket+authenticator
+ 	 * reply with mutual auth + supported protection mechanisms
+ 	 */
+ 	if (inputlen > MAX_KTXT_LEN) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Kerberos authenticator too long";
+ 	    return ACTE_FAIL;
+ 	}
+ 	authent.length = inputlen;
+ 	memcpy(authent.dat, input, inputlen);
+ 	authent.mbz = 0;
+ 	strcpy(instance, "*");
+ 	code = krb_rd_req(&authent, "imap", instance, 0L, &kstate->kdata,
+ 			  srvtab);
+ 	if (code) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = krb_err_txt[code];
+ 	    return ACTE_FAIL;
+ 	}
+ 	if (kstate->kdata.checksum != kstate->challenge) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Incorrect checksum in Kerberos authenticator";
+ 	    return ACTE_FAIL;
+ 	}
+ 	memcpy(kstate->session, kstate->kdata.session, sizeof(des_cblock));
+ 	des_key_sched(kstate->session, kstate->schedule);
+ 	
+ 	*(int *)outputbuf = htonl(kstate->challenge+1);
+ 	*(int *)(outputbuf+4) = htonl(kstate->maxbufsize);
+ 	outputbuf[4] = kstate->protallowed;
+ 	des_ecb_encrypt(outputbuf, outputbuf, kstate->schedule, 1);
+ 	*output = outputbuf;
+ 	*outputlen = 8;
+ 
+ 	return 0;
+ 
+     case 1:
+ 	/* Client gave us selected protection mechanism + userid, we're done */
+ 	if (inputlen < 16 || inputlen & 7) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Kerberos authenticator has incorrect length";
+ 	    return ACTE_FAIL;
+ 	}
+ 	des_pcbc_encrypt(input, input, inputlen,
+ 			 kstate->schedule, kstate->session, 0);
+ 	if (ntohl(*(int *)input) != kstate->challenge) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Incorrect checksum in Kerberos authenticator";
+ 	    return ACTE_FAIL;
+ 	}
+ 	maxbufsize = ntohl(*(int *)(input+4)) & 0xfffff;
+ 	if (maxbufsize < kstate->maxbufsize) kstate->maxbufsize = maxbufsize;
+ 	protallowed = input[4];
+ 	if (!(protallowed & kstate->protallowed)) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "No suitable protection mechanism selected";
+ 	    return ACTE_FAIL;
+ 	}
+ 	if (protallowed != ACTE_PROT_PRIVACY &&
+ 	    protallowed != ACTE_PROT_INTEGRITY &&
+ 	    protallowed != ACTE_PROT_NONE) {
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Multiple protection mechanisms selected";
+ 	    return ACTE_FAIL;
+ 	}
+ 	if (protallowed != ACTE_PROT_NONE &&
+ 	    kstate->maxbufsize <= PROTECTION_OVERHEAD) {
+ 	    /* Protection buffer too small */
+ 	    kstate->authstepno = -1;
+ 	    *reply = "Protection buffer size too small";
+ 	    return ACTE_FAIL;
+ 	}
+ 	kstate->protallowed = protallowed;
+ 
+ 	if (input[inputlen-1] != '\0') {
+ 	    *reply = "User name not nul-terminated";
+ 	    return ACTE_FAIL;
+ 	}
+ 	strcpy(kstate->user, input+8);
+ 
+ 	/* Check kerberos identity can log in as user */
+ 	if (krb_get_lrealm(realm,1)) {
+ 	    *reply = "Can't find local Kerberos realm";
+ 	    return ACTE_FAIL;
+ 	}
+ 	if (kstate->authproc) {
+ 	    strcpy(clientname, kstate->kdata.pname);
+ 	    if (kstate->kdata.pinst[0]) {
+ 		strcat(clientname, ".");
+ 		strcat(clientname, kstate->kdata.pinst);
+ 	    }
+ 	    if (kstate->kdata.prealm[0]) {
+ 		strcat(clientname, "@");
+ 		strcat(clientname, kstate->kdata.prealm);
+ 	    }
+ 	    if (kstate->authproc(kstate->user, clientname, reply) != 0) {
+ 		return ACTE_FAIL;
+ 	    }
+ 	}
+ 	else {
+ 	    if (strcmp(kstate->kdata.pname, kstate->user) != 0 ||
+ 		kstate->kdata.pinst[0] ||
+ 		strcmp(kstate->kdata.prealm, realm) != 0) {
+ 		*reply = "Kerberos ID does not match user name";
+ 		return ACTE_FAIL;
+ 	    }
+ 	}
+ 
+ 	return ACTE_DONE;
+ 
+     default:
+ 	*reply = "Internal error: invalid state in krb_server_auth";
+ 	return ACTE_FAIL;
+     }
+ }
+ 
+ /* Exported definition of server-side authentication mechanism */
+ struct acte_server krb_acte_server = {
+     "KERBEROS_V4",
+     krb_server_start,
+     krb_server_auth,
+     krb_query_state,
+     krb_free_state
+ };
+ 
+ /*
+  * Apply integrity protection to the 'inputlen' bytes of data at 'input',
+  * using the state in 'state', placing the output data and length in the
+  * buffers pointed to by 'output' and 'outputlen' respectively.
+  */
+ static char *krb_en_integrity(state, input, inputlen, output, outputlen)
+ void *state;
+ char *input;
+ int inputlen;
+ char *output;
+ int *outputlen;
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+ 
+     *outputlen = krb_mk_safe(input, output, inputlen, kstate->session,
+ 			     &kstate->localaddr, &kstate->remoteaddr);
+     return 0;
+ }
+ 
+ /*
+  * Decode integrity protection on the 'inputlen' bytes of data at
+  * 'input', using the state in 'state', placing a pointer to the
+  * output data and length in the buffers pointed to by 'output' and
+  * 'outputlen' respectively.
+  */
+ static char *krb_de_integrity(state, input, inputlen, output, outputlen)
+ void *state;
+ char *input;
+ int inputlen;
+ char **output;
+ int *outputlen;
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+     int code;
+     MSG_DAT m_data;
+ 
+     code = krb_rd_safe(input, inputlen, kstate->session,
+ 		       &kstate->remoteaddr, &kstate->localaddr, &m_data);
+     if (code) return krb_err_txt[code];
+     if (m_data.time_sec < kstate->prot_time_sec ||
+ 	(m_data.time_sec == kstate->prot_time_sec &&
+ 	 m_data.time_5ms < kstate->prot_time_5ms)) {
+ 	return krb_err_txt[RD_AP_TIME];
+     }
+     kstate->prot_time_sec = m_data.time_sec;
+     kstate->prot_time_5ms = m_data.time_5ms;
+ 
+     *output = m_data.app_data;
+     *outputlen = m_data.app_length;
+     return 0;
+ }
+ 
+ /*
+  * Apply privacy protection to the 'inputlen' bytes of data at 'input',
+  * using the state in 'state', placing the output data and length in the
+  * buffers pointed to by 'output' and 'outputlen' respectively.
+  */
+ static char *krb_en_privacy(state, input, inputlen, output, outputlen)
+ void *state;
+ char *input;
+ int inputlen;
+ char *output;
+ int *outputlen;
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+ 
+     *outputlen = krb_mk_priv(input, output, inputlen, kstate->schedule,
+ 			     kstate->session, &kstate->localaddr,
+ 			     &kstate->remoteaddr);
+     return 0;
+ }
+ 
+ /*
+  * Decode privacy protection on the 'inputlen' bytes of data at
+  * 'input', using the state in 'state', placing a pointer to the
+  * output data and length in the buffers pointed to by 'output' and
+  * 'outputlen' respectively.
+  */
+ static char *krb_de_privacy(state, input, inputlen, output, outputlen)
+ void *state;
+ char *input;
+ int inputlen;
+ char **output;
+ int *outputlen;
+ {
+     struct krb_state *kstate = (struct krb_state *)state;
+     int code;
+     MSG_DAT m_data;
+ 
+     code = krb_rd_priv(input, inputlen, kstate->schedule, kstate->session,
+ 		       &kstate->remoteaddr, &kstate->localaddr, &m_data);
+     if (code) return krb_err_txt[code];
+     if (m_data.time_sec < kstate->prot_time_sec ||
+ 	(m_data.time_sec == kstate->prot_time_sec &&
+ 	 m_data.time_5ms < kstate->prot_time_5ms)) {
+ 	return krb_err_txt[RD_AP_TIME];
+     }
+     kstate->prot_time_sec = m_data.time_sec;
+     kstate->prot_time_5ms = m_data.time_5ms;
+ 
+     *output = m_data.app_data;
+     *outputlen = m_data.app_length;
+     return 0;
+ }
+ 
+ static afs_string_to_key();
+ 
+ /*
+  * Kerberos set srvtab filename
+  * Accepts: name of srvtab file to use in reading authenticators
+  */
+ int kerberos_set_srvtab(fname)
+ char *fname;
+ {
+     srvtab = fname;
+     return 0;
+ }
+ 
+ static use_key(user, instance, realm, key, returned_key)
+ char *user;
+ char *instance;
+ char *realm;
+ des_cblock key;
+ des_cblock returned_key;
+ {
+     memcpy (returned_key, key, sizeof(des_cblock));
+     return 0;
+ }
+ 
+ /*
+  * Securely verify the plaintext password 'passwd' for user 'user' against
+  * the Kerberos database.  Returns 1 for success, 0 for failure.  On failure,
+  * 'reply' is filled in with a pointer to the reason.
+  */
+ int kerberos_verify_password(user, passwd, reply)
+ char *user;
+ char *passwd;
+ char **reply;
+ {
+     int result;
+     des_cblock key;
+     char tfname[40];
+     char realm[REALM_SZ];
+     char cell[REALM_SZ];
+     char hostname[MAXHOSTNAMELEN+1];
+     char phost[MAXHOSTNAMELEN+1];
+     KTEXT_ST authent;
+     char instance[INST_SZ];
+     AUTH_DAT kdata;
+ 
+     if (krb_get_lrealm(realm,1)) return 0;
+ 
+     sprintf(tfname, "/tmp/tkt_imapd_%d", getpid());
+     krb_set_tkt_string(tfname);
+ 
+     /* First try Kerberos string-to-key */
+     des_string_to_key(passwd, key);
+     
+     result = krb_get_in_tkt(user, "", realm,
+ 			    "krbtgt", realm, 1, use_key, NULL, key);
+ 
+     if (result == INTK_BADPW) {
+ 	/* Now try andrew string-to-key */
+ 	strcpy(cell, realm);
+ 	lcase(cell);
+ 	afs_string_to_key(passwd, key, cell);
+     
+ 	result = krb_get_in_tkt(user, "", realm,
+ 				"krbtgt", realm, 1, use_key, NULL, key);
+     }
+ 
+     memset(key, 0, sizeof(key));
+ 
+     if (result != 0) {
+ 	dest_tkt();
+ 	*reply = krb_err_txt[result];
+ 	return 0;
+     }
+ 
+     /* Check validity of returned ticket */
+     gethostname(hostname, sizeof(hostname));
+     strcpy(phost, krb_get_phost(hostname));
+     result = krb_mk_req(&authent, "imap", phost, realm, 0);
+     if (result != 0) {
+ 	memset(&authent, 0, sizeof(authent));
+ 	dest_tkt();
+ 	*reply = krb_err_txt[result];
+ 	return 0;
+     }
+     strcpy(instance, "*");
+     result = krb_rd_req(&authent, "imap", instance, 0L, &kdata, srvtab);
+     memset(&authent, 0, sizeof(authent));
+     memset(kdata.session, 0, sizeof(kdata.session));
+     if (result != 0 || strcmp(kdata.pname, user) != 0 || kdata.pinst[0] ||
+ 	strcmp(kdata.prealm, realm) != 0) {
+ 	if (result != 0) {
+ 	    *reply = krb_err_txt[result];
+ 	}
+ 	else {
+ 	    *reply = "Kerberos ID does not match user name";
+ 	}
+ 	result = 0;
+     }
+     else result = 1;
+ 
+     dest_tkt();
+     return result;
+ }
+ 
+ /* andrewstk.c -- afs string to key function
+  *
+  * Code taken from AuthMan from University of Michigan
+  */
+ 
+ /* forward declarations */
+ static afs_transarc_StringToKey();
+ static afs_cmu_StringToKey();
+ 
+ extern char *crypt();
+ 
+ /* This defines the Andrew string_to_key function.  It accepts a password
+  * string as input and converts its via a one-way encryption algorithm to a DES
+  * encryption key.  It is compatible with the original Andrew authentication
+  * service password database.
+  */
+ 
+ static
+ afs_cmu_StringToKey (str, cell, key)
+   char          *str;
+   char          *cell;                  /* cell for password */
+   des_cblock key;
+ {   char  password[8+1];                /* crypt is limited to 8 chars anyway */
+     int   i;
+     int   passlen;
+ 
+     memset(key, 0, sizeof(des_cblock));
+     memset((void *)password, 0, sizeof(password));
+ 
+     strncpy (password, cell, 8);
+     passlen = strlen (str);
+     if (passlen > 8) passlen = 8;
+ 
+     for (i=0; i<passlen; i++)
+         password[i] = str[i] ^ cell[i];
+ 
+     for (i=0;i<8;i++)
+         if (password[i] == '\0') password[i] = 'X';
+ 
+     /* crypt only considers the first 8 characters of password but for some
+        reason returns eleven characters of result (plus the two salt chars). */
+     strncpy((void *)key, crypt(password, "p1") + 2, sizeof(des_cblock));
+ 
+     /* parity is inserted into the LSB so leftshift each byte up one bit.  This
+        allows ascii characters with a zero MSB to retain as much significance
+        as possible. */
+     {   char *keybytes = (char *)key;
+         unsigned int temp;
+ 
+         for (i = 0; i < 8; i++) {
+             temp = (unsigned int) keybytes[i];
+             keybytes[i] = (unsigned char) (temp << 1);
+         }
+     }
+     des_fixup_key_parity (key);
+ }
+ 
+ static
+ afs_transarc_StringToKey (str, cell, key)
+   char          *str;
+   char          *cell;                  /* cell for password */
+   des_cblock *key;
+ {   des_key_schedule schedule;
+     char temp_key[8];
+     char ivec[8];
+     char password[BUFSIZ];
+     int  passlen;
+ 
+     strncpy (password, str, sizeof(password));
+     if ((passlen = strlen (password)) < sizeof(password)-1)
+         strncat (password, cell, sizeof(password)-passlen);
+     if ((passlen = strlen(password)) > sizeof(password)) passlen = sizeof(password);
+ 
+     memcpy (ivec, "kerberos", 8);
+     memcpy (temp_key, "kerberos", 8);
+     des_fixup_key_parity ((void *)temp_key);
+     des_key_sched (temp_key, schedule);
+     des_cbc_cksum (password, ivec, passlen, schedule, ivec);
+ 
+     memcpy (temp_key, ivec, 8);
+     des_fixup_key_parity ((void *)temp_key);
+     des_key_sched (temp_key, schedule);
+     des_cbc_cksum (password, (void *)key, passlen, schedule, ivec);
+ 
+     des_fixup_key_parity (key);
+ }
+ 
+ static afs_string_to_key(str, key, cell)
+   char          *str;
+   des_cblock	*key;
+   char          *cell;                  /* cell for password */
+ {
+ 	if (strlen(str) > 8)
+ 		afs_transarc_StringToKey (str, cell, key);
+ 	else
+ 		afs_cmu_StringToKey (str, cell, key);
+ }
+ 
diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/env.h ./ANSI/c-client/env.h
*** /usr/tmp/pine3.90/imap/ANSI/c-client/env.h	Sun Jun 26 20:22:32 1994
--- ./ANSI/c-client/env.h	Sun Aug 28 18:10:01 1994
***************
*** 37,42 ****
--- 37,44 ----
  
  void rfc822_date (char *date);
  long server_login (char *user,char *pass,char **home,int argc,char *argv[]);
+ int server_authproc(char *user, char *auth_identity, char **reply);
+ int server_authenticate(char *user,char **home,int argc,char *argv[]);
  char *mylocalhost (void);
  char *myhomedir (void);
  char *mailboxfile (char *dst,char *name);
diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/imap2.c ./ANSI/c-client/imap2.c
*** /usr/tmp/pine3.90/imap/ANSI/c-client/imap2.c	Tue Aug 16 03:22:43 1994
--- ./ANSI/c-client/imap2.c	Sun Aug 28 18:13:34 1994
***************
*** 48,53 ****
--- 48,58 ----
  #include "osdep.h"
  #include "imap2.h"
  #include "misc.h"
+ #include "rfc822.h"
+ #include "acte.h"
+ 
+ extern struct acte_client *login_acte_client[];
+ 
  
  /* Driver dispatch used by MAIL */
  
***************
*** 398,403 ****
--- 403,412 ----
  {
    long i,j;
    char c[2],usrnam[MAILTMPLEN],pwd[MAILTMPLEN],tmp[MAILTMPLEN];
+   struct acte_client **mech;
+   void *state;
+   int r, gotdata;
+   char *in, *out;
    NETMBX mb;
    char *s;
    void *tstream;
***************
*** 472,479 ****
      }
  
      if (LOCAL && LOCAL->tcpstream && !strcmp (reply->key,"OK")) {
  				/* only so many tries to login */
!       for (i = 0; i < map_maxlogintrials; ++i) {
  	*pwd = 0;		/* get password */
  				/* if caller wanted anonymous access */
  	if ((mb.anoflag || stream->anonymous) && !i) {
--- 481,547 ----
      }
  
      if (LOCAL && LOCAL->tcpstream && !strcmp (reply->key,"OK")) {
+       for (mech = login_acte_client; *mech; mech++) {
+         if (mb.anoflag || stream->anonymous) continue;
+         if ((*mech)->start(tcp_host (LOCAL->tcpstream), 0,
+                            ACTE_PROT_NONE, 0, 0, 0, &state)) continue;
+         mail_lock(stream);
+         sprintf(LOCAL->tmp, "auth AUTHENTICATE %s", (*mech)->auth_type);
+         r = gotdata = 0;
+         for (;;) {
+             if (stream->debug) mm_dlog (LOCAL->tmp);
+             strcat(LOCAL->tmp, "\015\012");
+             if (imap_soutr(stream, "auth", LOCAL->tmp)) {
+                 (*mech)->free_state(state);
+                 map_close(stream);
+                 return NIL;
+             }
+             reply = imap_reply(stream, "auth");
+             if (strcmp(reply->tag, "+")) break;
+             in = rfc822_base64(reply->text, strlen(reply->text), &i);
+             if (!in ||
+                 (r = (*mech)->auth(state, i, in, &j, &out)) == ACTE_FAIL) {
+                 if (in) fs_give((void **) &in);
+                 /* Force a BAD */
+                 strcpy(LOCAL->tmp, "*\015\012");
+             }
+             else {
+                 if (i) gotdata = 1;
+                 fs_give((void **) &in);
+                 in = rfc822_binary(out, j, &i);
+                 /* Copy base64 to output buffer, removing line breaks */
+                 s = in;
+                 out = LOCAL->tmp;
+                 while (*s) {
+                     if (*s != '\012' && *s != '\015') *out++ = *s;
+                     s++;
+                 }
+                 *out = '\0';
+                 fs_give((void **) &in);
+             }
+         }
+         mail_unlock(stream);
+         if (!strcmp(reply->key, "OK")) {
+             if (r != ACTE_DONE) {
+                 mm_log("Server ended authentication exchange too early",
+                        ERROR);
+                 (*mech)->free_state(state);
+                 map_close(stream);
+                 return NIL;
+             }
+             (*mech)->free_state(state);
+             break;
+         }
+         (*mech)->free_state(state);
+         if (gotdata) {
+             /* Stop searching auth mechanisms */
+             map_close(stream);
+             return NIL;
+         }
+       }
+ 
  				/* only so many tries to login */
!       for (i = 0; *mech == 0 && i < map_maxlogintrials; ++i) {
  	*pwd = 0;		/* get password */
  				/* if caller wanted anonymous access */
  	if ((mb.anoflag || stream->anonymous) && !i) {
diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/log_krb.c ./ANSI/c-client/log_krb.c
*** /usr/tmp/pine3.90/imap/ANSI/c-client/log_krb.c	Sun Aug 28 19:02:33 1994
--- ./ANSI/c-client/log_krb.c	Sun Aug 28 18:07:37 1994
***************
*** 0 ****
--- 1,116 ----
+ /*
+  * Program:   Kerberos login
+  *
+  * Author:    Mark Crispin
+  *            Networks and Distributed Computing
+  *            Computing & Communications
+  *            University of Washington
+  *            Administration Building, AG-44
+  *            Seattle, WA  98195
+  *            Internet: MRC@CAC.Washington.EDU
+  *
+  * Date:      1 August 1988
+  * Last Edited:       3 January 1994
+  *
+  * Copyright 1994 by the University of Washington.
+  *
+  *  Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose and without fee is hereby granted, provided
+  * that the above copyright notice appears in all copies and that both the
+  * above copyright notice and this permission notice appear in supporting
+  * documentation, and that the name of the University of Washington not be
+  * used in advertising or publicity pertaining to distribution of the software
+  * without specific, written prior permission.  This software is made available
+  * "as is", and
+  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  *
+  */
+ 
+ #include "acte_krb.c"
+ 
+ struct acte_client *login_acte_client[] = {
+     &krb_acte_client,
+     NULL
+ };
+ 
+ struct acte_server *login_acte_server[] = {
+     &krb_acte_server,
+     NULL
+ };
+ 
+ 
+  
+ /* Server log in
+  * Accepts: user name string
+  *        password string
+  *        optional place to return home directory
+  * Returns: T if password validated, NIL otherwise
+  */
+ 
+ long server_login (char *user,char *pass,char **home,int argc,char *argv[])
+ {
+   struct passwd *pw = getpwnam (lcase (user));
+                               /* no entry for this user or root */
+   char *err;
+   if (!(pw && pw->pw_uid)) return NIL;
+                               /* validate user/password */
+   if (!kerberos_verify_password (user,pass,&err)) return NIL;
+   setgid (pw->pw_gid);                /* all OK, login in as that user */
+   initgroups (user,pw->pw_gid);       /* initialize groups */
+   setuid (pw->pw_uid);
+                               /* note home directory */
+   if (home) *home = cpystr (pw->pw_dir);
+   return T;
+ }
+ 
+ /* Server AUTHENTICATE authorization procedure
+  * Accepts: user name string
+  *          authenticion identity
+  *          buffer to store pointer to static error string
+  * Returns: zero if authorized, non-zero if not authorized
+  */
+ int
+ server_authproc(char *user,char *auth_identity,char **reply)
+ {
+   struct passwd *pw;
+ 
+   lcase(user);
+   if (!strcmp(user, auth_identity)) {
+       *reply = "Not authorized";
+       return 1;
+   }
+   pw = getpwnam (lcase (user));
+                               /* no entry for this user or root */
+   if (!(pw && pw->pw_uid)) {
+       *reply = "invalid user";
+       return NIL;
+   }
+   return 0;
+ }
+     
+ /* Server complete authentication
+  * Accepts: user name string
+  *        optional place to return home directory
+  * Returns: T if password validated, NIL otherwise
+  */
+ 
+ int server_authenticate (char *user,char **home,int argc,char *argv[])
+ {
+   struct passwd *pw = getpwnam (lcase (user));
+                               /* no entry for this user or root */
+   if (!(pw && pw->pw_uid)) return NIL;
+                               /* validate user/password */
+   setgid (pw->pw_gid);                /* all OK, login in as that user */
+   initgroups (user,pw->pw_gid);       /* initialize groups */
+   setuid (pw->pw_uid);
+                               /* note home directory */
+   if (home) *home = cpystr (pw->pw_dir);
+   return T;
+ }
diff -cr /usr/tmp/pine3.90/imap/ANSI/c-client/log_std.c ./ANSI/c-client/log_std.c
*** /usr/tmp/pine3.90/imap/ANSI/c-client/log_std.c	Fri Apr 15 02:06:20 1994
--- ./ANSI/c-client/log_std.c	Sun Aug 28 18:07:38 1994
***************
*** 32,37 ****
--- 32,46 ----
   * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   *
   */
+ 
+ struct acte_client *login_acte_client[] = {
+     NULL
+ };
+ 
+ struct acte_server *login_acte_server[] = {
+     NULL
+ };
+ 
   
  /* Server log in
   * Accepts: user name string
***************
*** 56,59 ****
--- 65,91 ----
  				/* note home directory */
    if (home) *home = cpystr (pw->pw_dir);
    return T;
+ }
+ 
+ /* Server AUTHENTICATE authorization procedure
+  * Accepts: user name string
+  *          authenticion identity
+  *          buffer to store pointer to static error string
+  * Returns: zero if authorized, non-zero if not authorized
+  */
+ int
+ server_authproc(char *user,char *auth_identity,char **reply)
+ {
+   return 1;
+ }
+     
+ /* Server complete authentication
+  * Accepts: user name string
+  *        optional place to return home directory
+  * Returns: T if password validated, NIL otherwise
+  */
+ 
+ int server_authenticate (char *user,char **home,int argc,char *argv[])
+ {
+   return NIL;
  }
diff -cr /usr/tmp/pine3.90/imap/ANSI/imapd/imapd.c ./ANSI/imapd/imapd.c
*** /usr/tmp/pine3.90/imap/ANSI/imapd/imapd.c	Sun Aug 21 16:18:32 1994
--- ./ANSI/imapd/imapd.c	Sun Aug 28 18:07:39 1994
***************
*** 47,53 ****
--- 47,55 ----
  #include <sys/file.h>
  #include <sys/stat.h>
  #include "misc.h"
+ #include "acte.h"
  
+ extern struct acte_server *login_acte_server[];
  
  /* Daemon files */
  #define ALERTFILE "/etc/imapd.alert"
***************
*** 251,256 ****
--- 253,332 ----
  	    else syslog (LOG_INFO,"Login failure user=%s host=%s",
  			 user,tcp_clienthost (tmp));
  	  }
+ 	}
+ 	else if (!strcmp (cmd,"AUTHENTICATE")) {
+ 	    char *cmdbufend;
+ 	    struct acte_server **mech;
+ 	    void *m_state;
+ 	    char *in, *out, *s, *p;
+ 	    int inlen, outlen, slen;
+ 	    int r;
+ 	    char *err;
+ 	    char *u;
+ 	    int protlevel, (*encodefunc)(), (*decodefunc)(), maxplain;
+ 	    struct passwd *pwd;
+                               /* one argument */
+ 	    response = 0;
+ 	    if (arg) cmdbufend = arg + strlen(arg);
+ 	    if (!(in = snarf (&arg))) response = misarg;
+ 	    else if (arg) response = badarg;
+                               /* see if mechanism name is OK */
+ 	    if (!response) {
+ 		ucase(in);
+ 		for (mech = login_acte_server; *mech; mech++) {
+ 		    if (!strcmp(in, (*mech)->auth_type)) break;
+ 		}
+ 		if (!*mech) response = "%s NO Unrecognized authentication mechanism\015\012";
+ 	    }
+ 
+ 	    if (!response) {
+ 		r = (*mech)->start(server_authproc, ACTE_PROT_NONE, 0, 0, 0,
+ 				   &outlen, &out, &m_state, &err);
+ 		if (r) response = "%s NO Authentication failed";
+ 	    }
+ 	    if (!response) {
+ 		while (r == 0) {
+ 		    s = rfc822_binary(out, outlen, &slen);
+ 		    putchar('+');
+ 		    putchar(' ');
+ 		    for (p = s; *p; p++) {
+ 			if (*p != '\015' && *p != '\012') putchar(*p);
+ 		    }
+ 		    putchar('\015');
+ 		    putchar('\012');
+ 		    fs_give((void **)&s);
+ 		    fflush(stdout);
+ 		    alarm (TIMEOUT); /* get a reply under timeout */
+ 		    if (!fgets (cmdbufend,TMPLEN-1-(cmdbufend-cmdbuf),stdin)) _exit (1);
+ 		    alarm (0);    /* make sure timeout disabled */
+ 		    if (*cmdbufend == '*' ||
+ 			!(in = rfc822_base64(cmdbufend, strlen(cmdbufend), &inlen))) {
+ 			response = "%s BAD Invalid authentication reply\015\012";
+ 			(*mech)->free_state(m_state);
+ 			break;
+ 		    }
+ 		    r = (*mech)->auth(m_state, inlen, in, &outlen, &out, &err);
+ 		    fs_give((void **)&in);
+ 		}
+ 		if (r == ACTE_FAIL) {
+ 		    (*mech)->free_state(m_state);
+ 		    response = "%s NO Authentication failed\015\012";
+ 		}
+ 	    }
+ 	    if (!response) {
+ 		(*mech)->query_state(m_state, &u, &protlevel, &encodefunc,
+ 				     &decodefunc, &maxplain);
+ 		fs_give ((void **) &user);
+ 		user = cpystr(u);
+ 		(*mech)->free_state(m_state);
+ 		if (server_authenticate(user,&home,argc,argv)) {
+ 		    state = SELECT;
+ 		    response = win;
+ 		}
+ 		else {
+ 		    response = "%s NO Authentication failed\015\012";
+ 		}
+ 	    }
  	}
  	else response = "%s BAD Command unrecognized/login please: %s\015\012";
  	break;
