home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / c-client / auth_gss.c next >
C/C++ Source or Header  |  1998-12-01  |  13KB  |  358 lines

  1. /*
  2.  * Program:    GSSAPI authenticator
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    12 January 1998
  13.  * Last Edited:    1 December 1998
  14.  *
  15.  * Copyright 1998 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. #define PROTOTYPE(x) x
  37. #define KRB5_PROVIDE_PROTOTYPES
  38. #include <gssapi/gssapi_generic.h>
  39. #include <gssapi/gssapi_krb5.h>
  40. #include <krb5.h>
  41.  
  42. long auth_gssapi_valid (void);
  43. long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
  44.              NETMBX *mb,void *stream,unsigned long *trial,
  45.              char *user);
  46. char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]);
  47.  
  48. AUTHENTICATOR auth_gss = {
  49.   T,                /* secure authenticator */
  50.   "GSSAPI",            /* authenticator name */
  51.   auth_gssapi_valid,        /* check if valid */
  52.   auth_gssapi_client,        /* client method */
  53.   auth_gssapi_server,        /* server method */
  54.   NIL                /* next authenticator */
  55. };
  56.  
  57. #define AUTH_GSSAPI_P_NONE 1
  58. #define AUTH_GSSAPI_P_INTEGRITY 2
  59. #define AUTH_GSSAPI_P_PRIVACY 4
  60.  
  61. #define AUTH_GSSAPI_C_MAXSIZE 8192
  62.  
  63. #define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
  64.  
  65. extern char *krb5_defkeyname;    /* sneaky way to get this name */
  66.  
  67. /* Check if GSSAPI valid on this system
  68.  * Returns: T if valid, NIL otherwise
  69.  */
  70.  
  71. long auth_gssapi_valid (void)
  72. {
  73.   char *s,tmp[MAILTMPLEN];
  74.   OM_uint32 min;
  75.   gss_buffer_desc buf;
  76.   gss_name_t name;
  77.   struct stat sbuf;
  78.   sprintf (tmp,"host@%s",mylocalhost ());
  79.   buf.length = strlen (buf.value = tmp) + 1;
  80.                 /* see if can build a name */
  81.   if (gss_import_name (&min,&buf,gss_nt_service_name,&name) != GSS_S_COMPLETE)
  82.     return NIL;            /* failed */
  83.   if ((s = strchr (krb5_defkeyname,':')) && stat (++s,&sbuf))
  84.     auth_gss.server = NIL;    /* can't do server if no keytab */
  85.   gss_release_name (&min,name);    /* finished with name */
  86.   return LONGT;
  87. }
  88.  
  89. /* Client authenticator
  90.  * Accepts: challenger function
  91.  *        responder function
  92.  *        parsed network mailbox structure
  93.  *        stream argument for functions
  94.  *        pointer to current trial count
  95.  *        returned user name
  96.  * Returns: T if success, NIL otherwise, number of trials incremented if retry
  97.  */
  98.  
  99. long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
  100.             NETMBX *mb,void *stream,unsigned long *trial,
  101.             char *user)
  102. {
  103.   long ret = NIL;
  104.   char tmp[MAILTMPLEN];
  105.   OM_uint32 maj,min,mmaj,mmin;
  106.   OM_uint32 mctx = 0;
  107.   gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
  108.   gss_buffer_desc chal,resp,buf;
  109.   gss_name_t crname = NIL;
  110.   long i;
  111.   int conf;
  112.   gss_qop_t qop;
  113.   *trial = 0;            /* never retry */
  114.   if ((chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) &&
  115.       !chal.length) {        /* get initial (empty) challenge */
  116.     sprintf (tmp,"%s@%s",mb->service,mb->host);
  117.     buf.length = strlen (buf.value = tmp) + 1;
  118.                 /* get service name */
  119.     if (gss_import_name(&min,&buf,gss_nt_service_name,&crname)!=GSS_S_COMPLETE)
  120.       (*responder) (stream,NIL,0);
  121.     else switch (maj =        /* get context */
  122.          gss_init_sec_context (&min,GSS_C_NO_CREDENTIAL,&ctx,
  123.                        crname,GSS_C_NO_OID,
  124.                        GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
  125.                        0,GSS_C_NO_CHANNEL_BINDINGS,
  126.                        GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL)) {
  127.     case GSS_S_CONTINUE_NEEDED:
  128.       do {            /* negotiate authentication */
  129.     if (chal.value) fs_give ((void **) &chal.value);
  130.                 /* send response */
  131.     i = (*responder) (stream,resp.value,resp.length);
  132.     gss_release_buffer (&min,&resp);
  133.       }
  134.       while (i &&        /* get next challenge */
  135.          (chal.value=(*challenger)(stream,(unsigned long *)&chal.length))&&
  136.          (maj = gss_init_sec_context (&min,GSS_C_NO_CREDENTIAL,&ctx,
  137.                       crname,GSS_C_NO_OID,
  138.                       GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,
  139.                       0,GSS_C_NO_CHANNEL_BINDINGS,&chal,
  140.                       NIL,&resp,NIL,NIL) ==
  141.           GSS_S_CONTINUE_NEEDED));
  142.  
  143.     case GSS_S_COMPLETE:
  144.       if (chal.value) {
  145.     fs_give ((void **) &chal.value);
  146.     if (maj != GSS_S_COMPLETE) (*responder) (stream,NIL,0);
  147.       }
  148.                 /* get prot mechanisms and max size */
  149.       if ((maj == GSS_S_COMPLETE) &&
  150.       (*responder) (stream,resp.value ? resp.value : "",resp.length) &&
  151.       (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
  152.       (gss_unwrap (&min,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
  153.       (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
  154.                 /* make copy of flags and length */
  155.     memcpy (tmp,resp.value,4);
  156.     gss_release_buffer (&min,&resp);
  157.                 /* no session protection */
  158.     tmp[0] = AUTH_GSSAPI_P_NONE;
  159.                 /* install user name */
  160.     strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
  161.     buf.value = tmp; buf.length = strlen (user) + 4;
  162.                 /* successful negotiation */
  163.     if (gss_wrap (&min,ctx,FALSE,qop,&buf,&conf,&resp) == GSS_S_COMPLETE) {
  164.       if ((*responder) (stream,resp.value,resp.length)) ret = T;
  165.       gss_release_buffer (&min,&resp);
  166.     }
  167.     else (*responder) (stream,NIL,0);
  168.       }
  169.                 /* flush final challenge */
  170.       if (chal.value) fs_give ((void **) &chal.value);
  171.                 /* don't need context any more */
  172.       gss_delete_sec_context (&min,&ctx,NIL);
  173.       break;
  174.  
  175.     case GSS_S_CREDENTIALS_EXPIRED:
  176.       if (chal.value) fs_give ((void **) &chal.value);
  177.       sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
  178.            mb->host);
  179.       mm_log (tmp,WARN);
  180.       (*responder) (stream,NIL,0);
  181.       break;
  182.     case GSS_S_FAILURE:
  183.       if (chal.value) fs_give ((void **) &chal.value);
  184.       if (min == (OM_uint32) KRB5_FCC_NOFILE) {
  185.     sprintf (tmp,"No credentials cache found (try running kinit) for %s",
  186.          mb->host);
  187.     mm_log (tmp,WARN);
  188.       }
  189.       else do switch (mmaj = gss_display_status (&mmin,min,GSS_C_MECH_CODE,
  190.                          GSS_C_NULL_OID,&mctx,&resp)) {
  191.       case GSS_S_COMPLETE:
  192.       case GSS_S_CONTINUE_NEEDED:
  193.     sprintf (tmp,"GSSAPI failure: %s",resp.value);
  194.     mm_log (tmp,WARN);
  195.     gss_release_buffer (&mmin,&resp);
  196.       }
  197.       while (mmaj == GSS_S_CONTINUE_NEEDED);
  198.       (*responder) (stream,NIL,0);
  199.       break;
  200.     default:            /* miscellaneous errors */
  201.       if (chal.value) fs_give ((void **) &chal.value);
  202.       do switch (mmaj = gss_display_status (&mmin,maj,GSS_C_GSS_CODE,
  203.                         GSS_C_NULL_OID,&mctx,&resp)) {
  204.       case GSS_S_COMPLETE:
  205.     mctx = 0;
  206.       case GSS_S_CONTINUE_NEEDED:
  207.     sprintf (tmp,"Unknown GSSAPI failure: %s",resp.value);
  208.     mm_log (tmp,WARN);
  209.     gss_release_buffer (&mmin,&resp);
  210.       }
  211.       while (mmaj == GSS_S_CONTINUE_NEEDED);
  212.       do switch (mmaj = gss_display_status (&mmin,min,GSS_C_MECH_CODE,
  213.                         GSS_C_NULL_OID,&mctx,&resp)) {
  214.       case GSS_S_COMPLETE:
  215.       case GSS_S_CONTINUE_NEEDED:
  216.     sprintf (tmp,"GSSAPI mechanism status: %s",resp.value);
  217.     mm_log (tmp,WARN);
  218.     gss_release_buffer (&mmin,&resp);
  219.       }
  220.       while (mmaj == GSS_S_CONTINUE_NEEDED);
  221.       (*responder) (stream,NIL,0);
  222.       break;
  223.     }
  224.                 /* finished with credentials name */
  225.     if (crname) gss_release_name (&min,crname);
  226.   }
  227.   return ret;            /* return status */
  228. }
  229.  
  230. /* Server authenticator
  231.  * Accepts: responder function
  232.  *        argument count
  233.  *        argument vector
  234.  * Returns: authenticated user name or NIL
  235.  */
  236.  
  237. char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[])
  238. {
  239.   char *ret = NIL;
  240.   char tmp[MAILTMPLEN];
  241.   unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
  242.   int conf;
  243.   OM_uint32 maj,min,mmaj,mmin,flags;
  244.   OM_uint32 mctx = 0;
  245.   gss_name_t crname,name;
  246.   gss_OID mech;
  247.   gss_buffer_desc chal,resp,buf;
  248.   gss_cred_id_t crd;
  249.   gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
  250.   gss_qop_t qop = GSS_C_QOP_DEFAULT;
  251.   krb5_context ktx;
  252.   krb5_principal prnc;
  253.                 /* make service name */
  254.   sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
  255.        tcp_serverhost ());
  256.   buf.length = strlen (buf.value = tmp) + 1;
  257.                 /* acquire credentials */
  258.   if ((gss_import_name (&min,&buf,gss_nt_service_name,&crname)) ==
  259.       GSS_S_COMPLETE) {
  260.     if ((maj = gss_acquire_cred (&min,crname,0,GSS_C_NULL_OID_SET,GSS_C_ACCEPT,
  261.                  &crd,NIL,NIL)) == GSS_S_COMPLETE) {
  262.       if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
  263.     do {            /* negotiate authentication */
  264.       maj = gss_accept_sec_context (&min,&ctx,crd,&resp,
  265.                     GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
  266.                     &chal,&flags,NIL,NIL);
  267.                 /* don't need response any more */
  268.       fs_give ((void **) &resp.value);
  269.       switch (maj) {    /* how did it go? */
  270.       case GSS_S_COMPLETE:    /* successful */
  271.       case GSS_S_CONTINUE_NEEDED:
  272.         if (chal.value) {    /* send challenge, get next response */
  273.           resp.value = (*responder) (chal.value,chal.length,
  274.                      (unsigned long *) &resp.length);
  275.           gss_release_buffer (&min,&chal);
  276.         }
  277.         break;
  278.       }
  279.     }
  280.     while (resp.value && resp.length && (maj == GSS_S_CONTINUE_NEEDED));
  281.  
  282.                 /* successful exchange? */
  283.     if ((maj == GSS_S_COMPLETE) &&
  284.         (gss_display_name (&min,name,&buf,&mech) == GSS_S_COMPLETE)) {
  285.                 /* send security and size */
  286.       memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
  287.       tmp[0] = AUTH_GSSAPI_P_NONE;
  288.       if (gss_wrap (&min,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
  289.         resp.value = (*responder) (chal.value,chal.length,
  290.                        (unsigned long *) &resp.length);
  291.         gss_release_buffer (&min,&chal);
  292.         if (gss_unwrap (&min,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
  293.           if (chal.value && (chal.length > 4) && (chal.length < MAILTMPLEN)
  294.           && (*((char *) chal.value) & AUTH_GSSAPI_P_NONE) &&
  295.           !krb5_init_context (&ktx)) {
  296.                 /* parse name as principal */
  297.         if (!krb5_parse_name (ktx,buf.value,&prnc)) {
  298.                 /* copy flags/size/user name */
  299.           memcpy (tmp,chal.value,chal.length);
  300.                 /* make sure user name tied off */
  301.           tmp[chal.length] = '\0';
  302.                 /* OK for this principal to log in as user? */
  303.           if ((krb5_kuserok (ktx,prnc,tmp+4) &&
  304.                authserver_login (tmp+4,argc,argv)) ||
  305.               (krb5_kuserok (ktx,prnc,lcase (tmp+4)) &&
  306.                authserver_login (tmp+4,argc,argv))) ret = myusername();
  307.                 /* done with principal */
  308.           krb5_free_principal (ktx,prnc);
  309.         }
  310.                 /* done with context */
  311.         krb5_free_context (ktx);
  312.           }
  313.                 /* done with user name */
  314.           gss_release_buffer (&min,&chal);
  315.         }
  316.                 /* finished with response */
  317.         fs_give ((void **) &resp.value);
  318.       }
  319.                 /* don't need name buffer any more */
  320.       gss_release_buffer (&min,&buf);
  321.     }
  322.                 /* don't need client name any more */
  323.     gss_release_name (&min,&name);
  324.                 /* don't need context any more */
  325.     if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&min,&ctx,NIL);
  326.       }
  327.                 /* finished with credentials */
  328.       gss_release_cred (&min,&crd);
  329.     }
  330.  
  331.     else {            /* can't acquire credentials! */
  332.       if (gss_display_name (&mmin,crname,&buf,&mech) == GSS_S_COMPLETE)
  333.     SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
  334.       if (maj != GSS_S_FAILURE) do
  335.     switch (mmaj = gss_display_status (&mmin,maj,GSS_C_GSS_CODE,
  336.                        GSS_C_NULL_OID,&mctx,&resp)) {
  337.     case GSS_S_COMPLETE:
  338.       mctx = 0;
  339.     case GSS_S_CONTINUE_NEEDED:
  340.       SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
  341.       gss_release_buffer (&mmin,&resp);
  342.     }
  343.       while (mmaj == GSS_S_CONTINUE_NEEDED);
  344.       do switch (mmaj = gss_display_status (&mmin,min,GSS_C_MECH_CODE,
  345.                         GSS_C_NULL_OID,&mctx,&resp)) {
  346.       case GSS_S_COMPLETE:
  347.       case GSS_S_CONTINUE_NEEDED:
  348.     SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
  349.     gss_release_buffer (&mmin,&resp);
  350.       }
  351.       while (mmaj == GSS_S_CONTINUE_NEEDED);
  352.     }
  353.                 /* finished with credentials name */
  354.     gss_release_name (&min,crname);
  355.   }
  356.   return ret;            /* return status */
  357. }
  358.