1 /*
2  * Copyright 2005 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 
101 static OM_uint32
102 acquire_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    /* figure out what principal to use.  If the default name is
126       requested, use the default sn2princ output */
127 
128    if (desired_name == (gss_name_t) NULL) {
129       if ((code = krb5_sname_to_principal(context, NULL, NULL, KRB5_NT_SRV_HST,
130 					  &princ))) {
131 	 (void) krb5_kt_close(context, kt);
132 	 *minor_status = code;
133 	 return(GSS_S_FAILURE);
134       }
135       *output_princ = princ;
136    } else {
137       princ = (krb5_principal) desired_name;
138    }
139 
140    code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry);
141    if (code) {
142 	(void) krb5_kt_close(context, kt);
143 	if (code == KRB5_KT_NOTFOUND)
144 	     *minor_status = KG_KEYTAB_NOMATCH;
145 	else
146 	     *minor_status = code;
147 
148 	if (*output_princ != NULL) {
149 	    krb5_free_principal(context, *output_princ);
150 	    *output_princ = NULL;
151 	}
152 
153 	return(GSS_S_FAILURE);
154    }
155 
156    krb5_kt_free_entry(context, &entry);
157 
158    /* hooray.  we made it */
159 
160    cred->keytab = kt;
161 
162    /* Open the replay cache for this principal. */
163    if ((code = krb5_get_server_rcache(context,
164 				      krb5_princ_component(context, princ, 0),
165 				      &cred->rcache))) {
166        *minor_status = code;
167        return(GSS_S_FAILURE);
168    }
169 
170    return(GSS_S_COMPLETE);
171 }
172 
173 /* get credentials corresponding to the default credential cache.
174    If the default name is requested, return the name in output_princ.
175      If output_princ is non-NULL, the caller will use or free it, regardless
176      of the return value.
177    If successful, set the ccache-specific fields in cred.
178    */
179 
180 static OM_uint32
181 acquire_init_cred(context, minor_status, desired_name, output_princ, cred)
182      krb5_context context;
183      OM_uint32 *minor_status;
184      gss_name_t desired_name;
185      krb5_principal *output_princ;
186      krb5_gss_cred_id_rec *cred;
187 {
188    krb5_error_code code;
189    krb5_ccache ccache;
190    krb5_principal princ, tmp_princ;
191    krb5_flags flags;
192    krb5_cc_cursor cur;
193    krb5_creds creds;
194    int got_endtime;
195 
196    cred->ccache = NULL;
197 
198    /* SUNW14resync - do we need this? */
199 #if 0
200    /* load the GSS ccache name into the kg_context */
201    if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
202        return(GSS_S_FAILURE);
203 #endif
204 
205    /* open the default credential cache */
206 
207    code = krb5int_cc_default(context, &ccache);
208    if (code) {
209       *minor_status = code;
210       return(GSS_S_NO_CRED);
211    }
212 
213    /* turn off OPENCLOSE mode while extensive frobbing is going on */
214    /*
215     * SUNW14resync
216     * Added calls to krb5_cc_set_flags(... KRB5_TC_OPENCLOSE)
217     * on the error returns cuz the 1.4 krb5_cc_close does not always close
218     * the file like it used to and caused STC test gss.27 to fail.
219     */
220    flags = 0;		/* turns off OPENCLOSE mode */
221    if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) {
222       (void)krb5_cc_close(context, ccache);
223       *minor_status = code;
224       return(GSS_S_NO_CRED);
225    }
226 
227    /* get out the principal name and see if it matches */
228 
229    if ((code = krb5_cc_get_principal(context, ccache, &princ)) != 0) {
230       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
231       (void)krb5_cc_close(context, ccache);
232       *minor_status = code;
233       return(GSS_S_FAILURE);
234    }
235 
236    if (desired_name != (gss_name_t) NULL) {
237       if (! krb5_principal_compare(context, princ, (krb5_principal) desired_name)) {
238 	 (void)krb5_free_principal(context, princ);
239          (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
240 	 (void)krb5_cc_close(context, ccache);
241 	 *minor_status = KG_CCACHE_NOMATCH;
242 	 return(GSS_S_NO_CRED);
243       }
244       (void)krb5_free_principal(context, princ);
245       princ = (krb5_principal) desired_name;
246    } else {
247       *output_princ = princ;
248    }
249 
250    /* iterate over the ccache, find the tgt */
251 
252    if ((code = krb5_cc_start_seq_get(context, ccache, &cur)) != 0) {
253       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
254       (void)krb5_cc_close(context, ccache);
255       *minor_status = code;
256       return(GSS_S_FAILURE);
257    }
258 
259    /* this is hairy.  If there's a tgt for the principal's local realm
260       in here, that's what we want for the expire time.  But if
261       there's not, then we want to use the first key.  */
262 
263    got_endtime = 0;
264 
265    code = krb5_build_principal_ext(context, &tmp_princ,
266 				   krb5_princ_realm(context, princ)->length,
267 				   krb5_princ_realm(context, princ)->data,
268 				   6, "krbtgt",
269 				   krb5_princ_realm(context, princ)->length,
270 				   krb5_princ_realm(context, princ)->data,
271 				   0);
272    if (code) {
273       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
274       (void)krb5_cc_close(context, ccache);
275       *minor_status = code;
276       return(GSS_S_FAILURE);
277    }
278    while ((code = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) {
279       if (krb5_principal_compare(context, tmp_princ, creds.server)) {
280 	 cred->tgt_expire = creds.times.endtime;
281 	 got_endtime = 1;
282 	 *minor_status = 0;
283 	 code = 0;
284 	 krb5_free_cred_contents(context, &creds);
285 	 break;
286       }
287       if (got_endtime == 0) {
288 	 cred->tgt_expire = creds.times.endtime;
289 	 got_endtime = 1;
290       }
291       krb5_free_cred_contents(context, &creds);
292    }
293    krb5_free_principal(context, tmp_princ);
294 
295    if (code && code != KRB5_CC_END) {
296       /* this means some error occurred reading the ccache */
297       (void)krb5_cc_end_seq_get(context, ccache, &cur);
298       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
299       (void)krb5_cc_close(context, ccache);
300       *minor_status = code;
301       return(GSS_S_FAILURE);
302    } else if (! got_endtime) {
303       /* this means the ccache was entirely empty */
304       (void)krb5_cc_end_seq_get(context, ccache, &cur);
305       (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
306       (void)krb5_cc_close(context, ccache);
307       *minor_status = KG_EMPTY_CCACHE;
308       return(GSS_S_FAILURE);
309    } else {
310       /* this means that we found an endtime to use. */
311       if ((code = krb5_cc_end_seq_get(context, ccache, &cur)) != 0) {
312 	 (void)krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
313 	 (void)krb5_cc_close(context, ccache);
314 	 *minor_status = code;
315 	 return(GSS_S_FAILURE);
316       }
317       flags = KRB5_TC_OPENCLOSE;	/* turns on OPENCLOSE mode */
318       if ((code = krb5_cc_set_flags(context, ccache, flags)) != 0) {
319 	 (void)krb5_cc_close(context, ccache);
320 	 *minor_status = code;
321 	 return(GSS_S_FAILURE);
322       }
323    }
324 
325    /* the credentials match and are valid */
326 
327    cred->ccache = ccache;
328    /* minor_status is set while we are iterating over the ccache */
329    return(GSS_S_COMPLETE);
330 }
331 
332 OM_uint32
333 krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req,
334 		      desired_mechs, cred_usage, output_cred_handle,
335 		      actual_mechs, time_rec)
336      void *ctx;
337      OM_uint32 *minor_status;
338      gss_name_t desired_name;
339      OM_uint32 time_req;
340      gss_OID_set desired_mechs;
341      gss_cred_usage_t cred_usage;
342      gss_cred_id_t *output_cred_handle;
343      gss_OID_set *actual_mechs;
344      OM_uint32 *time_rec;
345 {
346     OM_uint32 ret;
347 
348     mutex_lock(&krb5_mutex);
349     ret = krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name,
350 	    time_req, desired_mechs, cred_usage, output_cred_handle,
351 	    actual_mechs, time_rec);
352     mutex_unlock(&krb5_mutex);
353     return(ret);
354 }
355 
356 /*ARGSUSED*/
357 OM_uint32
358 krb5_gss_acquire_cred_no_lock(ctx, minor_status, desired_name, time_req,
359 		      desired_mechs, cred_usage, output_cred_handle,
360 		      actual_mechs, time_rec)
361      void *ctx;
362      OM_uint32 *minor_status;
363      gss_name_t desired_name;
364      OM_uint32 time_req;
365      gss_OID_set desired_mechs;
366      gss_cred_usage_t cred_usage;
367      gss_cred_id_t *output_cred_handle;
368      gss_OID_set *actual_mechs;
369      OM_uint32 *time_rec;
370 {
371    krb5_context context;
372    size_t i;
373    krb5_gss_cred_id_t cred;
374    gss_OID_set ret_mechs = GSS_C_NULL_OID_SET;
375    const gss_OID_set_desc * valid_mechs;
376    int req_old, req_new;
377    OM_uint32 ret;
378    krb5_error_code code;
379 
380    /* Solaris Kerberos:  for MT safety, we avoid the use of a default
381     * context via kg_get_context() */
382 #if 0
383    if (GSS_ERROR(kg_get_context(minor_status, &context)))
384       return(GSS_S_FAILURE);
385 #endif
386 
387    context = ctx;
388 
389    /* make sure all outputs are valid */
390 
391    *output_cred_handle = NULL;
392    if (actual_mechs)
393       *actual_mechs = NULL;
394    if (time_rec)
395       *time_rec = 0;
396 
397    /* validate the name */
398 
399    /*SUPPRESS 29*/
400    if ((desired_name != (gss_name_t) NULL) &&
401        (! kg_validate_name(desired_name))) {
402       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
403       return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
404    }
405 
406    /* verify that the requested mechanism set is the default, or
407       contains krb5 */
408 
409    if (desired_mechs == GSS_C_NULL_OID_SET) {
410       valid_mechs = gss_mech_set_krb5_both;
411       req_old = 1;
412       req_new = 1;
413    } else {
414       req_old = 0;
415       req_new = 0;
416 
417       for (i=0; i<desired_mechs->count; i++) {
418 	 if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i])))
419 	    req_old++;
420 	 if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i])))
421 	    req_new++;
422       }
423 
424       if (!req_old && !req_new) {
425 	 *minor_status = 0;
426 	 return(GSS_S_BAD_MECH);
427       }
428    }
429 
430    /* create the gss cred structure */
431 
432    if ((cred =
433 	(krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) {
434       *minor_status = ENOMEM;
435       return(GSS_S_FAILURE);
436    }
437    memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
438 
439    cred->usage = cred_usage;
440    cred->princ = NULL;
441    cred->actual_mechs = valid_mechs;
442    cred->prerfc_mech = req_old;
443    cred->rfc_mech = req_new;
444 
445    cred->keytab = NULL;
446    cred->ccache = NULL;
447 
448    if ((cred_usage != GSS_C_INITIATE) &&
449        (cred_usage != GSS_C_ACCEPT) &&
450        (cred_usage != GSS_C_BOTH)) {
451       xfree(cred);
452       *minor_status = (OM_uint32) G_BAD_USAGE;
453       return(GSS_S_FAILURE);
454    }
455 
456    /* if requested, acquire credentials for accepting */
457    /* this will fill in cred->princ if the desired_name is not specified */
458 
459    if ((cred_usage == GSS_C_ACCEPT) ||
460        (cred_usage == GSS_C_BOTH))
461       if ((ret = acquire_accept_cred(context, minor_status, desired_name,
462 				     &(cred->princ), cred))
463 	  != GSS_S_COMPLETE) {
464 	 if (cred->princ)
465 	    krb5_free_principal(context, cred->princ);
466 	 xfree(cred);
467 	 /* minor_status set by acquire_accept_cred() */
468 	 return(ret);
469       }
470 
471    /* if requested, acquire credentials for initiation */
472    /* this will fill in cred->princ if it wasn't set above, and
473       the desired_name is not specified */
474 
475    if ((cred_usage == GSS_C_INITIATE) ||
476        (cred_usage == GSS_C_BOTH))
477       if ((ret =
478 	   acquire_init_cred(context, minor_status,
479 			     cred->princ?(gss_name_t)cred->princ:desired_name,
480 			     &(cred->princ), cred))
481 	  != GSS_S_COMPLETE) {
482 	 if (cred->keytab)
483 	    (void) krb5_kt_close(context, cred->keytab);
484 	 if (cred->princ)
485 	    krb5_free_principal(context, cred->princ);
486 	 xfree(cred);
487 	 /* minor_status set by acquire_init_cred() */
488 	 return(ret);
489       }
490 
491    /* if the princ wasn't filled in already, fill it in now */
492 
493    if (!cred->princ)
494       if ((code = krb5_copy_principal(context, (krb5_principal) desired_name,
495 				      &(cred->princ)))) {
496 	 if (cred->ccache)
497 	    (void)krb5_cc_close(context, cred->ccache);
498 	 if (cred->keytab)
499 	    (void)krb5_kt_close(context, cred->keytab);
500 	 xfree(cred);
501 	 *minor_status = code;
502 	 return(GSS_S_FAILURE);
503       }
504 
505    /*** at this point, the cred structure has been completely created */
506 
507    /* compute time_rec */
508 
509    if (cred_usage == GSS_C_ACCEPT) {
510       if (time_rec)
511 	 *time_rec = GSS_C_INDEFINITE;
512    } else {
513       krb5_timestamp now;
514 
515       if ((code = krb5_timeofday(context, &now))) {
516 	 if (cred->ccache)
517 	    (void)krb5_cc_close(context, cred->ccache);
518 	 if (cred->keytab)
519 	    (void)krb5_kt_close(context, cred->keytab);
520 	 if (cred->princ)
521 	    krb5_free_principal(context, cred->princ);
522 	 xfree(cred);
523 	 *minor_status = code;
524 	 return(GSS_S_FAILURE);
525       }
526 
527       if (time_rec)
528 	 *time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
529    }
530 
531    /* create mechs */
532 
533    if (actual_mechs) {
534        if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status,
535 							    &ret_mechs)) ||
536 	   (cred->prerfc_mech &&
537 	    GSS_ERROR(ret = gss_add_oid_set_member(minor_status,
538 							   (gss_OID) gss_mech_krb5_old,
539 							   &ret_mechs))) ||
540 	   (cred->rfc_mech &&
541 	    GSS_ERROR(ret = gss_add_oid_set_member(minor_status,
542 							   (gss_OID) gss_mech_krb5,
543 							   &ret_mechs)))) {
544 	   if (cred->ccache)
545 	       (void)krb5_cc_close(context, cred->ccache);
546 	   if (cred->keytab)
547 	       (void)krb5_kt_close(context, cred->keytab);
548 	   if (cred->princ)
549 	       krb5_free_principal(context, cred->princ);
550 	   xfree(cred);
551 	   /* (*minor_status) set above */
552 	   return(ret);
553        }
554    }
555 
556    /* intern the credential handle */
557 
558    if (! kg_save_cred_id((gss_cred_id_t) cred)) {
559       (void) gss_release_oid_set(NULL, &ret_mechs);
560       free(ret_mechs->elements);
561       free(ret_mechs);
562       if (cred->ccache)
563 	 (void)krb5_cc_close(context, cred->ccache);
564       if (cred->keytab)
565 	 (void)krb5_kt_close(context, cred->keytab);
566       if (cred->princ)
567 	 krb5_free_principal(context, cred->princ);
568       xfree(cred);
569       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
570       return(GSS_S_FAILURE);
571    }
572 
573    /* return success */
574 
575    *minor_status = 0;
576    *output_cred_handle = (gss_cred_id_t) cred;
577    if (actual_mechs)
578       *actual_mechs = ret_mechs;
579    return(GSS_S_COMPLETE);
580 }
581