From: Unknown <> Date: Wed, 30 Aug 2006 23:37:16 +0000 (+0000) Subject: add files for 2006-08-30T23:37:16Z X-Git-Tag: imap-2007a1~412 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4069b133469902acdab683231bcaec4bb49c5e9a;p=uw-imap add files for 2006-08-30T23:37:16Z --- diff --git a/src/c-client/auth_gss.c b/src/c-client/auth_gss.c new file mode 100644 index 0000000..26bf9e5 --- /dev/null +++ b/src/c-client/auth_gss.c @@ -0,0 +1,423 @@ +/* ======================================================================== + * Copyright 1988-2006 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * + * ======================================================================== + */ + +/* + * Program: GSSAPI authenticator + * + * 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: 12 January 1998 + * Last Edited: 30 August 2006 + */ + + +long auth_gssapi_valid (void); +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder, + char *service,NETMBX *mb,void *stream, + unsigned long *trial,char *user); +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal, + authrespond_t responder,char *service,NETMBX *mb, + void *stream,char *user,kinit_t ki); +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]); + + +AUTHENTICATOR auth_gss = { + AU_SECURE | AU_AUTHUSER, /* secure authenticator */ + "GSSAPI", /* authenticator name */ + auth_gssapi_valid, /* check if valid */ + auth_gssapi_client, /* client method */ + auth_gssapi_server, /* server method */ + NIL /* next authenticator */ +}; + +#define AUTH_GSSAPI_P_NONE 1 +#define AUTH_GSSAPI_P_INTEGRITY 2 +#define AUTH_GSSAPI_P_PRIVACY 4 + +#define AUTH_GSSAPI_C_MAXSIZE 8192 + +#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y) + +/* Check if GSSAPI valid on this system + * Returns: T if valid, NIL otherwise + */ + +long auth_gssapi_valid (void) +{ + char tmp[MAILTMPLEN]; + OM_uint32 smn; + gss_buffer_desc buf; + gss_name_t name; + /* make service name */ + sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), + mylocalhost ()); + buf.length = strlen (buf.value = tmp); + /* see if can build a name */ + if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) != + GSS_S_COMPLETE) return NIL; + /* remove server method if no keytab */ + if (!kerberos_server_valid ()) auth_gss.server = NIL; + gss_release_name (&smn,&name);/* finished with name */ + return LONGT; +} + +/* Client authenticator + * Accepts: challenger function + * responder function + * SASL service name + * parsed network mailbox structure + * stream argument for functions + * pointer to current trial count + * returned user name + * Returns: T if success, NIL otherwise, number of trials incremented if retry + */ + +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder, + char *service,NETMBX *mb,void *stream, + unsigned long *trial,char *user) +{ + gss_buffer_desc chal; + kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL); + long ret = NIL; + *trial = 65535; /* never retry */ + /* get initial (empty) challenge */ + if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) { + if (chal.length) { /* abort if challenge non-empty */ + mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN); + (*responder) (stream,NIL,0); + ret = LONGT; /* will get a BAD response back */ + } + else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) { + mm_log ("Can't use Kerberos: invalid /authuser",WARN); + (*responder) (stream,NIL,0); + ret = LONGT; /* will get a BAD response back */ + } + else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, + stream,user,ki); + } + return ret; +} + +/* Client authenticator worker function + * Accepts: challenger function + * responder function + * SASL service name + * parsed network mailbox structure + * stream argument for functions + * returned user name + * kinit function pointer if should retry with kinit + * Returns: T if success, NIL otherwise + */ + +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal, + authrespond_t responder,char *service,NETMBX *mb, + void *stream,char *user,kinit_t ki) +{ + char tmp[MAILTMPLEN]; + OM_uint32 smj,smn,dsmj,dsmn; + OM_uint32 mctx = 0; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_buffer_desc resp,buf; + long i; + int conf; + gss_qop_t qop; + gss_name_t crname = NIL; + blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); + void *data; + long ret = NIL; + sprintf (tmp,"%s@%s",service,mb->host); + buf.length = strlen (buf.value = tmp); + /* get service name */ + if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) != + GSS_S_COMPLETE) { + mm_log ("Can't import Kerberos service name",WARN); + (*responder) (stream,NIL,0); + } + else { + data = (*bn) (BLOCK_SENSITIVE,NIL); + /* negotiate with KDC */ + smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL, + GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG | + GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS, + GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL); + (*bn) (BLOCK_NONSENSITIVE,data); + + /* while continuation needed */ + while (smj == GSS_S_CONTINUE_NEEDED) { + if (chal.value) fs_give ((void **) &chal.value); + /* send response, get next challenge */ + i = (*responder) (stream,resp.value,resp.length) && + (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)); + gss_release_buffer (&smn,&resp); + if (i) { /* negotiate continuation with KDC */ + data = (*bn) (BLOCK_SENSITIVE,NIL); + switch (smj = /* make sure continuation going OK */ + gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx, + crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG | + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0, + GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL, + &resp,NIL,NIL)) { + case GSS_S_CONTINUE_NEEDED: + case GSS_S_COMPLETE: + break; + default: /* error, don't need context any more */ + gss_delete_sec_context (&smn,&ctx,NIL); + } + (*bn) (BLOCK_NONSENSITIVE,data); + } + else { /* error in continuation */ + mm_log ("Error in negotiating Kerberos continuation",WARN); + (*responder) (stream,NIL,0); + /* don't need context any more */ + gss_delete_sec_context (&smn,&ctx,NIL); + break; + } + } + + switch (smj) { /* done - deal with final condition */ + case GSS_S_COMPLETE: + if (chal.value) fs_give ((void **) &chal.value); + /* get prot mechanisms and max size */ + if ((*responder) (stream,resp.value ? resp.value : "",resp.length) && + (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&& + (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) && + (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){ + /* make copy of flags and length */ + memcpy (tmp,resp.value,4); + gss_release_buffer (&smn,&resp); + /* no session protection */ + tmp[0] = AUTH_GSSAPI_P_NONE; + /* install user name */ + strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ())); + buf.value = tmp; buf.length = strlen (user) + 4; + /* successful negotiation */ + switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) { + case GSS_S_COMPLETE: + if ((*responder) (stream,resp.value,resp.length)) ret = T; + gss_release_buffer (&smn,&resp); + break; + default: + do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + mctx = 0; + case GSS_S_CONTINUE_NEEDED: + sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value); + mm_log (tmp,WARN); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + case GSS_S_CONTINUE_NEEDED: + sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value); + mm_log (tmp,WARN); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + (*responder) (stream,NIL,0); + } + } + /* flush final challenge */ + if (chal.value) fs_give ((void **) &chal.value); + /* don't need context any more */ + gss_delete_sec_context (&smn,&ctx,NIL); + break; + + case GSS_S_CREDENTIALS_EXPIRED: + if (chal.value) fs_give ((void **) &chal.value); + /* retry if application kinits */ + if (ki && (*ki) (mb->host,"Kerberos credentials expired")) + ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, + stream,user,NIL); + else { /* application can't kinit */ + sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s", + mb->host); + mm_log (tmp,WARN); + (*responder) (stream,NIL,0); + } + break; + case GSS_S_FAILURE: + if (chal.value) fs_give ((void **) &chal.value); + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: /* end of message, can kinit? */ + if (ki && kerberos_try_kinit (smn) && + (*ki) (mb->host,(char *) resp.value)) { + gss_release_buffer (&dsmn,&resp); + ret = auth_gssapi_client_work (challenger,chal,responder,service,mb, + stream,user,NIL); + break; /* done */ + } + else (*responder) (stream,NIL,0); + case GSS_S_CONTINUE_NEEDED: + sprintf (tmp,kerberos_try_kinit (smn) ? + "Kerberos error: %.80s (try running kinit) for %.80s" : + "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host); + mm_log (tmp,WARN); + gss_release_buffer (&dsmn,&resp); + } while (dsmj == GSS_S_CONTINUE_NEEDED); + break; + + default: /* miscellaneous errors */ + if (chal.value) fs_give ((void **) &chal.value); + do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + mctx = 0; + case GSS_S_CONTINUE_NEEDED: + sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value); + mm_log (tmp,WARN); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + case GSS_S_CONTINUE_NEEDED: + sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value); + mm_log (tmp,WARN); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + (*responder) (stream,NIL,0); + break; + } + /* finished with credentials name */ + if (crname) gss_release_name (&smn,&crname); + } + return ret; /* return status */ +} + +/* Server authenticator + * Accepts: responder function + * argument count + * argument vector + * Returns: authenticated user name or NIL + */ + +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]) +{ + char *ret = NIL; + char tmp[MAILTMPLEN]; + unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE); + int conf; + OM_uint32 smj,smn,dsmj,dsmn,flags; + OM_uint32 mctx = 0; + gss_name_t crname,name; + gss_OID mech; + gss_buffer_desc chal,resp,buf; + gss_cred_id_t crd; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_qop_t qop = GSS_C_QOP_DEFAULT; + /* make service name */ + sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL), + tcp_serverhost ()); + buf.length = strlen (buf.value = tmp); + /* acquire credentials */ + if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) == + GSS_S_COMPLETE) { + if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL)) + == GSS_S_COMPLETE) { + if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) { + do { /* negotiate authentication */ + smj = gss_accept_sec_context (&smn,&ctx,crd,&resp, + GSS_C_NO_CHANNEL_BINDINGS,&name,&mech, + &chal,&flags,NIL,NIL); + /* don't need response any more */ + fs_give ((void **) &resp.value); + switch (smj) { /* how did it go? */ + case GSS_S_COMPLETE: /* successful */ + case GSS_S_CONTINUE_NEEDED: + if (chal.value) { /* send challenge, get next response */ + resp.value = (*responder) (chal.value,chal.length, + (unsigned long *) &resp.length); + gss_release_buffer (&smn,&chal); + } + break; + } + } + while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED)); + + /* successful exchange? */ + if ((smj == GSS_S_COMPLETE) && + (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) { + /* send security and size */ + memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4); + tmp[0] = AUTH_GSSAPI_P_NONE; + if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){ + resp.value = (*responder) (chal.value,chal.length, + (unsigned long *) &resp.length); + gss_release_buffer (&smn,&chal); + if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) { + /* client request valid */ + if (chal.value && (chal.length > 4) && + (chal.length < (MAILTMPLEN - 1)) && + memcpy (tmp,chal.value,chal.length) && + (tmp[0] & AUTH_GSSAPI_P_NONE)) { + /* tie off authorization ID */ + tmp[chal.length] = '\0'; + ret = kerberos_login (tmp+4,buf.value,argc,argv); + } + /* done with user name */ + gss_release_buffer (&smn,&chal); + } + /* finished with response */ + fs_give ((void **) &resp.value); + } + /* don't need name buffer any more */ + gss_release_buffer (&smn,&buf); + } + /* don't need client name any more */ + gss_release_name (&smn,&name); + /* don't need context any more */ + if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL); + } + /* finished with credentials */ + gss_release_cred (&smn,&crd); + } + + else { /* can't acquire credentials! */ + if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE) + SERVER_LOG ("Failed to acquire credentials for %s",buf.value); + if (smj != GSS_S_FAILURE) do + switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + mctx = 0; + case GSS_S_CONTINUE_NEEDED: + SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE, + GSS_C_NO_OID,&mctx,&resp)) { + case GSS_S_COMPLETE: + case GSS_S_CONTINUE_NEEDED: + SERVER_LOG ("GSSAPI mechanism status: %s",resp.value); + gss_release_buffer (&dsmn,&resp); + } + while (dsmj == GSS_S_CONTINUE_NEEDED); + } + /* finished with credentials name */ + gss_release_name (&smn,&crname); + } + return ret; /* return status */ +}