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