Home | History | Annotate | Download | only in mech
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * Copyright 2000 by the Massachusetts Institute of Technology.
      9  * All Rights Reserved.
     10  *
     11  * Export of this software from the United States of America may
     12  *   require a specific license from the United States Government.
     13  *   It is the responsibility of any person or organization contemplating
     14  *   export to obtain such a license before exporting.
     15  *
     16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     17  * distribute this software and its documentation for any purpose and
     18  * without fee is hereby granted, provided that the above copyright
     19  * notice appear in all copies and that both that copyright notice and
     20  * this permission notice appear in supporting documentation, and that
     21  * the name of M.I.T. not be used in advertising or publicity pertaining
     22  * to distribution of the software without specific, written prior
     23  * permission.  Furthermore if you modify this software you must label
     24  * your software as modified software and not distribute it in such a
     25  * fashion that it might be confused with the original M.I.T. software.
     26  * M.I.T. makes no representations about the suitability of
     27  * this software for any purpose.  It is provided "as is" without express
     28  * or implied warranty.
     29  *
     30  */
     31 /*
     32  * Copyright 1993 by OpenVision Technologies, Inc.
     33  *
     34  * Permission to use, copy, modify, distribute, and sell this software
     35  * and its documentation for any purpose is hereby granted without fee,
     36  * provided that the above copyright notice appears in all copies and
     37  * that both that copyright notice and this permission notice appear in
     38  * supporting documentation, and that the name of OpenVision not be used
     39  * in advertising or publicity pertaining to distribution of the software
     40  * without specific, written prior permission. OpenVision makes no
     41  * representations about the suitability of this software for any
     42  * purpose.  It is provided "as is" without express or implied warranty.
     43  *
     44  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     45  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     46  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     47  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
     48  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     49  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     50  * PERFORMANCE OF THIS SOFTWARE.
     51  */
     52 
     53 /*
     54  * Copyright (C) 1998 by the FundsXpress, INC.
     55  *
     56  * All rights reserved.
     57  *
     58  * Export of this software from the United States of America may require
     59  * a specific license from the United States Government.  It is the
     60  * responsibility of any person or organization contemplating export to
     61  * obtain such a license before exporting.
     62  *
     63  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     64  * distribute this software and its documentation for any purpose and
     65  * without fee is hereby granted, provided that the above copyright
     66  * notice appear in all copies and that both that copyright notice and
     67  * this permission notice appear in supporting documentation, and that
     68  * the name of FundsXpress. not be used in advertising or publicity pertaining
     69  * to distribution of the software without specific, written prior
     70  * permission.  FundsXpress makes no representations about the suitability of
     71  * this software for any purpose.  It is provided "as is" without express
     72  * or implied warranty.
     73  *
     74  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     75  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     76  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     77  */
     78 
     79 #include "k5-int.h"
     80 #include "gss_libinit.h"
     81 #include "gssapiP_krb5.h"
     82 #include "mglueP.h"
     83 #ifdef HAVE_STRING_H
     84 #include <string.h>
     85 #else
     86 #include <strings.h>
     87 #endif
     88 
     89 #if defined(USE_LOGIN_LIBRARY)
     90 #include <Kerberos/KerberosLoginPrivate.h>
     91 #elif defined(USE_LEASH)
     92 #ifdef _WIN64
     93 #define LEASH_DLL "leashw64.dll"
     94 #else
     95 #define LEASH_DLL "leashw32.dll"
     96 #endif
     97 static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL;
     98 static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
     99 #endif
    100 
    101 k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
    102 static char *krb5_gss_keytab = NULL;
    103 
    104 /* Heimdal calls this gsskrb5_register_acceptor_identity. */
    105 OM_uint32 KRB5_CALLCONV
    106 krb5_gss_register_acceptor_identity(const char *keytab)
    107 {
    108     size_t	len;
    109     char *new, *old;
    110     int err;
    111 
    112     err = gssint_initialize_library();
    113     if (err != 0)
    114 	return GSS_S_FAILURE;
    115 
    116     if (keytab == NULL)
    117 	return GSS_S_FAILURE;
    118 
    119     len = strlen(keytab);
    120     new = malloc(len + 1);
    121     if (new == NULL)
    122 	return GSS_S_FAILURE;
    123     strcpy(new, keytab);
    124 
    125     err = k5_mutex_lock(&gssint_krb5_keytab_lock);
    126     if (err) {
    127 	free(new);
    128 	return GSS_S_FAILURE;
    129     }
    130     old = krb5_gss_keytab;
    131     krb5_gss_keytab = new;
    132     k5_mutex_unlock(&gssint_krb5_keytab_lock);
    133     if (old != NULL)
    134 	free(old);
    135     return GSS_S_COMPLETE;
    136 }
    137 
    138 /* get credentials corresponding to a key in the krb5 keytab.
    139    If the default name is requested, return the name in output_princ.
    140      If output_princ is non-NULL, the caller will use or free it, regardless
    141      of the return value.
    142    If successful, set the keytab-specific fields in cred
    143    */
    144 
    145 static OM_uint32
    146 acquire_accept_cred(context, minor_status, desired_name, output_princ, cred)
    147      krb5_context context;
    148      OM_uint32 *minor_status;
    149      gss_name_t desired_name;
    150      krb5_principal *output_princ;
    151      krb5_gss_cred_id_rec *cred;
    152 {
    153    krb5_error_code code;
    154    krb5_principal princ;
    155    krb5_keytab kt;
    156    krb5_keytab_entry entry;
    157 
    158    *output_princ = NULL;
    159    cred->keytab = NULL;
    160 
    161    /* open the default keytab */
    162 
    163    code = gssint_initialize_library();
    164    if (code != 0) {
    165        *minor_status = code;
    166        return GSS_S_FAILURE;
    167    }
    168    code = k5_mutex_lock(&gssint_krb5_keytab_lock);
    169    if (code) {
    170        *minor_status = code;
    171        return GSS_S_FAILURE;
    172    }
    173    if (krb5_gss_keytab != NULL) {
    174       code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
    175       k5_mutex_unlock(&gssint_krb5_keytab_lock);
    176    } else {
    177       k5_mutex_unlock(&gssint_krb5_keytab_lock);
    178       code = krb5_kt_default(context, &kt);
    179    }
    180 
    181    if (code) {
    182       *minor_status = code;
    183       /* Solaris Kerb NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
    184       return(GSS_S_NO_CRED);
    185    }
    186 
    187    if (desired_name != GSS_C_NO_NAME) {
    188       princ = (krb5_principal) desired_name;
    189       if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) {
    190 	 (void) krb5_kt_close(context, kt);
    191 	 if (code == KRB5_KT_NOTFOUND)
    192 	    *minor_status = KG_KEYTAB_NOMATCH;
    193 	 else
    194 	    *minor_status = code;
    195 	 /* Solaris Kerb NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
    196 	 return(GSS_S_NO_CRED);
    197       }
    198       krb5_kt_free_entry(context, &entry);
    199 
    200       /* Open the replay cache for this principal. */
    201       if ((code = krb5_get_server_rcache(context,
    202 					 krb5_princ_component(context, princ, 0),
    203 					 &cred->rcache))) {
    204 	 *minor_status = code;
    205 	 return(GSS_S_FAILURE);
    206       }
    207 
    208    }
    209 
    210 /* hooray.  we made it */
    211 
    212    cred->keytab = kt;
    213 
    214    return(GSS_S_COMPLETE);
    215 }
    216 
    217 /* get credentials corresponding to the default credential cache.
    218    If the default name is requested, return the name in output_princ.
    219      If output_princ is non-NULL, the caller will use or free it, regardless
    220      of the return value.
    221    If successful, set the ccache-specific fields in cred.
    222    */
    223 
    224 static OM_uint32
    225 acquire_init_cred(context, minor_status, desired_name, output_princ, cred)
    226      krb5_context context;
    227      OM_uint32 *minor_status;
    228      gss_name_t desired_name;
    229      krb5_principal *output_princ;
    230      krb5_gss_cred_id_rec *cred;
    231 {
    232    krb5_error_code code;
    233    krb5_ccache ccache;
    234    krb5_principal princ, tmp_princ;
    235    krb5_flags flags;
    236    krb5_cc_cursor cur;
    237    krb5_creds creds;
    238    int got_endtime;
    239    int caller_provided_ccache_name = 0;
    240 
    241    cred->ccache = NULL;
    242 
    243    /* load the GSS ccache name into the kg_context */
    244 
    245    if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
    246        return(GSS_S_FAILURE);
    247 
    248    /* check to see if the caller provided a ccache name if so
    249     * we will just use that and not search the cache collection */
    250    if (GSS_ERROR(kg_caller_provided_ccache_name (minor_status, &caller_provided_ccache_name))) {
    251        return(GSS_S_FAILURE);
    252    }
    253 
    254 #if defined(USE_LOGIN_LIBRARY) || defined(USE_LEASH)
    255    if (desired_name && !caller_provided_ccache_name) {
    256 #if defined(USE_LOGIN_LIBRARY)
    257        KLStatus err = klNoErr;
    258        char *ccache_name = NULL;
    259        KLPrincipal kl_desired_princ = NULL;
    260 
    261        err = __KLCreatePrincipalFromKerberos5Principal ((krb5_principal) desired_name,
    262                                                         &kl_desired_princ);
    263 
    264        if (!err) {
    265            err = KLAcquireInitialTickets (kl_desired_princ, NULL, NULL, &ccache_name);
    266        }
    267 
    268        if (!err) {
    269            err = krb5_cc_resolve (context, ccache_name, &ccache);
    270        }
    271 
    272        if (err) {
    273            *minor_status = err;
    274            return(GSS_S_CRED_UNAVAIL);
    275        }
    276 
    277        if (kl_desired_princ != NULL) { KLDisposePrincipal (kl_desired_princ); }
    278        if (ccache_name      != NULL) { KLDisposeString (ccache_name); }
    279 
    280 #elif defined(USE_LEASH)
    281        if ( hLeashDLL == INVALID_HANDLE_VALUE ) {
    282 	   hLeashDLL = LoadLibrary(LEASH_DLL);
    283 	   if ( hLeashDLL != INVALID_HANDLE_VALUE ) {
    284 	       (FARPROC) pLeash_AcquireInitialTicketsIfNeeded =
    285 		   GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
    286 	   }
    287        }
    288 
    289        if ( pLeash_AcquireInitialTicketsIfNeeded ) {
    290 	   char ccname[256]="";
    291 	   pLeash_AcquireInitialTicketsIfNeeded(context, (krb5_principal) desired_name, ccname, sizeof(ccname));
    292 	   if (!ccname[0]) {
    293 	       *minor_status = KRB5_CC_NOTFOUND;
    294 	       return(GSS_S_NO_CRED);
    295 	   }
    296 
    297 	   if ((code = krb5_cc_resolve (context, ccname, &ccache))) {
    298 	       *minor_status = code;
    299 	       return(GSS_S_NO_CRED);
    300 	   }
    301        } else {
    302 	   /* leash dll not available, open the default credential cache */
    303 
    304 	   if ((code = krb5int_cc_default(context, &ccache))) {
    305 	       *minor_status = code;
    306 	       return(GSS_S_NO_CRED);
    307 	   }
    308        }
    309 #endif /* USE_LEASH */
    310    } else
    311 #endif /* USE_LOGIN_LIBRARY || USE_LEASH */
    312    {
    313        /* open the default credential cache */
    314 
    315        if ((code = krb5int_cc_default(context, &ccache))) {
    316 	   *minor_status = code;
    317 	   return(GSS_S_NO_CRED);
    318        }
    319    }
    320 
    321    /* turn off OPENCLOSE mode while extensive frobbing is going on */
    322    /*
    323     * SUNW14resync
    324     * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE)
    325     * on the error returns cuz the 1.4 krb5_cc_close does not always close
    326     * the file like it used to and caused STC test gss.27 to fail.
    327     */
    328    flags = 0;		/* turns off OPENCLOSE mode */
    329    if ((code = krb5_cc_set_flags(context, ccache, flags))) {
    330       (void)krb5_cc_close(context, ccache);
    331       *minor_status = code;
    332       return(GSS_S_NO_CRED);
    333    }
    334 
    335    /* get out the principal name and see if it matches */
    336 
    337    if ((code = krb5_cc_get_principal(context, ccache, &princ))) {
    338       /* Solaris Kerberos */
    339       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    340       (void)krb5_cc_close(context, ccache);
    341       *minor_status = code;
    342       return(GSS_S_FAILURE);
    343    }
    344 
    345    if (desired_name != (gss_name_t) NULL) {
    346       if (! krb5_principal_compare(context, princ, (krb5_principal) desired_name)) {
    347 	 (void)krb5_free_principal(context, princ);
    348 	 /* Solaris Kerberos */
    349 	 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    350 	 (void)krb5_cc_close(context, ccache);
    351 	 *minor_status = KG_CCACHE_NOMATCH;
    352 	 return(GSS_S_NO_CRED);
    353       }
    354       (void)krb5_free_principal(context, princ);
    355       princ = (krb5_principal) desired_name;
    356    } else {
    357       *output_princ = princ;
    358    }
    359 
    360    /* iterate over the ccache, find the tgt */
    361 
    362    if ((code = krb5_cc_start_seq_get(context, ccache, &cur))) {
    363       /* Solaris Kerberos */
    364       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    365       (void)krb5_cc_close(context, ccache);
    366       *minor_status = code;
    367       return(GSS_S_FAILURE);
    368    }
    369 
    370    /* this is hairy.  If there's a tgt for the principal's local realm
    371       in here, that's what we want for the expire time.  But if
    372       there's not, then we want to use the first key.  */
    373 
    374    got_endtime = 0;
    375 
    376    code = krb5_build_principal_ext(context, &tmp_princ,
    377 				   krb5_princ_realm(context, princ)->length,
    378 				   krb5_princ_realm(context, princ)->data,
    379 				   6, "krbtgt",
    380 				   krb5_princ_realm(context, princ)->length,
    381 				   krb5_princ_realm(context, princ)->data,
    382 				   0);
    383    if (code) {
    384       /* Solaris Kerberos */
    385       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    386       (void)krb5_cc_close(context, ccache);
    387       *minor_status = code;
    388       return(GSS_S_FAILURE);
    389    }
    390    while (!(code = krb5_cc_next_cred(context, ccache, &cur, &creds))) {
    391       if (krb5_principal_compare(context, tmp_princ, creds.server)) {
    392 	 cred->tgt_expire = creds.times.endtime;
    393 	 got_endtime = 1;
    394 	 *minor_status = 0;
    395 	 code = 0;
    396 	 krb5_free_cred_contents(context, &creds);
    397 	 break;
    398       }
    399       if (got_endtime == 0) {
    400 	 cred->tgt_expire = creds.times.endtime;
    401 	 got_endtime = 1;
    402       }
    403       krb5_free_cred_contents(context, &creds);
    404    }
    405    krb5_free_principal(context, tmp_princ);
    406 
    407    if (code && code != KRB5_CC_END) {
    408       /* this means some error occurred reading the ccache */
    409       (void)krb5_cc_end_seq_get(context, ccache, &cur);
    410       /* Solaris Kerberos */
    411       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    412       (void)krb5_cc_close(context, ccache);
    413       *minor_status = code;
    414       return(GSS_S_FAILURE);
    415    } else if (! got_endtime) {
    416       /* this means the ccache was entirely empty */
    417       (void)krb5_cc_end_seq_get(context, ccache, &cur);
    418       /* Solaris Kerberos */
    419       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    420       (void)krb5_cc_close(context, ccache);
    421       *minor_status = KG_EMPTY_CCACHE;
    422       return(GSS_S_FAILURE);
    423    } else {
    424       /* this means that we found an endtime to use. */
    425       if ((code = krb5_cc_end_seq_get(context, ccache, &cur))) {
    426 	 /* Solaris Kerberos */
    427 	 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
    428 	 (void)krb5_cc_close(context, ccache);
    429 	 *minor_status = code;
    430 	 return(GSS_S_FAILURE);
    431       }
    432       flags = KRB5_TC_OPENCLOSE;	/* turns on OPENCLOSE mode */
    433       if ((code = krb5_cc_set_flags(context, ccache, flags))) {
    434 	 (void)krb5_cc_close(context, ccache);
    435 	 *minor_status = code;
    436 	 return(GSS_S_FAILURE);
    437       }
    438    }
    439 
    440    /* the credentials match and are valid */
    441 
    442    cred->ccache = ccache;
    443    /* minor_status is set while we are iterating over the ccache */
    444    return(GSS_S_COMPLETE);
    445 }
    446 
    447 /*ARGSUSED*/
    448 OM_uint32
    449 krb5_gss_acquire_cred(minor_status, desired_name, time_req,
    450 		      desired_mechs, cred_usage, output_cred_handle,
    451 		      actual_mechs, time_rec)
    452      OM_uint32 *minor_status;
    453      gss_name_t desired_name;
    454      OM_uint32 time_req;
    455      gss_OID_set desired_mechs;
    456      gss_cred_usage_t cred_usage;
    457      gss_cred_id_t *output_cred_handle;
    458      gss_OID_set *actual_mechs;
    459      OM_uint32 *time_rec;
    460 {
    461    krb5_context context;
    462    size_t i;
    463    krb5_gss_cred_id_t cred;
    464    gss_OID_set ret_mechs;
    465    int req_old, req_new;
    466    OM_uint32 ret;
    467    krb5_error_code code;
    468 
    469    code = gssint_initialize_library();
    470    if (code) {
    471        *minor_status = code;
    472        return GSS_S_FAILURE;
    473    }
    474 
    475    code = krb5_gss_init_context(&context);
    476    if (code) {
    477        *minor_status = code;
    478        return GSS_S_FAILURE;
    479    }
    480 
    481    /* make sure all outputs are valid */
    482 
    483    *output_cred_handle = NULL;
    484    if (actual_mechs)
    485       *actual_mechs = NULL;
    486    if (time_rec)
    487       *time_rec = 0;
    488 
    489    /* validate the name */
    490 
    491    /*SUPPRESS 29*/
    492    if ((desired_name != (gss_name_t) NULL) &&
    493        (! kg_validate_name(desired_name))) {
    494       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
    495       krb5_free_context(context);
    496       return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
    497    }
    498 
    499    /* verify that the requested mechanism set is the default, or
    500       contains krb5 */
    501 
    502    if (desired_mechs == GSS_C_NULL_OID_SET) {
    503       req_old = 1;
    504       req_new = 1;
    505    } else {
    506       req_old = 0;
    507       req_new = 0;
    508 
    509       for (i=0; i<desired_mechs->count; i++) {
    510 	 if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i])))
    511 	    req_old++;
    512 	 if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i])))
    513 	    req_new++;
    514       }
    515 
    516       if (!req_old && !req_new) {
    517 	 *minor_status = 0;
    518 	 krb5_free_context(context);
    519 	 return(GSS_S_BAD_MECH);
    520       }
    521    }
    522 
    523    /* create the gss cred structure */
    524 
    525    if ((cred =
    526 	(krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) {
    527       *minor_status = ENOMEM;
    528       krb5_free_context(context);
    529       return(GSS_S_FAILURE);
    530    }
    531    memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
    532 
    533    cred->usage = cred_usage;
    534    cred->princ = NULL;
    535    cred->prerfc_mech = req_old;
    536    cred->rfc_mech = req_new;
    537 
    538    cred->keytab = NULL;
    539    cred->ccache = NULL;
    540 
    541    code = k5_mutex_init(&cred->lock);
    542    if (code) {
    543        *minor_status = code;
    544        krb5_free_context(context);
    545        return GSS_S_FAILURE;
    546    }
    547    /* Note that we don't need to lock this GSSAPI credential record
    548       here, because no other thread can gain access to it until we
    549       return it.  */
    550 
    551    if ((cred_usage != GSS_C_INITIATE) &&
    552        (cred_usage != GSS_C_ACCEPT) &&
    553        (cred_usage != GSS_C_BOTH)) {
    554       k5_mutex_destroy(&cred->lock);
    555       xfree(cred);
    556       *minor_status = (OM_uint32) G_BAD_USAGE;
    557       krb5_free_context(context);
    558       return(GSS_S_FAILURE);
    559    }
    560 
    561    /* if requested, acquire credentials for accepting */
    562    /* this will fill in cred->princ if the desired_name is not specified */
    563 
    564    if ((cred_usage == GSS_C_ACCEPT) ||
    565        (cred_usage == GSS_C_BOTH))
    566       if ((ret = acquire_accept_cred(context, minor_status, desired_name,
    567 				     &(cred->princ), cred))
    568 	  != GSS_S_COMPLETE) {
    569 	 if (cred->princ)
    570 	    krb5_free_principal(context, cred->princ);
    571          k5_mutex_destroy(&cred->lock);
    572          xfree(cred);
    573 	 /* minor_status set by acquire_accept_cred() */
    574 	 krb5_free_context(context);
    575 	 return(ret);
    576       }
    577 
    578    /* if requested, acquire credentials for initiation */
    579    /* this will fill in cred->princ if it wasn't set above, and
    580       the desired_name is not specified */
    581 
    582    if ((cred_usage == GSS_C_INITIATE) ||
    583        (cred_usage == GSS_C_BOTH))
    584       if ((ret =
    585 	   acquire_init_cred(context, minor_status,
    586 			     cred->princ?(gss_name_t)cred->princ:desired_name,
    587 			     &(cred->princ), cred))
    588 	  != GSS_S_COMPLETE) {
    589 	 if (cred->keytab)
    590 	    krb5_kt_close(context, cred->keytab);
    591 	 if (cred->princ)
    592 	    krb5_free_principal(context, cred->princ);
    593          k5_mutex_destroy(&cred->lock);
    594          xfree(cred);
    595 	 /* minor_status set by acquire_init_cred() */
    596 	 krb5_free_context(context);
    597 	 return(ret);
    598       }
    599 
    600    /* Solaris Kerberos:
    601     * if the princ wasn't filled in already, fill it in now unless
    602     * a cred with no associated princ is requested (will invoke default
    603     * behaviour when gss_accept_init_context() is called).
    604     * Note MIT 1.4 has GSS_C_NO_CREDENTIAL instead of GSS_C_NO_NAME
    605     */
    606    if (!cred->princ && (desired_name != GSS_C_NO_NAME))
    607       if ((code = krb5_copy_principal(context, (krb5_principal) desired_name,
    608 				      &(cred->princ)))) {
    609 	 if (cred->ccache)
    610 	    (void)krb5_cc_close(context, cred->ccache);
    611 	 if (cred->keytab)
    612 	    (void)krb5_kt_close(context, cred->keytab);
    613          k5_mutex_destroy(&cred->lock);
    614          xfree(cred);
    615 	 *minor_status = code;
    616 	 krb5_free_context(context);
    617 	 return(GSS_S_FAILURE);
    618       }
    619 
    620    /*** at this point, the cred structure has been completely created */
    621 
    622    /* compute time_rec */
    623 
    624    if (cred_usage == GSS_C_ACCEPT) {
    625       if (time_rec)
    626 	 *time_rec = GSS_C_INDEFINITE;
    627    } else {
    628       krb5_timestamp now;
    629 
    630       if ((code = krb5_timeofday(context, &now))) {
    631 	 if (cred->ccache)
    632 	    (void)krb5_cc_close(context, cred->ccache);
    633 	 if (cred->keytab)
    634 	    (void)krb5_kt_close(context, cred->keytab);
    635 	 if (cred->princ)
    636 	    krb5_free_principal(context, cred->princ);
    637          k5_mutex_destroy(&cred->lock);
    638          xfree(cred);
    639 	 *minor_status = code;
    640 	 krb5_free_context(context);
    641 	 return(GSS_S_FAILURE);
    642       }
    643 
    644       if (time_rec)
    645 	 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
    646    }
    647 
    648    /* create mechs */
    649 
    650    if (actual_mechs) {
    651        if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status,
    652 							    &ret_mechs)) ||
    653 	   (cred->prerfc_mech &&
    654 	    GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
    655 							  (const gss_OID) gss_mech_krb5_old,
    656 							   &ret_mechs))) ||
    657 	   (cred->rfc_mech &&
    658 	    GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
    659 							  (const gss_OID) gss_mech_krb5,
    660 							   &ret_mechs)))) {
    661 	   if (cred->ccache)
    662 	       (void)krb5_cc_close(context, cred->ccache);
    663 	   if (cred->keytab)
    664 	       (void)krb5_kt_close(context, cred->keytab);
    665 	   if (cred->princ)
    666 	       krb5_free_principal(context, cred->princ);
    667            k5_mutex_destroy(&cred->lock);
    668 	   xfree(cred);
    669 	   /* *minor_status set above */
    670 	   krb5_free_context(context);
    671 	   return(ret);
    672        }
    673    }
    674 
    675    /* intern the credential handle */
    676 
    677    if (! kg_save_cred_id((gss_cred_id_t) cred)) {
    678       free(ret_mechs->elements);
    679       free(ret_mechs);
    680       if (cred->ccache)
    681 	 (void)krb5_cc_close(context, cred->ccache);
    682       if (cred->keytab)
    683 	 (void)krb5_kt_close(context, cred->keytab);
    684       if (cred->princ)
    685 	 krb5_free_principal(context, cred->princ);
    686       k5_mutex_destroy(&cred->lock);
    687       xfree(cred);
    688       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
    689       krb5_free_context(context);
    690       return(GSS_S_FAILURE);
    691    }
    692 
    693    /* return success */
    694 
    695    *minor_status = 0;
    696    *output_cred_handle = (gss_cred_id_t) cred;
    697    if (actual_mechs)
    698       *actual_mechs = ret_mechs;
    699 
    700    krb5_free_context(context);
    701    return(GSS_S_COMPLETE);
    702 }
    703