"Fossies" - the Fresh Open Source Software Archive

Member "krb5-1.18/src/lib/gssapi/krb5/acquire_cred.c" (12 Feb 2020, 41480 Bytes) of package /linux/misc/krb5-1.18.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "acquire_cred.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.17.1_vs_1.18.

    1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
    2 /*
    3  * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology.
    4  * All Rights Reserved.
    5  *
    6  * Export of this software from the United States of America may
    7  *   require a specific license from the United States Government.
    8  *   It is the responsibility of any person or organization contemplating
    9  *   export to obtain such a license before exporting.
   10  *
   11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
   12  * distribute this software and its documentation for any purpose and
   13  * without fee is hereby granted, provided that the above copyright
   14  * notice appear in all copies and that both that copyright notice and
   15  * this permission notice appear in supporting documentation, and that
   16  * the name of M.I.T. not be used in advertising or publicity pertaining
   17  * to distribution of the software without specific, written prior
   18  * permission.  Furthermore if you modify this software you must label
   19  * your software as modified software and not distribute it in such a
   20  * fashion that it might be confused with the original M.I.T. software.
   21  * M.I.T. makes no representations about the suitability of
   22  * this software for any purpose.  It is provided "as is" without express
   23  * or implied warranty.
   24  */
   25 /*
   26  * Copyright 1993 by OpenVision Technologies, Inc.
   27  *
   28  * Permission to use, copy, modify, distribute, and sell this software
   29  * and its documentation for any purpose is hereby granted without fee,
   30  * provided that the above copyright notice appears in all copies and
   31  * that both that copyright notice and this permission notice appear in
   32  * supporting documentation, and that the name of OpenVision not be used
   33  * in advertising or publicity pertaining to distribution of the software
   34  * without specific, written prior permission. OpenVision makes no
   35  * representations about the suitability of this software for any
   36  * purpose.  It is provided "as is" without express or implied warranty.
   37  *
   38  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
   39  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
   40  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
   41  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
   42  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
   43  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
   44  * PERFORMANCE OF THIS SOFTWARE.
   45  */
   46 
   47 /*
   48  * Copyright (C) 1998 by the FundsXpress, INC.
   49  *
   50  * All rights reserved.
   51  *
   52  * Export of this software from the United States of America may require
   53  * a specific license from the United States Government.  It is the
   54  * responsibility of any person or organization contemplating export to
   55  * obtain such a license before exporting.
   56  *
   57  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
   58  * distribute this software and its documentation for any purpose and
   59  * without fee is hereby granted, provided that the above copyright
   60  * notice appear in all copies and that both that copyright notice and
   61  * this permission notice appear in supporting documentation, and that
   62  * the name of FundsXpress. not be used in advertising or publicity pertaining
   63  * to distribution of the software without specific, written prior
   64  * permission.  FundsXpress makes no representations about the suitability of
   65  * this software for any purpose.  It is provided "as is" without express
   66  * or implied warranty.
   67  *
   68  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
   69  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
   70  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   71  */
   72 
   73 #include "k5-int.h"
   74 #include "gssapiP_krb5.h"
   75 #ifdef HAVE_STRING_H
   76 #include <string.h>
   77 #else
   78 #include <strings.h>
   79 #endif
   80 
   81 #ifdef USE_LEASH
   82 #ifdef _WIN64
   83 #define LEASH_DLL "leashw64.dll"
   84 #else
   85 #define LEASH_DLL "leashw32.dll"
   86 #endif
   87 static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL;
   88 static HANDLE hLeashDLL = INVALID_HANDLE_VALUE;
   89 #endif
   90 
   91 #ifndef LEAN_CLIENT
   92 k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER;
   93 static char *krb5_gss_keytab = NULL;
   94 
   95 /* Heimdal calls this gsskrb5_register_acceptor_identity. */
   96 OM_uint32
   97 gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
   98                                        const gss_OID desired_mech,
   99                                        const gss_OID desired_object,
  100                                        gss_buffer_t value)
  101 {
  102     char *new = NULL, *old;
  103     int err;
  104 
  105     err = gss_krb5int_initialize_library();
  106     if (err != 0)
  107         return GSS_S_FAILURE;
  108 
  109     if (value->value != NULL) {
  110         new = strdup((char *)value->value);
  111         if (new == NULL)
  112             return GSS_S_FAILURE;
  113     }
  114 
  115     k5_mutex_lock(&gssint_krb5_keytab_lock);
  116     old = krb5_gss_keytab;
  117     krb5_gss_keytab = new;
  118     k5_mutex_unlock(&gssint_krb5_keytab_lock);
  119     free(old);
  120     return GSS_S_COMPLETE;
  121 }
  122 
  123 /* Try to verify that keytab contains at least one entry for name.  Return 0 if
  124  * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */
  125 static krb5_error_code
  126 check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name)
  127 {
  128     krb5_error_code code;
  129     krb5_keytab_entry ent;
  130     krb5_kt_cursor cursor;
  131     krb5_principal accprinc = NULL;
  132     krb5_boolean match;
  133     char *princname;
  134 
  135     if (name->service == NULL) {
  136         code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent);
  137         if (code == 0)
  138             krb5_kt_free_entry(context, &ent);
  139         return code;
  140     }
  141 
  142     /* If we can't iterate through the keytab, skip this check. */
  143     if (kt->ops->start_seq_get == NULL)
  144         return 0;
  145 
  146     /* Get the partial principal for the acceptor name. */
  147     code = kg_acceptor_princ(context, name, &accprinc);
  148     if (code)
  149         return code;
  150 
  151     /* Scan the keytab for host-based entries matching accprinc. */
  152     code = krb5_kt_start_seq_get(context, kt, &cursor);
  153     if (code)
  154         goto cleanup;
  155     while ((code = krb5_kt_next_entry(context, kt, &ent, &cursor)) == 0) {
  156         match = krb5_sname_match(context, accprinc, ent.principal);
  157         (void)krb5_free_keytab_entry_contents(context, &ent);
  158         if (match)
  159             break;
  160     }
  161     (void)krb5_kt_end_seq_get(context, kt, &cursor);
  162     if (code == KRB5_KT_END) {
  163         code = KRB5_KT_NOTFOUND;
  164         if (krb5_unparse_name(context, accprinc, &princname) == 0) {
  165             k5_setmsg(context, code, _("No key table entry found matching %s"),
  166                       princname);
  167             free(princname);
  168         }
  169     }
  170 
  171 cleanup:
  172     krb5_free_principal(context, accprinc);
  173     return code;
  174 }
  175 
  176 /* get credentials corresponding to a key in the krb5 keytab.
  177    If successful, set the keytab-specific fields in cred
  178 */
  179 
  180 static OM_uint32
  181 acquire_accept_cred(krb5_context context, OM_uint32 *minor_status,
  182                     krb5_keytab req_keytab, const char *rcname,
  183                     krb5_gss_cred_id_rec *cred)
  184 {
  185     OM_uint32 major;
  186     krb5_error_code code;
  187     krb5_keytab kt = NULL;
  188     krb5_rcache rc = NULL;
  189 
  190     assert(cred->keytab == NULL);
  191 
  192     /* If we have an explicit rcache name, open it. */
  193     if (rcname != NULL) {
  194         code = k5_rc_resolve(context, rcname, &rc);
  195         if (code) {
  196             major = GSS_S_FAILURE;
  197             goto cleanup;
  198         }
  199     }
  200 
  201     if (req_keytab != NULL) {
  202         code = krb5_kt_dup(context, req_keytab, &kt);
  203     } else {
  204         k5_mutex_lock(&gssint_krb5_keytab_lock);
  205         if (krb5_gss_keytab != NULL) {
  206             code = krb5_kt_resolve(context, krb5_gss_keytab, &kt);
  207             k5_mutex_unlock(&gssint_krb5_keytab_lock);
  208         } else {
  209             k5_mutex_unlock(&gssint_krb5_keytab_lock);
  210             code = krb5_kt_default(context, &kt);
  211         }
  212     }
  213     if (code) {
  214         major = GSS_S_CRED_UNAVAIL;
  215         goto cleanup;
  216     }
  217 
  218     if (cred->name != NULL) {
  219         /* Make sure we have keys matching the desired name in the keytab. */
  220         code = check_keytab(context, kt, cred->name);
  221         if (code) {
  222             if (code == KRB5_KT_NOTFOUND) {
  223                 k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH);
  224                 code = KG_KEYTAB_NOMATCH;
  225             }
  226             major = GSS_S_CRED_UNAVAIL;
  227             goto cleanup;
  228         }
  229 
  230         if (rc == NULL) {
  231             /* Open the replay cache for this principal. */
  232             code = krb5_get_server_rcache(context, &cred->name->princ->data[0],
  233                                           &rc);
  234             if (code) {
  235                 major = GSS_S_FAILURE;
  236                 goto cleanup;
  237             }
  238         }
  239     } else {
  240         /* Make sure we have a keytab with keys in it. */
  241         code = krb5_kt_have_content(context, kt);
  242         if (code) {
  243             major = GSS_S_CRED_UNAVAIL;
  244             goto cleanup;
  245         }
  246     }
  247 
  248     cred->keytab = kt;
  249     kt = NULL;
  250     cred->rcache = rc;
  251     rc = NULL;
  252     major = GSS_S_COMPLETE;
  253 
  254 cleanup:
  255     if (kt != NULL)
  256         krb5_kt_close(context, kt);
  257     if (rc != NULL)
  258         k5_rc_close(context, rc);
  259     *minor_status = code;
  260     return major;
  261 }
  262 #endif /* LEAN_CLIENT */
  263 
  264 #ifdef USE_LEASH
  265 static krb5_error_code
  266 get_ccache_leash(krb5_context context, krb5_principal desired_princ,
  267                  krb5_ccache *ccache_out)
  268 {
  269     krb5_error_code code;
  270     krb5_ccache ccache;
  271     char ccname[256] = "";
  272 
  273     *ccache_out = NULL;
  274 
  275     if (hLeashDLL == INVALID_HANDLE_VALUE) {
  276         hLeashDLL = LoadLibrary(LEASH_DLL);
  277         if (hLeashDLL != INVALID_HANDLE_VALUE) {
  278             (FARPROC) pLeash_AcquireInitialTicketsIfNeeded =
  279                 GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded");
  280         }
  281     }
  282 
  283     if (pLeash_AcquireInitialTicketsIfNeeded) {
  284         pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname,
  285                                              sizeof(ccname));
  286         if (!ccname[0])
  287             return KRB5_CC_NOTFOUND;
  288 
  289         code = krb5_cc_resolve(context, ccname, &ccache);
  290         if (code)
  291             return code;
  292     } else {
  293         /* leash dll not available, open the default credential cache. */
  294         code = krb5int_cc_default(context, &ccache);
  295         if (code)
  296             return code;
  297     }
  298 
  299     *ccache_out = ccache;
  300     return 0;
  301 }
  302 #endif /* USE_LEASH */
  303 
  304 /* Set fields in cred according to a ccache config entry whose key (in
  305  * principal form) is config_princ and whose value is value. */
  306 static krb5_error_code
  307 scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred,
  308                krb5_const_principal config_princ, const krb5_data *value)
  309 {
  310     krb5_error_code code;
  311     krb5_data data0 = empty_data();
  312 
  313     if (config_princ->length != 2)
  314         return 0;
  315     if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR)
  316         && cred->impersonator == NULL) {
  317         code = krb5int_copy_data_contents_add0(context, value, &data0);
  318         if (code)
  319             return code;
  320         code = krb5_parse_name(context, data0.data, &cred->impersonator);
  321         krb5_free_data_contents(context, &data0);
  322         if (code)
  323             return code;
  324     } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME)
  325                && cred->refresh_time == 0) {
  326         code = krb5int_copy_data_contents_add0(context, value, &data0);
  327         if (code)
  328             return code;
  329         cred->refresh_time = atol(data0.data);
  330         krb5_free_data_contents(context, &data0);
  331     }
  332     return 0;
  333 }
  334 
  335 /* Return true if it appears that we can non-interactively get initial
  336  * tickets for cred. */
  337 static krb5_boolean
  338 can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
  339 {
  340     krb5_error_code code;
  341     krb5_keytab_entry entry;
  342 
  343     if (cred->password != NULL)
  344         return TRUE;
  345 
  346     if (cred->client_keytab == NULL)
  347         return FALSE;
  348 
  349     /* If we don't know the client principal yet, check for any keytab keys. */
  350     if (cred->name == NULL)
  351         return !krb5_kt_have_content(context, cred->client_keytab);
  352 
  353     /* Check if we have a keytab key for the client principal. */
  354     code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ,
  355                              0, 0, &entry);
  356     if (code) {
  357         krb5_clear_error_message(context);
  358         return FALSE;
  359     }
  360     krb5_free_keytab_entry_contents(context, &entry);
  361     return TRUE;
  362 }
  363 
  364 /* Scan cred->ccache for name, expiry time, impersonator, refresh time. */
  365 static krb5_error_code
  366 scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred)
  367 {
  368     krb5_error_code code;
  369     krb5_ccache ccache = cred->ccache;
  370     krb5_principal ccache_princ = NULL, tgt_princ = NULL;
  371     krb5_data *realm;
  372     krb5_cc_cursor cursor;
  373     krb5_creds creds;
  374     krb5_timestamp endtime;
  375     krb5_boolean is_tgt;
  376 
  377     /* Turn on NOTICKET, as we don't need session keys here. */
  378     code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
  379     if (code)
  380         return code;
  381 
  382     /* Credentials cache principal must match the initiator name. */
  383     code = krb5_cc_get_principal(context, ccache, &ccache_princ);
  384     if (code != 0)
  385         goto cleanup;
  386     if (cred->name != NULL &&
  387         !krb5_principal_compare(context, ccache_princ, cred->name->princ)) {
  388         code = KG_CCACHE_NOMATCH;
  389         goto cleanup;
  390     }
  391 
  392     /* Save the ccache principal as the credential name if not already set. */
  393     if (!cred->name) {
  394         code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
  395                             KG_INIT_NAME_NO_COPY, &cred->name);
  396         if (code)
  397             goto cleanup;
  398         ccache_princ = NULL;
  399     }
  400 
  401     assert(cred->name->princ != NULL);
  402     realm = krb5_princ_realm(context, cred->name->princ);
  403     code = krb5_build_principal_ext(context, &tgt_princ,
  404                                     realm->length, realm->data,
  405                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
  406                                     realm->length, realm->data,
  407                                     0);
  408     if (code)
  409         return code;
  410 
  411     /* If there's a tgt for the principal's local realm in here, use its expiry
  412      * time.  Otherwise use the first key. */
  413     code = krb5_cc_start_seq_get(context, ccache, &cursor);
  414     if (code) {
  415         krb5_free_principal(context, tgt_princ);
  416         return code;
  417     }
  418     while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) {
  419         if (krb5_is_config_principal(context, creds.server)) {
  420             code = scan_cc_config(context, cred, creds.server, &creds.ticket);
  421             krb5_free_cred_contents(context, &creds);
  422             if (code)
  423                 break;
  424             continue;
  425         }
  426         is_tgt = krb5_principal_compare(context, tgt_princ, creds.server);
  427         endtime = creds.times.endtime;
  428         krb5_free_cred_contents(context, &creds);
  429         if (is_tgt)
  430             cred->have_tgt = TRUE;
  431         if (is_tgt || cred->expire == 0)
  432             cred->expire = endtime;
  433     }
  434     krb5_cc_end_seq_get(context, ccache, &cursor);
  435     if (code && code != KRB5_CC_END)
  436         goto cleanup;
  437     code = 0;
  438 
  439     if (cred->expire == 0 && !can_get_initial_creds(context, cred)) {
  440         code = KG_EMPTY_CCACHE;
  441         goto cleanup;
  442     }
  443 
  444 cleanup:
  445     (void)krb5_cc_set_flags(context, ccache, 0);
  446     krb5_free_principal(context, ccache_princ);
  447     krb5_free_principal(context, tgt_princ);
  448     return code;
  449 }
  450 
  451 /* Find an existing or destination ccache for cred->name. */
  452 static krb5_error_code
  453 get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
  454 {
  455     krb5_error_code code;
  456     krb5_boolean can_get, have_collection;
  457     krb5_ccache defcc = NULL;
  458     krb5_principal princ = NULL;
  459     const char *cctype;
  460 
  461     assert(cred->name != NULL && cred->ccache == NULL);
  462 #ifdef USE_LEASH
  463     code = get_ccache_leash(context, cred->name->princ, &cred->ccache);
  464     return code ? code : scan_ccache(context, cred);
  465 #else
  466     /* Check first whether we can acquire tickets, to avoid overwriting the
  467      * extended error message from krb5_cc_cache_match. */
  468     can_get = can_get_initial_creds(context, cred);
  469 
  470     /* Look for an existing cache for the client principal. */
  471     code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
  472     if (code == 0)
  473         return scan_ccache(context, cred);
  474     if (code != KRB5_CC_NOTFOUND || !can_get)
  475         return code;
  476     krb5_clear_error_message(context);
  477 
  478     /* There is no existing ccache, but we can acquire credentials.  Get the
  479      * default ccache to help decide where we should put them. */
  480     code = krb5_cc_default(context, &defcc);
  481     if (code)
  482         return code;
  483     cctype = krb5_cc_get_type(context, defcc);
  484     have_collection = krb5_cc_support_switch(context, cctype);
  485 
  486     /* We can use an empty default ccache if we're using a password or if
  487      * there's no collection. */
  488     if (cred->password != NULL || !have_collection) {
  489         if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) {
  490             cred->ccache = defcc;
  491             defcc = NULL;
  492         }
  493         krb5_clear_error_message(context);
  494     }
  495 
  496     /* Otherwise, try to use a new cache in the collection. */
  497     if (cred->ccache == NULL) {
  498         if (!have_collection) {
  499             code = KG_CCACHE_NOMATCH;
  500             goto cleanup;
  501         }
  502         code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache);
  503         if (code)
  504             goto cleanup;
  505     }
  506 
  507 cleanup:
  508     krb5_free_principal(context, princ);
  509     if (defcc != NULL)
  510         krb5_cc_close(context, defcc);
  511     return code;
  512 #endif /* not USE_LEASH */
  513 }
  514 
  515 /* Try to set cred->name using the client keytab. */
  516 static krb5_error_code
  517 get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred)
  518 {
  519     krb5_error_code code;
  520     krb5_principal princ;
  521 
  522     assert(cred->name == NULL);
  523 
  524     if (cred->client_keytab == NULL)
  525         return KRB5_KT_NOTFOUND;
  526 
  527     code = k5_kt_get_principal(context, cred->client_keytab, &princ);
  528     if (code)
  529         return code;
  530     code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY,
  531                         &cred->name);
  532     if (code) {
  533         krb5_free_principal(context, princ);
  534         return code;
  535     }
  536     return 0;
  537 }
  538 
  539 /* Make a note in ccache that we should attempt to refresh it from the client
  540  * keytab at refresh_time. */
  541 static void
  542 set_refresh_time(krb5_context context, krb5_ccache ccache,
  543                  krb5_timestamp refresh_time)
  544 {
  545     char buf[128];
  546     krb5_data d;
  547 
  548     snprintf(buf, sizeof(buf), "%u", (unsigned int)ts2tt(refresh_time));
  549     d = string2data(buf);
  550     (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME,
  551                              &d);
  552     krb5_clear_error_message(context);
  553 }
  554 
  555 /* Return true if it's time to refresh cred from the client keytab.  If
  556  * returning true, avoid retrying for 30 seconds. */
  557 krb5_boolean
  558 kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred)
  559 {
  560     krb5_timestamp now;
  561 
  562     if (krb5_timeofday(context, &now))
  563         return FALSE;
  564     if (cred->refresh_time != 0 && !ts_after(cred->refresh_time, now)) {
  565         set_refresh_time(context, cred->ccache,
  566                          ts_incr(cred->refresh_time, 30));
  567         return TRUE;
  568     }
  569     return FALSE;
  570 }
  571 
  572 /* If appropriate, make a note to refresh cred from the client keytab when it
  573  * is halfway to expired. */
  574 void
  575 kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
  576                             krb5_ticket_times *times)
  577 {
  578     krb5_timestamp refresh;
  579 
  580     /* For now, we only mark keytab-acquired credentials for refresh. */
  581     if (cred->password != NULL)
  582         return;
  583 
  584     /* Make a note to refresh these when they are halfway to expired. */
  585     refresh = ts_incr(times->starttime,
  586                       ts_delta(times->endtime, times->starttime) / 2);
  587     set_refresh_time(context, cred->ccache, refresh);
  588 }
  589 
  590 /* Get initial credentials using the supplied password or client keytab. */
  591 static krb5_error_code
  592 get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
  593 {
  594     krb5_error_code code;
  595     krb5_get_init_creds_opt *opt = NULL;
  596     krb5_creds creds;
  597 
  598     code = krb5_get_init_creds_opt_alloc(context, &opt);
  599     if (code)
  600         return code;
  601     code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache);
  602     if (code)
  603         goto cleanup;
  604     if (cred->password != NULL) {
  605         code = krb5_get_init_creds_password(context, &creds, cred->name->princ,
  606                                             cred->password, NULL, NULL, 0,
  607                                             NULL, opt);
  608     } else if (cred->client_keytab != NULL) {
  609         code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ,
  610                                           cred->client_keytab, 0, NULL, opt);
  611     } else {
  612         code = KRB5_KT_NOTFOUND;
  613     }
  614     if (code)
  615         goto cleanup;
  616     kg_cred_set_initial_refresh(context, cred, &creds.times);
  617     cred->have_tgt = TRUE;
  618     cred->expire = creds.times.endtime;
  619     krb5_free_cred_contents(context, &creds);
  620 cleanup:
  621     krb5_get_init_creds_opt_free(context, opt);
  622     return code;
  623 }
  624 
  625 /* Get initial credentials if we ought to and are able to. */
  626 static krb5_error_code
  627 maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
  628 {
  629     krb5_error_code code;
  630 
  631     /* Don't get creds if we don't know the name or are doing IAKERB. */
  632     if (cred->name == NULL || cred->iakerb_mech)
  633         return 0;
  634 
  635     /* Get creds if we have none or if it's time to refresh. */
  636     if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
  637         code = get_initial_cred(context, cred);
  638         /* If we were trying to refresh and failed, we can keep going. */
  639         if (code && cred->expire == 0)
  640             return code;
  641         krb5_clear_error_message(context);
  642     }
  643     return 0;
  644 }
  645 
  646 static OM_uint32
  647 acquire_init_cred(krb5_context context,
  648                   OM_uint32 *minor_status,
  649                   krb5_ccache req_ccache,
  650                   gss_buffer_t password,
  651                   krb5_keytab client_keytab,
  652                   krb5_gss_cred_id_rec *cred)
  653 {
  654     krb5_error_code code;
  655     krb5_data pwdata, pwcopy;
  656     int caller_ccname = 0;
  657 
  658     /* Get ccache from caller if available. */
  659     if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
  660         return GSS_S_FAILURE;
  661     if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status,
  662                                                  &caller_ccname)))
  663         return GSS_S_FAILURE;
  664 
  665     if (password != GSS_C_NO_BUFFER) {
  666         pwdata = make_data(password->value, password->length);
  667         code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy);
  668         if (code)
  669             goto error;
  670         cred->password = pwcopy.data;
  671 
  672         /* We will fetch the credential into a private memory ccache. */
  673         assert(req_ccache == NULL);
  674         code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
  675         if (code)
  676             goto error;
  677         cred->destroy_ccache = 1;
  678     } else if (req_ccache != NULL) {
  679         code = krb5_cc_dup(context, req_ccache, &cred->ccache);
  680         if (code)
  681             goto error;
  682     } else if (caller_ccname) {
  683         /* Caller's ccache name has been set as the context default. */
  684         code = krb5int_cc_default(context, &cred->ccache);
  685         if (code)
  686             goto error;
  687     }
  688 
  689     if (client_keytab != NULL) {
  690         code = krb5_kt_dup(context, client_keytab, &cred->client_keytab);
  691     } else {
  692         code = krb5_kt_client_default(context, &cred->client_keytab);
  693         if (code) {
  694             /* Treat resolution failure similarly to a client keytab which
  695              * resolves but doesn't exist or has no content. */
  696             TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code);
  697             krb5_clear_error_message(context);
  698             code = 0;
  699         }
  700     }
  701     if (code)
  702         goto error;
  703 
  704     if (cred->ccache != NULL) {
  705         /* The caller specified a ccache; check what's in it. */
  706         code = scan_ccache(context, cred);
  707         if (code == KRB5_FCC_NOFILE) {
  708             /* See if we can get initial creds.  If the caller didn't specify
  709              * a name, pick one from the client keytab. */
  710             if (cred->name == NULL) {
  711                 if (!get_name_from_client_keytab(context, cred))
  712                     code = 0;
  713             } else if (can_get_initial_creds(context, cred)) {
  714                 code = 0;
  715             }
  716         }
  717         if (code)
  718             goto error;
  719     } else if (cred->name != NULL) {
  720         /* The caller specified a name but not a ccache; pick a cache. */
  721         code = get_cache_for_name(context, cred);
  722         if (code)
  723             goto error;
  724     }
  725 
  726 #ifndef USE_LEASH
  727     /* If we haven't picked a name, make sure we have or can get any creds,
  728      * unless we're using Leash and might be able to get them interactively. */
  729     if (cred->name == NULL && !can_get_initial_creds(context, cred)) {
  730         code = krb5_cccol_have_content(context);
  731         if (code)
  732             goto error;
  733     }
  734 #endif
  735 
  736     code = maybe_get_initial_cred(context, cred);
  737     if (code)
  738         goto error;
  739 
  740     *minor_status = 0;
  741     return GSS_S_COMPLETE;
  742 
  743 error:
  744     *minor_status = code;
  745     return GSS_S_CRED_UNAVAIL;
  746 }
  747 
  748 static OM_uint32
  749 acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
  750                      gss_name_t desired_name, gss_buffer_t password,
  751                      OM_uint32 time_req, gss_cred_usage_t cred_usage,
  752                      krb5_ccache ccache, krb5_keytab client_keytab,
  753                      krb5_keytab keytab, const char *rcname,
  754                      krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
  755                      OM_uint32 *time_rec)
  756 {
  757     krb5_gss_cred_id_t cred = NULL;
  758     krb5_gss_name_t name = (krb5_gss_name_t)desired_name;
  759     OM_uint32 ret;
  760     krb5_error_code code = 0;
  761 
  762     /* make sure all outputs are valid */
  763     *output_cred_handle = GSS_C_NO_CREDENTIAL;
  764     if (time_rec)
  765         *time_rec = 0;
  766 
  767     /* create the gss cred structure */
  768     cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
  769     if (cred == NULL)
  770         goto krb_error_out;
  771 
  772     cred->usage = cred_usage;
  773     cred->name = NULL;
  774     cred->impersonator = NULL;
  775     cred->iakerb_mech = iakerb;
  776     cred->default_identity = (name == NULL);
  777 #ifndef LEAN_CLIENT
  778     cred->keytab = NULL;
  779 #endif /* LEAN_CLIENT */
  780     cred->destroy_ccache = 0;
  781     cred->suppress_ci_flags = 0;
  782     cred->ccache = NULL;
  783 
  784     code = k5_mutex_init(&cred->lock);
  785     if (code)
  786         goto krb_error_out;
  787 
  788     switch (cred_usage) {
  789     case GSS_C_INITIATE:
  790     case GSS_C_ACCEPT:
  791     case GSS_C_BOTH:
  792         break;
  793     default:
  794         ret = GSS_S_FAILURE;
  795         *minor_status = (OM_uint32) G_BAD_USAGE;
  796         goto error_out;
  797     }
  798 
  799     if (name != NULL) {
  800         code = kg_duplicate_name(context, name, &cred->name);
  801         if (code)
  802             goto krb_error_out;
  803     }
  804 
  805 #ifndef LEAN_CLIENT
  806     /*
  807      * If requested, acquire credentials for accepting. This will fill
  808      * in cred->name if desired_princ is specified.
  809      */
  810     if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
  811         ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred);
  812         if (ret != GSS_S_COMPLETE)
  813             goto error_out;
  814     }
  815 #endif /* LEAN_CLIENT */
  816 
  817     /*
  818      * If requested, acquire credentials for initiation. This will fill
  819      * in cred->name if it wasn't set above.
  820      */
  821     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
  822         ret = acquire_init_cred(context, minor_status, ccache, password,
  823                                 client_keytab, cred);
  824         if (ret != GSS_S_COMPLETE)
  825             goto error_out;
  826     }
  827 
  828     assert(cred->default_identity || cred->name != NULL);
  829 
  830     /*** at this point, the cred structure has been completely created */
  831 
  832     if (cred_usage == GSS_C_ACCEPT) {
  833         if (time_rec)
  834             *time_rec = GSS_C_INDEFINITE;
  835     } else {
  836         krb5_timestamp now;
  837 
  838         code = krb5_timeofday(context, &now);
  839         if (code != 0)
  840             goto krb_error_out;
  841 
  842         if (time_rec) {
  843             /* Resolve cred now to determine the expiration time. */
  844             ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred,
  845                                   GSS_C_NO_NAME);
  846             if (GSS_ERROR(ret))
  847                 goto error_out;
  848             *time_rec = ts_after(cred->expire, now) ?
  849                 ts_delta(cred->expire, now) : 0;
  850             k5_mutex_unlock(&cred->lock);
  851         }
  852     }
  853 
  854     *minor_status = 0;
  855     *output_cred_handle = (gss_cred_id_t) cred;
  856 
  857     return GSS_S_COMPLETE;
  858 
  859 krb_error_out:
  860     *minor_status = code;
  861     ret = GSS_S_FAILURE;
  862 
  863 error_out:
  864     if (cred != NULL) {
  865         if (cred->ccache) {
  866             if (cred->destroy_ccache)
  867                 krb5_cc_destroy(context, cred->ccache);
  868             else
  869                 krb5_cc_close(context, cred->ccache);
  870         }
  871         if (cred->client_keytab)
  872             krb5_kt_close(context, cred->client_keytab);
  873 #ifndef LEAN_CLIENT
  874         if (cred->keytab)
  875             krb5_kt_close(context, cred->keytab);
  876 #endif /* LEAN_CLIENT */
  877         if (cred->rcache)
  878             k5_rc_close(context, cred->rcache);
  879         if (cred->name)
  880             kg_release_name(context, &cred->name);
  881         krb5_free_principal(context, cred->impersonator);
  882         zapfreestr(cred->password);
  883         k5_mutex_destroy(&cred->lock);
  884         xfree(cred);
  885     }
  886     save_error_info(*minor_status, context);
  887     return ret;
  888 }
  889 
  890 static OM_uint32
  891 acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
  892              gss_buffer_t password, OM_uint32 time_req,
  893              gss_cred_usage_t cred_usage, krb5_ccache ccache,
  894              krb5_keytab keytab, krb5_boolean iakerb,
  895              gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec)
  896 {
  897     krb5_context context = NULL;
  898     krb5_error_code code = 0;
  899     OM_uint32 ret;
  900 
  901     code = gss_krb5int_initialize_library();
  902     if (code) {
  903         *minor_status = code;
  904         ret = GSS_S_FAILURE;
  905         goto out;
  906     }
  907 
  908     code = krb5_gss_init_context(&context);
  909     if (code) {
  910         *minor_status = code;
  911         ret = GSS_S_FAILURE;
  912         goto out;
  913     }
  914 
  915     ret = acquire_cred_context(context, minor_status, desired_name, password,
  916                                time_req, cred_usage, ccache, NULL, keytab,
  917                                NULL, iakerb, output_cred_handle, time_rec);
  918 
  919 out:
  920     krb5_free_context(context);
  921     return ret;
  922 }
  923 
  924 /*
  925  * Resolve the name and ccache for an initiator credential if it has not yet
  926  * been done.  If specified, use the target name to pick an appropriate ccache
  927  * within the collection.  Validates cred_handle and leaves it locked on
  928  * success.
  929  */
  930 OM_uint32
  931 kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
  932                 gss_cred_id_t cred_handle, gss_name_t target_name)
  933 {
  934     OM_uint32 maj;
  935     krb5_error_code code;
  936     krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle;
  937     krb5_gss_name_t tname = (krb5_gss_name_t)target_name;
  938     krb5_principal client_princ;
  939 
  940     *minor_status = 0;
  941 
  942     maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
  943     if (maj != 0)
  944         return maj;
  945     k5_mutex_assert_locked(&cred->lock);
  946 
  947     if (cred->usage == GSS_C_ACCEPT || cred->name != NULL)
  948         return GSS_S_COMPLETE;
  949     /* acquire_init_cred should have set both name and ccache, or neither. */
  950     assert(cred->ccache == NULL);
  951 
  952     if (tname != NULL) {
  953         /* Use the target name to select an existing ccache or a principal. */
  954         code = krb5_cc_select(context, tname->princ, &cred->ccache,
  955                               &client_princ);
  956         if (code && code != KRB5_CC_NOTFOUND)
  957             goto kerr;
  958         if (client_princ != NULL) {
  959             code = kg_init_name(context, client_princ, NULL, NULL, NULL,
  960                                 KG_INIT_NAME_NO_COPY, &cred->name);
  961             if (code) {
  962                 krb5_free_principal(context, client_princ);
  963                 goto kerr;
  964             }
  965         }
  966         if (cred->ccache != NULL) {
  967             code = scan_ccache(context, cred);
  968             if (code)
  969                 goto kerr;
  970         }
  971     }
  972 
  973     /* If we still haven't picked a client principal, try using an existing
  974      * default ccache.  (On Windows, this may acquire initial creds.) */
  975     if (cred->name == NULL) {
  976         code = krb5int_cc_default(context, &cred->ccache);
  977         if (code)
  978             goto kerr;
  979         code = scan_ccache(context, cred);
  980         if (code == KRB5_FCC_NOFILE) {
  981             /* Default ccache doesn't exist; fall through to client keytab. */
  982             krb5_cc_close(context, cred->ccache);
  983             cred->ccache = NULL;
  984         } else if (code) {
  985             goto kerr;
  986         }
  987     }
  988 
  989     /* If that didn't work, try getting a name from the client keytab. */
  990     if (cred->name == NULL) {
  991         code = get_name_from_client_keytab(context, cred);
  992         if (code) {
  993             code = KG_EMPTY_CCACHE;
  994             goto kerr;
  995         }
  996     }
  997 
  998     if (cred->name != NULL && cred->ccache == NULL) {
  999         /* Pick a cache for the name we chose (from krb5_cc_select or from the
 1000          * client keytab). */
 1001         code = get_cache_for_name(context, cred);
 1002         if (code)
 1003             goto kerr;
 1004     }
 1005 
 1006     /* Resolve name to ccache and possibly get initial credentials. */
 1007     code = maybe_get_initial_cred(context, cred);
 1008     if (code)
 1009         goto kerr;
 1010 
 1011     return GSS_S_COMPLETE;
 1012 
 1013 kerr:
 1014     k5_mutex_unlock(&cred->lock);
 1015     save_error_info(code, context);
 1016     *minor_status = code;
 1017     return GSS_S_CRED_UNAVAIL;
 1018 }
 1019 
 1020 OM_uint32
 1021 gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
 1022                             gss_cred_id_t *cred_handle,
 1023                             const gss_OID desired_oid,
 1024                             const gss_buffer_t value)
 1025 {
 1026     krb5_gss_cred_id_t cred;
 1027     krb5_error_code code;
 1028     krb5_context context;
 1029     krb5_rcache rcache;
 1030 
 1031     assert(value->length == sizeof(rcache));
 1032 
 1033     if (value->length != sizeof(rcache))
 1034         return GSS_S_FAILURE;
 1035 
 1036     rcache = (krb5_rcache)value->value;
 1037 
 1038     cred = (krb5_gss_cred_id_t)*cred_handle;
 1039 
 1040     code = krb5_gss_init_context(&context);
 1041     if (code) {
 1042         *minor_status = code;
 1043         return GSS_S_FAILURE;
 1044     }
 1045     if (cred->rcache != NULL)
 1046         k5_rc_close(context, cred->rcache);
 1047 
 1048     cred->rcache = rcache;
 1049 
 1050     krb5_free_context(context);
 1051 
 1052     *minor_status = 0;
 1053     return GSS_S_COMPLETE;
 1054 }
 1055 
 1056 /*
 1057  * krb5 and IAKERB mech API functions follow.  The mechglue always passes null
 1058  * desired_mechs and actual_mechs, so we ignore those parameters.
 1059  */
 1060 
 1061 OM_uint32 KRB5_CALLCONV
 1062 krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
 1063                       OM_uint32 time_req, gss_OID_set desired_mechs,
 1064                       gss_cred_usage_t cred_usage,
 1065                       gss_cred_id_t *output_cred_handle,
 1066                       gss_OID_set *actual_mechs, OM_uint32 *time_rec)
 1067 {
 1068     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
 1069                         NULL, NULL, FALSE, output_cred_handle, time_rec);
 1070 }
 1071 
 1072 OM_uint32 KRB5_CALLCONV
 1073 iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
 1074                         OM_uint32 time_req, gss_OID_set desired_mechs,
 1075                         gss_cred_usage_t cred_usage,
 1076                         gss_cred_id_t *output_cred_handle,
 1077                         gss_OID_set *actual_mechs, OM_uint32 *time_rec)
 1078 {
 1079     return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage,
 1080                         NULL, NULL, TRUE, output_cred_handle, time_rec);
 1081 }
 1082 
 1083 OM_uint32 KRB5_CALLCONV
 1084 krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
 1085                                     const gss_name_t desired_name,
 1086                                     const gss_buffer_t password,
 1087                                     OM_uint32 time_req,
 1088                                     const gss_OID_set desired_mechs,
 1089                                     int cred_usage,
 1090                                     gss_cred_id_t *output_cred_handle,
 1091                                     gss_OID_set *actual_mechs,
 1092                                     OM_uint32 *time_rec)
 1093 {
 1094     return acquire_cred(minor_status, desired_name, password, time_req,
 1095                         cred_usage, NULL, NULL, FALSE, output_cred_handle,
 1096                         time_rec);
 1097 }
 1098 
 1099 OM_uint32 KRB5_CALLCONV
 1100 iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
 1101                                       const gss_name_t desired_name,
 1102                                       const gss_buffer_t password,
 1103                                       OM_uint32 time_req,
 1104                                       const gss_OID_set desired_mechs,
 1105                                       int cred_usage,
 1106                                       gss_cred_id_t *output_cred_handle,
 1107                                       gss_OID_set *actual_mechs,
 1108                                       OM_uint32 *time_rec)
 1109 {
 1110     return acquire_cred(minor_status, desired_name, password, time_req,
 1111                         cred_usage, NULL, NULL, TRUE, output_cred_handle,
 1112                         time_rec);
 1113 }
 1114 
 1115 OM_uint32
 1116 gss_krb5int_import_cred(OM_uint32 *minor_status,
 1117                         gss_cred_id_t *cred_handle,
 1118                         const gss_OID desired_oid,
 1119                         const gss_buffer_t value)
 1120 {
 1121     struct krb5_gss_import_cred_req *req;
 1122     krb5_gss_name_rec name;
 1123     OM_uint32 time_rec;
 1124     krb5_error_code code;
 1125     gss_cred_usage_t usage;
 1126     gss_name_t desired_name = GSS_C_NO_NAME;
 1127 
 1128     assert(value->length == sizeof(*req));
 1129 
 1130     if (value->length != sizeof(*req))
 1131         return GSS_S_FAILURE;
 1132 
 1133     req = (struct krb5_gss_import_cred_req *)value->value;
 1134 
 1135     if (req->id != NULL) {
 1136         usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE;
 1137     } else if (req->keytab != NULL) {
 1138         usage = GSS_C_ACCEPT;
 1139     } else {
 1140         *minor_status = EINVAL;
 1141         return GSS_S_FAILURE;
 1142     }
 1143 
 1144     if (req->keytab_principal != NULL) {
 1145         memset(&name, 0, sizeof(name));
 1146         code = k5_mutex_init(&name.lock);
 1147         if (code != 0) {
 1148             *minor_status = code;
 1149             return GSS_S_FAILURE;
 1150         }
 1151         name.princ = req->keytab_principal;
 1152         desired_name = (gss_name_t)&name;
 1153     }
 1154 
 1155     code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE,
 1156                         usage, req->id, req->keytab, FALSE, cred_handle,
 1157                         &time_rec);
 1158     if (req->keytab_principal != NULL)
 1159         k5_mutex_destroy(&name.lock);
 1160     return code;
 1161 }
 1162 
 1163 OM_uint32 KRB5_CALLCONV
 1164 krb5_gss_acquire_cred_from(OM_uint32 *minor_status,
 1165                            const gss_name_t desired_name,
 1166                            OM_uint32 time_req,
 1167                            const gss_OID_set desired_mechs,
 1168                            gss_cred_usage_t cred_usage,
 1169                            gss_const_key_value_set_t cred_store,
 1170                            gss_cred_id_t *output_cred_handle,
 1171                            gss_OID_set *actual_mechs,
 1172                            OM_uint32 *time_rec)
 1173 {
 1174     krb5_context context = NULL;
 1175     krb5_error_code code = 0;
 1176     krb5_keytab client_keytab = NULL;
 1177     krb5_keytab keytab = NULL;
 1178     krb5_ccache ccache = NULL;
 1179     const char *rcname, *value;
 1180     OM_uint32 ret;
 1181 
 1182     code = gss_krb5int_initialize_library();
 1183     if (code) {
 1184         *minor_status = code;
 1185         ret = GSS_S_FAILURE;
 1186         goto out;
 1187     }
 1188 
 1189     code = krb5_gss_init_context(&context);
 1190     if (code) {
 1191         *minor_status = code;
 1192         ret = GSS_S_FAILURE;
 1193         goto out;
 1194     }
 1195 
 1196     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value);
 1197     if (GSS_ERROR(ret))
 1198         goto out;
 1199 
 1200     if (value) {
 1201         code = krb5_cc_resolve(context, value, &ccache);
 1202         if (code != 0) {
 1203             *minor_status = code;
 1204             ret = GSS_S_CRED_UNAVAIL;
 1205             goto out;
 1206         }
 1207     }
 1208 
 1209     ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value);
 1210     if (GSS_ERROR(ret))
 1211         goto out;
 1212 
 1213     if (value) {
 1214         code = krb5_kt_resolve(context, value, &client_keytab);
 1215         if (code != 0) {
 1216             *minor_status = code;
 1217             ret = GSS_S_CRED_UNAVAIL;
 1218             goto out;
 1219         }
 1220     }
 1221 
 1222     ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value);
 1223     if (GSS_ERROR(ret))
 1224         goto out;
 1225 
 1226     if (value) {
 1227         code = krb5_kt_resolve(context, value, &keytab);
 1228         if (code != 0) {
 1229             *minor_status = code;
 1230             ret = GSS_S_CRED_UNAVAIL;
 1231             goto out;
 1232         }
 1233     }
 1234 
 1235     ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname);
 1236     if (GSS_ERROR(ret))
 1237         goto out;
 1238 
 1239     ret = acquire_cred_context(context, minor_status, desired_name, NULL,
 1240                                time_req, cred_usage, ccache, client_keytab,
 1241                                keytab, rcname, 0, output_cred_handle,
 1242                                time_rec);
 1243 
 1244 out:
 1245     if (ccache != NULL)
 1246         krb5_cc_close(context, ccache);
 1247     if (client_keytab != NULL)
 1248         krb5_kt_close(context, client_keytab);
 1249     if (keytab != NULL)
 1250         krb5_kt_close(context, keytab);
 1251     krb5_free_context(context);
 1252     return ret;
 1253 }