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