acquire_cred.c revision 3376:15d24e91f408
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/*
34 * Copyright 1993 by OpenVision Technologies, Inc.
35 *
36 * Permission to use, copy, modify, distribute, and sell this software
37 * and its documentation for any purpose is hereby granted without fee,
38 * provided that the above copyright notice appears in all copies and
39 * that both that copyright notice and this permission notice appear in
40 * supporting documentation, and that the name of OpenVision not be used
41 * in advertising or publicity pertaining to distribution of the software
42 * without specific, written prior permission. OpenVision makes no
43 * representations about the suitability of this software for any
44 * purpose.  It is provided "as is" without express or implied warranty.
45 *
46 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
47 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
48 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
49 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
50 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
51 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52 * PERFORMANCE OF THIS SOFTWARE.
53 */
54
55/*
56 * Copyright (C) 1998 by the FundsXpress, INC.
57 *
58 * All rights reserved.
59 *
60 * Export of this software from the United States of America may require
61 * a specific license from the United States Government.  It is the
62 * responsibility of any person or organization contemplating export to
63 * obtain such a license before exporting.
64 *
65 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
66 * distribute this software and its documentation for any purpose and
67 * without fee is hereby granted, provided that the above copyright
68 * notice appear in all copies and that both that copyright notice and
69 * this permission notice appear in supporting documentation, and that
70 * the name of FundsXpress. not be used in advertising or publicity pertaining
71 * to distribution of the software without specific, written prior
72 * permission.  FundsXpress makes no representations about the suitability of
73 * this software for any purpose.  It is provided "as is" without express
74 * or implied warranty.
75 *
76 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
77 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
78 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
79 */
80
81#include <gssapiP_krb5.h>
82#include <k5-int.h>
83
84#ifdef HAVE_STRING_H
85#include <string.h>
86#else
87#include <strings.h>
88#endif
89
90/*
91 * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $
92 */
93
94/* get credentials corresponding to a key in the krb5 keytab.
95   If the default name is requested, return the name in output_princ.
96     If output_princ is non-NULL, the caller will use or free it, regardless
97     of the return value.
98   If successful, set the keytab-specific fields in cred
99   */
100
101static OM_uint32
102acquire_accept_cred(context, minor_status, desired_name, output_princ, cred)
103     krb5_context context;
104     OM_uint32 *minor_status;
105     gss_name_t desired_name;
106     krb5_principal *output_princ;
107     krb5_gss_cred_id_rec *cred;
108{
109   krb5_error_code code;
110   krb5_principal princ;
111   krb5_keytab kt;
112   krb5_keytab_entry entry;
113
114   *output_princ = NULL;
115   cred->keytab = NULL;
116
117   /* open the default keytab */
118
119   if ((code = krb5_kt_default(context, &kt))) {
120      *minor_status = code;
121      /* NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
122      return(GSS_S_NO_CRED);
123   }
124
125   if (desired_name != GSS_C_NO_NAME) {
126      princ = (krb5_principal) desired_name;
127      if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) {
128	 (void) krb5_kt_close(context, kt);
129	 if (code == KRB5_KT_NOTFOUND)
130	    *minor_status = KG_KEYTAB_NOMATCH;
131	 else
132	    *minor_status = code;
133      /* NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
134	 return(GSS_S_NO_CRED);
135      }
136      krb5_kt_free_entry(context, &entry);
137
138      /* Open the replay cache for this principal. */
139      if ((code = krb5_get_server_rcache(context,
140					 krb5_princ_component(context, princ, 0),
141					 &cred->rcache))) {
142	 *minor_status = code;
143	 return(GSS_S_FAILURE);
144      }
145
146   }
147
148   /* hooray.  we made it */
149
150   cred->keytab = kt;
151
152   return(GSS_S_COMPLETE);
153}
154
155/* get credentials corresponding to the default credential cache.
156   If the default name is requested, return the name in output_princ.
157     If output_princ is non-NULL, the caller will use or free it, regardless
158     of the return value.
159   If successful, set the ccache-specific fields in cred.
160   */
161
162static OM_uint32
163acquire_init_cred(context, minor_status, desired_name, output_princ, cred)
164     krb5_context context;
165     OM_uint32 *minor_status;
166     gss_name_t desired_name;
167     krb5_principal *output_princ;
168     krb5_gss_cred_id_rec *cred;
169{
170   krb5_error_code code;
171   krb5_ccache ccache;
172   krb5_principal princ, tmp_princ;
173   krb5_flags flags;
174   krb5_cc_cursor cur;
175   krb5_creds creds;
176   int got_endtime;
177
178   cred->ccache = NULL;
179
180   /* SUNW14resync - do we need this? */
181#if 0
182   /* load the GSS ccache name into the kg_context */
183   if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
184       return(GSS_S_FAILURE);
185#endif
186
187   /* open the default credential cache */
188
189   code = krb5int_cc_default(context, &ccache);
190   if (code) {
191      *minor_status = code;
192      return(GSS_S_NO_CRED);
193   }
194
195   /* turn off OPENCLOSE mode while extensive frobbing is going on */
196   /*
197    * SUNW14resync
198    * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE)
199    * on the error returns cuz the 1.4 krb5_cc_close does not always close
200    * the file like it used to and caused STC test gss.27 to fail.
201    */
202   flags = 0;		/* turns off OPENCLOSE mode */
203   if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) {
204      (void)krb5_cc_close(context, ccache);
205      *minor_status = code;
206      return(GSS_S_NO_CRED);
207   }
208
209   /* get out the principal name and see if it matches */
210
211   if ((code = krb5_cc_get_principal(context, ccache, &princ)) != 0) {
212      (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
213      (void)krb5_cc_close(context, ccache);
214      *minor_status = code;
215      return(GSS_S_FAILURE);
216   }
217
218   if (desired_name != (gss_name_t) NULL) {
219      if (! krb5_principal_compare(context, princ, (krb5_principal) desired_name)) {
220	 (void)krb5_free_principal(context, princ);
221         (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
222	 (void)krb5_cc_close(context, ccache);
223	 *minor_status = KG_CCACHE_NOMATCH;
224	 return(GSS_S_NO_CRED);
225      }
226      (void)krb5_free_principal(context, princ);
227      princ = (krb5_principal) desired_name;
228   } else {
229      *output_princ = princ;
230   }
231
232   /* iterate over the ccache, find the tgt */
233
234   if ((code = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) {
235      (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
236      (void)krb5_cc_close(context, ccache);
237      *minor_status = code;
238      return(GSS_S_FAILURE);
239   }
240
241   /* this is hairy.  If there's a tgt for the principal's local realm
242      in here, that's what we want for the expire time.  But if
243      there's not, then we want to use the first key.  */
244
245   got_endtime = 0;
246
247   code = krb5_build_principal_ext(context, &tmp_princ,
248				   krb5_princ_realm(context, princ)->length,
249				   krb5_princ_realm(context, princ)->data,
250				   6, "krbtgt",
251				   krb5_princ_realm(context, princ)->length,
252				   krb5_princ_realm(context, princ)->data,
253				   0);
254   if (code) {
255      (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
256      (void)krb5_cc_close(context, ccache);
257      *minor_status = code;
258      return(GSS_S_FAILURE);
259   }
260   while ((code = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) {
261      if (krb5_principal_compare(context, tmp_princ, creds.server)) {
262	 cred->tgt_expire = creds.times.endtime;
263	 got_endtime = 1;
264	 *minor_status = 0;
265	 code = 0;
266	 krb5_free_cred_contents(context, &creds);
267	 break;
268      }
269      if (got_endtime == 0) {
270	 cred->tgt_expire = creds.times.endtime;
271	 got_endtime = 1;
272      }
273      krb5_free_cred_contents(context, &creds);
274   }
275   krb5_free_principal(context, tmp_princ);
276
277   if (code && code != KRB5_CC_END) {
278      /* this means some error occurred reading the ccache */
279      (void)krb5_cc_end_seq_get(context, ccache, &cur);
280      (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
281      (void)krb5_cc_close(context, ccache);
282      *minor_status = code;
283      return(GSS_S_FAILURE);
284   } else if (! got_endtime) {
285      /* this means the ccache was entirely empty */
286      (void)krb5_cc_end_seq_get(context, ccache, &cur);
287      (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
288      (void)krb5_cc_close(context, ccache);
289      *minor_status = KG_EMPTY_CCACHE;
290      return(GSS_S_FAILURE);
291   } else {
292      /* this means that we found an endtime to use. */
293      if ((code = krb5_cc_end_seq_get(context, ccache, &cur)) != 0) {
294	 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
295	 (void)krb5_cc_close(context, ccache);
296	 *minor_status = code;
297	 return(GSS_S_FAILURE);
298      }
299      flags = KRB5_TC_OPENCLOSE;	/* turns on OPENCLOSE mode */
300      if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) {
301	 (void)krb5_cc_close(context, ccache);
302	 *minor_status = code;
303	 return(GSS_S_FAILURE);
304      }
305   }
306
307   /* the credentials match and are valid */
308
309   cred->ccache = ccache;
310   /* minor_status is set while we are iterating over the ccache */
311   return(GSS_S_COMPLETE);
312}
313
314OM_uint32
315krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req,
316		      desired_mechs, cred_usage, output_cred_handle,
317		      actual_mechs, time_rec)
318     void *ctx;
319     OM_uint32 *minor_status;
320     gss_name_t desired_name;
321     OM_uint32 time_req;
322     gss_OID_set desired_mechs;
323     gss_cred_usage_t cred_usage;
324     gss_cred_id_t *output_cred_handle;
325     gss_OID_set *actual_mechs;
326     OM_uint32 *time_rec;
327{
328    OM_uint32 ret;
329
330    mutex_lock(&krb5_mutex);
331    ret = krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name,
332	    time_req, desired_mechs, cred_usage, output_cred_handle,
333	    actual_mechs, time_rec);
334    mutex_unlock(&krb5_mutex);
335    return(ret);
336}
337
338/*ARGSUSED*/
339OM_uint32
340krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, time_req,
341		      desired_mechs, cred_usage, output_cred_handle,
342		      actual_mechs, time_rec)
343     void *ctx;
344     OM_uint32 *minor_status;
345     gss_name_t desired_name;
346     OM_uint32 time_req;
347     gss_OID_set desired_mechs;
348     gss_cred_usage_t cred_usage;
349     gss_cred_id_t *output_cred_handle;
350     gss_OID_set *actual_mechs;
351     OM_uint32 *time_rec;
352{
353   krb5_context context;
354   size_t i;
355   krb5_gss_cred_id_t cred;
356   gss_OID_set ret_mechs = GSS_C_NULL_OID_SET;
357   const gss_OID_set_desc * valid_mechs;
358   int req_old, req_new;
359   OM_uint32 ret;
360   krb5_error_code code;
361
362   /* Solaris Kerberos:  for MT safety, we avoid the use of a default
363    * context via kg_get_context() */
364#if 0
365   if (GSS_ERROR(kg_get_context(minor_status, &context)))
366      return(GSS_S_FAILURE);
367#endif
368
369   context = ctx;
370
371   /* make sure all outputs are valid */
372
373   *output_cred_handle = NULL;
374   if (actual_mechs)
375      *actual_mechs = NULL;
376   if (time_rec)
377      *time_rec = 0;
378
379   /* validate the name */
380
381   /*SUPPRESS 29*/
382   if ((desired_name != (gss_name_t) NULL) &&
383       (! kg_validate_name(desired_name))) {
384      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
385      return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
386   }
387
388   /* verify that the requested mechanism set is the default, or
389      contains krb5 */
390
391   if (desired_mechs == GSS_C_NULL_OID_SET) {
392      valid_mechs = gss_mech_set_krb5_both;
393      req_old = 1;
394      req_new = 1;
395   } else {
396      req_old = 0;
397      req_new = 0;
398
399      for (i=0; i<desired_mechs->count; i++) {
400	 if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i])))
401	    req_old++;
402	 if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i])))
403	    req_new++;
404      }
405
406      if (!req_old && !req_new) {
407	 *minor_status = 0;
408	 return(GSS_S_BAD_MECH);
409      }
410   }
411
412   /* create the gss cred structure */
413
414   if ((cred =
415	(krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) {
416      *minor_status = ENOMEM;
417      return(GSS_S_FAILURE);
418   }
419   memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
420
421   cred->usage = cred_usage;
422   cred->princ = NULL;
423   cred->actual_mechs = valid_mechs;
424   cred->prerfc_mech = req_old;
425   cred->rfc_mech = req_new;
426
427   cred->keytab = NULL;
428   cred->ccache = NULL;
429
430   if ((cred_usage != GSS_C_INITIATE) &&
431       (cred_usage != GSS_C_ACCEPT) &&
432       (cred_usage != GSS_C_BOTH)) {
433      xfree(cred);
434      *minor_status = (OM_uint32) G_BAD_USAGE;
435      return(GSS_S_FAILURE);
436   }
437
438   /* if requested, acquire credentials for accepting */
439   /* this will fill in cred->princ if the desired_name is not specified */
440
441   if ((cred_usage == GSS_C_ACCEPT) ||
442       (cred_usage == GSS_C_BOTH))
443      if ((ret = acquire_accept_cred(context, minor_status, desired_name,
444				     &(cred->princ), cred))
445	  != GSS_S_COMPLETE) {
446	 if (cred->princ)
447	    krb5_free_principal(context, cred->princ);
448	 xfree(cred);
449	 /* minor_status set by acquire_accept_cred() */
450	 return(ret);
451      }
452
453   /* if requested, acquire credentials for initiation */
454   /* this will fill in cred->princ if it wasn't set above, and
455      the desired_name is not specified */
456
457   if ((cred_usage == GSS_C_INITIATE) ||
458       (cred_usage == GSS_C_BOTH))
459      if ((ret =
460	   acquire_init_cred(context, minor_status,
461			     cred->princ?(gss_name_t)cred->princ:desired_name,
462			     &(cred->princ), cred))
463	  != GSS_S_COMPLETE) {
464	 if (cred->keytab)
465	    (void) krb5_kt_close(context, cred->keytab);
466	 if (cred->princ)
467	    krb5_free_principal(context, cred->princ);
468	 xfree(cred);
469	 /* minor_status set by acquire_init_cred() */
470	 return(ret);
471      }
472
473   /* Solaris Kerberos:
474    * if the princ wasn't filled in already, fill it in now unless
475    * a cred with no associated princ is requested (will invoke default
476    * behaviour when gss_accept_init_context() is called).
477    */
478   if (!cred->princ && (desired_name != GSS_C_NO_NAME))
479      if ((code = krb5_copy_principal(context, (krb5_principal) desired_name,
480				      &(cred->princ)))) {
481	 if (cred->ccache)
482	    (void)krb5_cc_close(context, cred->ccache);
483	 if (cred->keytab)
484	    (void)krb5_kt_close(context, cred->keytab);
485	 xfree(cred);
486	 *minor_status = code;
487	 return(GSS_S_FAILURE);
488      }
489
490   /*** at this point, the cred structure has been completely created */
491
492   /* compute time_rec */
493
494   if (cred_usage == GSS_C_ACCEPT) {
495      if (time_rec)
496	 *time_rec = GSS_C_INDEFINITE;
497   } else {
498      krb5_timestamp now;
499
500      if ((code = krb5_timeofday(context, &now))) {
501	 if (cred->ccache)
502	    (void)krb5_cc_close(context, cred->ccache);
503	 if (cred->keytab)
504	    (void)krb5_kt_close(context, cred->keytab);
505	 if (cred->princ)
506	    krb5_free_principal(context, cred->princ);
507	 xfree(cred);
508	 *minor_status = code;
509	 return(GSS_S_FAILURE);
510      }
511
512      if (time_rec)
513	 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
514   }
515
516   /* create mechs */
517
518   if (actual_mechs) {
519       if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status,
520							    &ret_mechs)) ||
521	   (cred->prerfc_mech &&
522	    GSS_ERROR(ret = gss_add_oid_set_member(minor_status,
523							   (gss_OID) gss_mech_krb5_old,
524							   &ret_mechs))) ||
525	   (cred->rfc_mech &&
526	    GSS_ERROR(ret = gss_add_oid_set_member(minor_status,
527							   (gss_OID) gss_mech_krb5,
528							   &ret_mechs)))) {
529	   if (cred->ccache)
530	       (void)krb5_cc_close(context, cred->ccache);
531	   if (cred->keytab)
532	       (void)krb5_kt_close(context, cred->keytab);
533	   if (cred->princ)
534	       krb5_free_principal(context, cred->princ);
535	   xfree(cred);
536	   /* (*minor_status) set above */
537	   return(ret);
538       }
539   }
540
541   /* intern the credential handle */
542
543   if (! kg_save_cred_id((gss_cred_id_t) cred)) {
544      (void) gss_release_oid_set(NULL, &ret_mechs);
545      free(ret_mechs->elements);
546      free(ret_mechs);
547      if (cred->ccache)
548	 (void)krb5_cc_close(context, cred->ccache);
549      if (cred->keytab)
550	 (void)krb5_kt_close(context, cred->keytab);
551      if (cred->princ)
552	 krb5_free_principal(context, cred->princ);
553      xfree(cred);
554      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
555      return(GSS_S_FAILURE);
556   }
557
558   /* return success */
559
560   *minor_status = 0;
561   *output_cred_handle = (gss_cred_id_t) cred;
562   if (actual_mechs)
563      *actual_mechs = ret_mechs;
564   return(GSS_S_COMPLETE);
565}
566