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 + #include + #include + #include + #include + #include + #include + + #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 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 #include #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; .