xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_acquire_cred.c (revision 328110da2661a8841f12000b99fea27ceacdd5b2)
1 /* #pragma ident	"@(#)g_acquire_cred.c	1.22	04/02/23 SMI" */
2 
3 /*
4  * Copyright 1996 by Sun Microsystems, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear in
10  * supporting documentation, and that the name of Sun Microsystems not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission. Sun Microsystems makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 /*
26  *  glue routine for gss_acquire_cred
27  */
28 
29 #include "mglueP.h"
30 #include <stdio.h>
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #include <string.h>
35 #include <errno.h>
36 #include <time.h>
37 
38 static OM_uint32
39 val_acq_cred_args(
40     OM_uint32 *minor_status,
41     gss_name_t desired_name,
42     OM_uint32 time_req,
43     gss_OID_set desired_mechs,
44     int cred_usage,
45     gss_const_key_value_set_t cred_store,
46     gss_cred_id_t *output_cred_handle,
47     gss_OID_set *actual_mechs,
48     OM_uint32 *time_rec)
49 {
50 
51     /* Initialize outputs. */
52 
53     if (minor_status != NULL)
54 	*minor_status = 0;
55 
56     if (output_cred_handle != NULL)
57 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
58 
59     if (actual_mechs != NULL)
60 	*actual_mechs = GSS_C_NULL_OID_SET;
61 
62     if (time_rec != NULL)
63 	*time_rec = 0;
64 
65     /* Validate arguments. */
66 
67     if (minor_status == NULL)
68 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
69 
70     if (output_cred_handle == NULL)
71 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
72 
73     if (cred_usage != GSS_C_ACCEPT
74 	&& cred_usage != GSS_C_INITIATE
75 	&& cred_usage != GSS_C_BOTH) {
76 	if (minor_status) {
77 	    *minor_status = EINVAL;
78 	    map_errcode(minor_status);
79 	}
80 	return GSS_S_FAILURE;
81     }
82 
83     return (GSS_S_COMPLETE);
84 }
85 
86 
87 OM_uint32 KRB5_CALLCONV
88 gss_acquire_cred(minor_status,
89                  desired_name,
90                  time_req,
91                  desired_mechs,
92 		 cred_usage,
93                  output_cred_handle,
94                  actual_mechs,
95                  time_rec)
96 
97 OM_uint32 *		minor_status;
98 gss_name_t		desired_name;
99 OM_uint32		time_req;
100 gss_OID_set		desired_mechs;
101 int			cred_usage;
102 gss_cred_id_t *		output_cred_handle;
103 gss_OID_set *		actual_mechs;
104 OM_uint32 *		time_rec;
105 
106 {
107     return gss_acquire_cred_from(minor_status, desired_name, time_req,
108 				 desired_mechs, cred_usage, NULL,
109 				 output_cred_handle, actual_mechs, time_rec);
110 }
111 
112 OM_uint32 KRB5_CALLCONV
113 gss_acquire_cred_from(minor_status,
114 		      desired_name,
115 		      time_req,
116 		      desired_mechs,
117 		      cred_usage,
118 		      cred_store,
119 		      output_cred_handle,
120 		      actual_mechs,
121 		      time_rec)
122 
123 OM_uint32 *			minor_status;
124 gss_name_t			desired_name;
125 OM_uint32			time_req;
126 gss_OID_set			desired_mechs;
127 int				cred_usage;
128 gss_const_key_value_set_t	cred_store;
129 gss_cred_id_t *			output_cred_handle;
130 gss_OID_set *			actual_mechs;
131 OM_uint32 *			time_rec;
132 
133 {
134     OM_uint32 major = GSS_S_FAILURE, tmpMinor;
135     OM_uint32 first_major = GSS_S_COMPLETE, first_minor = 0;
136     OM_uint32 initTimeOut = 0, acceptTimeOut = 0, outTime = GSS_C_INDEFINITE;
137     gss_OID_set mechs = GSS_C_NO_OID_SET;
138     gss_OID_set_desc except_attrs;
139     gss_OID_desc attr_oids[2];
140     unsigned int i;
141     gss_union_cred_t creds = NULL;
142 
143     major = val_acq_cred_args(minor_status,
144 			      desired_name,
145 			      time_req,
146 			      desired_mechs,
147 			      cred_usage,
148 			      cred_store,
149 			      output_cred_handle,
150 			      actual_mechs,
151 			      time_rec);
152     if (major != GSS_S_COMPLETE)
153 	goto cleanup;
154 
155     /*
156      * if desired_mechs equals GSS_C_NULL_OID_SET, then try to
157      * acquire credentials for all non-deprecated mechanisms.
158      */
159     if (desired_mechs == GSS_C_NULL_OID_SET) {
160 	attr_oids[0] = *GSS_C_MA_DEPRECATED;
161 	attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
162 	except_attrs.count = 2;
163 	except_attrs.elements = attr_oids;
164 	major = gss_indicate_mechs_by_attrs(minor_status, GSS_C_NO_OID_SET,
165 					    &except_attrs, GSS_C_NO_OID_SET,
166 					    &mechs);
167 	if (major != GSS_S_COMPLETE)
168 	    goto cleanup;
169     } else
170 	mechs = desired_mechs;
171 
172     if (mechs->count == 0) {
173 	major = GSS_S_BAD_MECH;
174 	goto cleanup;
175     }
176 
177     /* allocate the output credential structure */
178     creds = (gss_union_cred_t)calloc(1, sizeof (gss_union_cred_desc));
179     if (creds == NULL) {
180 	major = GSS_S_FAILURE;
181 	*minor_status = ENOMEM;
182 	goto cleanup;
183     }
184 
185     creds->count = 0;
186     creds->loopback = creds;
187 
188     /* for each requested mech attempt to obtain a credential */
189     for (i = 0, major = GSS_S_UNAVAILABLE; i < mechs->count; i++) {
190 	major = gss_add_cred_from(&tmpMinor, (gss_cred_id_t)creds,
191 				  desired_name, &mechs->elements[i],
192 				  cred_usage, time_req, time_req,
193 				  cred_store, NULL, NULL,
194 				  time_rec ? &initTimeOut : NULL,
195 				  time_rec ? &acceptTimeOut : NULL);
196 	if (major == GSS_S_COMPLETE) {
197 	    /* update the credential's time */
198 	    if (cred_usage == GSS_C_ACCEPT) {
199 		if (outTime > acceptTimeOut)
200 		    outTime = acceptTimeOut;
201 	    } else if (cred_usage == GSS_C_INITIATE) {
202 		if (outTime > initTimeOut)
203 		    outTime = initTimeOut;
204 	    } else {
205 		/*
206 		 * time_rec is the lesser of the
207 		 * init/accept times
208 		 */
209 		if (initTimeOut > acceptTimeOut)
210 		    outTime = (outTime > acceptTimeOut) ?
211 			acceptTimeOut : outTime;
212 		else
213 		    outTime = (outTime > initTimeOut) ?
214 			initTimeOut : outTime;
215 	    }
216 	} else if (first_major == GSS_S_COMPLETE) {
217 	    first_major = major;
218 	    first_minor = tmpMinor;
219 	}
220     } /* for */
221 
222     /* If we didn't get any creds, return the error status from the first mech
223      * (which is often the preferred one). */
224     if (creds->count < 1) {
225 	major = first_major;
226 	*minor_status = first_minor;
227 	goto cleanup;
228     }
229     major = GSS_S_COMPLETE;
230 
231     /*
232      * fill in output parameters
233      * setup the actual mechs output parameter
234      */
235     if (actual_mechs != NULL) {
236 	major = gssint_make_public_oid_set(minor_status, creds->mechs_array,
237 					   creds->count, actual_mechs);
238 	if (GSS_ERROR(major))
239 	    goto cleanup;
240     }
241 
242     if (time_rec)
243 	*time_rec = outTime;
244 
245     *output_cred_handle = (gss_cred_id_t)creds;
246 
247 cleanup:
248     if (GSS_ERROR(major))
249 	gss_release_cred(&tmpMinor, (gss_cred_id_t *)&creds);
250     if (desired_mechs == GSS_C_NO_OID_SET)
251         generic_gss_release_oid_set(&tmpMinor, &mechs);
252 
253     return (major);
254 }
255 
256 static OM_uint32
257 val_add_cred_args(
258     OM_uint32 *minor_status,
259     gss_cred_id_t input_cred_handle,
260     gss_name_t desired_name,
261     gss_OID desired_mech,
262     gss_cred_usage_t cred_usage,
263     gss_const_key_value_set_t cred_store,
264     OM_uint32 initiator_time_req,
265     OM_uint32 acceptor_time_req,
266     gss_cred_id_t *output_cred_handle,
267     gss_OID_set *actual_mechs,
268     OM_uint32 *initiator_time_rec,
269     OM_uint32 *acceptor_time_rec)
270 {
271 
272     /* Initialize outputs. */
273 
274     if (minor_status != NULL)
275 	*minor_status = 0;
276 
277     if (output_cred_handle != NULL)
278 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
279 
280     if (actual_mechs != NULL)
281 	*actual_mechs = GSS_C_NO_OID_SET;
282 
283     if (acceptor_time_rec != NULL)
284 	*acceptor_time_rec = 0;
285 
286     if (initiator_time_rec != NULL)
287 	*initiator_time_rec = 0;
288 
289     /* Validate arguments. */
290 
291     if (minor_status == NULL)
292 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
293 
294     if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
295 	output_cred_handle == NULL)
296 	return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
297 
298     if (cred_usage != GSS_C_ACCEPT
299 	&& cred_usage != GSS_C_INITIATE
300 	&& cred_usage != GSS_C_BOTH) {
301 	if (minor_status) {
302 	    *minor_status = EINVAL;
303 	    map_errcode(minor_status);
304 	}
305 	return GSS_S_FAILURE;
306     }
307 
308     return (GSS_S_COMPLETE);
309 }
310 
311 /* Copy a mechanism credential (with the mechanism given by mech_oid) as
312  * faithfully as possible. */
313 static OM_uint32
314 copy_mech_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in,
315 	       gss_OID mech_oid, gss_cred_id_t *cred_out)
316 {
317     OM_uint32 status, tmpmin;
318     gss_mechanism mech;
319     gss_buffer_desc buf;
320     gss_name_t name;
321     OM_uint32 life;
322     gss_cred_usage_t usage;
323     gss_OID_set_desc oidset;
324 
325     mech = gssint_get_mechanism(mech_oid);
326     if (mech == NULL)
327 	return (GSS_S_BAD_MECH);
328     if (mech->gss_export_cred != NULL && mech->gss_import_cred != NULL) {
329 	status = mech->gss_export_cred(minor_status, cred_in, &buf);
330 	if (status != GSS_S_COMPLETE)
331 	    return (status);
332 	status = mech->gss_import_cred(minor_status, &buf, cred_out);
333 	(void) gss_release_buffer(&tmpmin, &buf);
334     } else if (mech->gss_inquire_cred != NULL &&
335 	       mech->gss_acquire_cred != NULL) {
336 	status = mech->gss_inquire_cred(minor_status, cred_in, &name, &life,
337 					&usage, NULL);
338 	if (status != GSS_S_COMPLETE)
339 	    return (status);
340 	oidset.count = 1;
341 	oidset.elements = gssint_get_public_oid(mech_oid);
342 	status = mech->gss_acquire_cred(minor_status, name, life, &oidset,
343 					usage, cred_out, NULL, NULL);
344 	gss_release_name(&tmpmin, &name);
345     } else {
346 	status = GSS_S_UNAVAILABLE;
347     }
348     return (status);
349 }
350 
351 /* Copy a union credential from cred_in to *cred_out. */
352 static OM_uint32
353 copy_union_cred(OM_uint32 *minor_status, gss_cred_id_t cred_in,
354 		gss_union_cred_t *cred_out)
355 {
356     OM_uint32 status, tmpmin;
357     gss_union_cred_t cred = (gss_union_cred_t)cred_in;
358     gss_union_cred_t ncred = NULL;
359     gss_cred_id_t tmpcred;
360     int i;
361 
362     ncred = calloc(1, sizeof (*ncred));
363     if (ncred == NULL)
364 	goto oom;
365     ncred->mechs_array = calloc(cred->count, sizeof (*ncred->mechs_array));
366     ncred->cred_array = calloc(cred->count, sizeof (*ncred->cred_array));
367     if (ncred->mechs_array == NULL || ncred->cred_array == NULL)
368 	goto oom;
369     ncred->count = cred->count;
370 
371     for (i = 0; i < cred->count; i++) {
372 	/* Copy this element's mechanism OID. */
373 	ncred->mechs_array[i].elements = malloc(cred->mechs_array[i].length);
374 	if (ncred->mechs_array[i].elements == NULL)
375 	    goto oom;
376 	g_OID_copy(&ncred->mechs_array[i], &cred->mechs_array[i]);
377 
378 	/* Copy this element's mechanism cred. */
379 	status = copy_mech_cred(minor_status, cred->cred_array[i],
380 				&cred->mechs_array[i], &ncred->cred_array[i]);
381 	if (status != GSS_S_COMPLETE)
382 	    goto error;
383     }
384 
385     ncred->loopback = ncred;
386     *cred_out = ncred;
387     return GSS_S_COMPLETE;
388 
389 oom:
390     status = GSS_S_FAILURE;
391     *minor_status = ENOMEM;
392 error:
393     tmpcred = (gss_cred_id_t)ncred;
394     (void) gss_release_cred(&tmpmin, &tmpcred);
395     return status;
396 }
397 
398 /* V2 KRB5_CALLCONV */
399 OM_uint32 KRB5_CALLCONV
400 gss_add_cred(minor_status, input_cred_handle,
401 		  desired_name, desired_mech, cred_usage,
402 		  initiator_time_req, acceptor_time_req,
403 		  output_cred_handle, actual_mechs,
404 		  initiator_time_rec, acceptor_time_rec)
405     OM_uint32		*minor_status;
406     gss_cred_id_t	input_cred_handle;
407     gss_name_t		desired_name;
408     gss_OID		desired_mech;
409     gss_cred_usage_t	cred_usage;
410     OM_uint32		initiator_time_req;
411     OM_uint32		acceptor_time_req;
412     gss_cred_id_t	*output_cred_handle;
413     gss_OID_set		*actual_mechs;
414     OM_uint32		*initiator_time_rec;
415     OM_uint32		*acceptor_time_rec;
416 {
417     return gss_add_cred_from(minor_status, input_cred_handle, desired_name,
418 			     desired_mech, cred_usage, initiator_time_req,
419 			     acceptor_time_req, NULL, output_cred_handle,
420 			     actual_mechs, initiator_time_rec,
421 			     acceptor_time_rec);
422 }
423 
424 OM_uint32 KRB5_CALLCONV
425 gss_add_cred_from(minor_status, input_cred_handle,
426 		  desired_name, desired_mech,
427 		  cred_usage,
428 		  initiator_time_req, acceptor_time_req,
429 		  cred_store,
430 		  output_cred_handle, actual_mechs,
431 		  initiator_time_rec, acceptor_time_rec)
432     OM_uint32		*minor_status;
433     gss_cred_id_t	input_cred_handle;
434     gss_name_t		desired_name;
435     gss_OID		desired_mech;
436     gss_cred_usage_t	cred_usage;
437     OM_uint32		initiator_time_req;
438     OM_uint32		acceptor_time_req;
439     gss_const_key_value_set_t  cred_store;
440     gss_cred_id_t	*output_cred_handle;
441     gss_OID_set		*actual_mechs;
442     OM_uint32		*initiator_time_rec;
443     OM_uint32		*acceptor_time_rec;
444 {
445     OM_uint32		status, temp_minor_status;
446     OM_uint32		time_req, time_rec = 0, *time_recp = NULL;
447     gss_union_name_t	union_name;
448     gss_union_cred_t	union_cred;
449     gss_name_t		internal_name = GSS_C_NO_NAME;
450     gss_name_t		allocated_name = GSS_C_NO_NAME;
451     gss_mechanism	mech;
452     gss_cred_id_t	cred = NULL, tmpcred;
453     void		*newptr, *oidbuf = NULL;
454     gss_OID_set_desc	target_mechs;
455     gss_OID		selected_mech = GSS_C_NO_OID;
456 
457     status = val_add_cred_args(minor_status,
458 			       input_cred_handle,
459 			       desired_name,
460 			       desired_mech,
461 			       cred_usage,
462 			       cred_store,
463 			       initiator_time_req,
464 			       acceptor_time_req,
465 			       output_cred_handle,
466 			       actual_mechs,
467 			       initiator_time_rec,
468 			       acceptor_time_rec);
469     if (status != GSS_S_COMPLETE)
470 	return (status);
471 
472     status = gssint_select_mech_type(minor_status, desired_mech,
473 				     &selected_mech);
474     if (status != GSS_S_COMPLETE)
475 	return (status);
476 
477     mech = gssint_get_mechanism(selected_mech);
478     if (!mech)
479 	return GSS_S_BAD_MECH;
480     else if (!mech->gss_acquire_cred)
481 	return (GSS_S_UNAVAILABLE);
482 
483     union_cred = (gss_union_cred_t)input_cred_handle;
484     if (union_cred != NULL &&
485 	gssint_get_mechanism_cred(union_cred,
486 				  selected_mech) != GSS_C_NO_CREDENTIAL)
487 	return (GSS_S_DUPLICATE_ELEMENT);
488 
489     if (union_cred == NULL) {
490 	/* Create a new credential handle. */
491 	union_cred = malloc(sizeof (gss_union_cred_desc));
492 	if (union_cred == NULL)
493 	    return (GSS_S_FAILURE);
494 
495 	(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
496 	union_cred->loopback = union_cred;
497     } else if (output_cred_handle != NULL) {
498 	/* Create a new credential handle with the mechanism credentials of the
499 	 * input handle plus the acquired mechanism credential. */
500 	status = copy_union_cred(minor_status, input_cred_handle, &union_cred);
501 	if (status != GSS_S_COMPLETE)
502 	    return (status);
503     }
504 
505     /* We may need to create a mechanism specific name. */
506     if (desired_name != GSS_C_NO_NAME) {
507 	union_name = (gss_union_name_t)desired_name;
508 	if (union_name->mech_type &&
509 	    g_OID_equal(union_name->mech_type, selected_mech)) {
510 	    internal_name = union_name->mech_name;
511 	} else {
512 	    if (gssint_import_internal_name(minor_status, selected_mech,
513 					    union_name, &allocated_name) !=
514 		GSS_S_COMPLETE) {
515 		status = GSS_S_BAD_NAME;
516 		goto errout;
517 	    }
518 	    internal_name = allocated_name;
519 	}
520     }
521 
522 
523     if (cred_usage == GSS_C_ACCEPT)
524 	time_req = acceptor_time_req;
525     else if (cred_usage == GSS_C_INITIATE)
526 	time_req = initiator_time_req;
527     else if (cred_usage == GSS_C_BOTH)
528 	time_req = (acceptor_time_req > initiator_time_req) ?
529 	    acceptor_time_req : initiator_time_req;
530     else
531 	time_req = 0;
532 
533     target_mechs.count = 1;
534     target_mechs.elements = gssint_get_public_oid(selected_mech);
535     if (target_mechs.elements == NULL) {
536 	status = GSS_S_FAILURE;
537 	goto errout;
538     }
539 
540     if (initiator_time_rec != NULL || acceptor_time_rec != NULL)
541 	time_recp = &time_rec;
542 
543     if (mech->gss_acquire_cred_from) {
544 	status = mech->gss_acquire_cred_from(minor_status, internal_name,
545 					     time_req, &target_mechs,
546 					     cred_usage, cred_store, &cred,
547 					     NULL, time_recp);
548     } else if (cred_store == GSS_C_NO_CRED_STORE) {
549 	status = mech->gss_acquire_cred(minor_status, internal_name, time_req,
550 					&target_mechs, cred_usage, &cred, NULL,
551 					time_recp);
552     } else {
553 	status = GSS_S_UNAVAILABLE;
554 	goto errout;
555     }
556 
557     if (status != GSS_S_COMPLETE) {
558 	map_error(minor_status, mech);
559 	goto errout;
560     }
561 
562     /* Extend the arrays in the union cred. */
563 
564     newptr = realloc(union_cred->mechs_array,
565 		     (union_cred->count + 1) * sizeof (gss_OID_desc));
566     if (newptr == NULL) {
567 	status = GSS_S_FAILURE;
568 	goto errout;
569     }
570     union_cred->mechs_array = newptr;
571 
572     newptr = realloc(union_cred->cred_array,
573 		     (union_cred->count + 1) * sizeof (gss_cred_id_t));
574     if (newptr == NULL) {
575 	status = GSS_S_FAILURE;
576 	goto errout;
577     }
578     union_cred->cred_array = newptr;
579 
580     if (acceptor_time_rec)
581 	if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
582 	    *acceptor_time_rec = time_rec;
583     if (initiator_time_rec)
584 	if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
585 	    *initiator_time_rec = time_rec;
586 
587     oidbuf = malloc(selected_mech->length);
588     if (oidbuf == NULL)
589 	goto errout;
590     union_cred->mechs_array[union_cred->count].elements = oidbuf;
591     g_OID_copy(&union_cred->mechs_array[union_cred->count], selected_mech);
592 
593     if (actual_mechs != NULL) {
594 	status = gssint_make_public_oid_set(minor_status,
595 					    union_cred->mechs_array,
596 					    union_cred->count + 1,
597 					    actual_mechs);
598 	if (GSS_ERROR(status))
599 	    goto errout;
600     }
601 
602     union_cred->cred_array[union_cred->count] = cred;
603     union_cred->count++;
604     if (output_cred_handle != NULL)
605 	*output_cred_handle = (gss_cred_id_t)union_cred;
606 
607     /* We're done with the internal name. Free it if we allocated it. */
608 
609     if (allocated_name)
610 	(void) gssint_release_internal_name(&temp_minor_status,
611 					   selected_mech,
612 					   &allocated_name);
613 
614     return (GSS_S_COMPLETE);
615 
616 errout:
617     if (cred != NULL && mech->gss_release_cred)
618 	mech->gss_release_cred(&temp_minor_status, &cred);
619 
620     if (allocated_name)
621 	(void) gssint_release_internal_name(&temp_minor_status,
622 					   selected_mech,
623 					   &allocated_name);
624 
625     if (output_cred_handle != NULL && union_cred != NULL) {
626 	tmpcred = union_cred;
627 	(void) gss_release_cred(&temp_minor_status, &tmpcred);
628     }
629 
630     free(oidbuf);
631 
632     return (status);
633 }
634