xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* #pragma ident	"@(#)g_acquire_cred.c	1.22	04/02/23 SMI" */
2 
3 /*
4  * Copyright 2009 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * Copyright 1996 by Sun Microsystems, Inc.
28  *
29  * Permission to use, copy, modify, distribute, and sell this software
30  * and its documentation for any purpose is hereby granted without fee,
31  * provided that the above copyright notice appears in all copies and
32  * that both that copyright notice and this permission notice appear in
33  * supporting documentation, and that the name of Sun Microsystems not be used
34  * in advertising or publicity pertaining to distribution of the software
35  * without specific, written prior permission. Sun Microsystems makes no
36  * representations about the suitability of this software for any
37  * purpose.  It is provided "as is" without express or implied warranty.
38  *
39  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
41  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
42  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
43  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
44  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
45  * PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 /* Glue routine for gss_acquire_cred_impersonate_name */
49 
50 #include "mglueP.h"
51 #include <stdio.h>
52 #ifdef HAVE_STDLIB_H
53 #include <stdlib.h>
54 #endif
55 #include <string.h>
56 #include <errno.h>
57 #include <time.h>
58 
59 static OM_uint32
val_acq_cred_impersonate_name_args(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)60 val_acq_cred_impersonate_name_args(
61     OM_uint32 *minor_status,
62     const gss_cred_id_t impersonator_cred_handle,
63     const gss_name_t desired_name,
64     OM_uint32 time_req,
65     gss_OID_set desired_mechs,
66     gss_cred_usage_t cred_usage,
67     gss_cred_id_t *output_cred_handle,
68     gss_OID_set *actual_mechs,
69     OM_uint32 *time_rec)
70 {
71 
72     /* Initialize outputs. */
73 
74     if (minor_status != NULL)
75 	*minor_status = 0;
76 
77     if (output_cred_handle != NULL)
78 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
79 
80     if (actual_mechs != NULL)
81 	*actual_mechs = GSS_C_NULL_OID_SET;
82 
83     if (time_rec != NULL)
84 	*time_rec = 0;
85 
86     /* Validate arguments. */
87 
88     if (minor_status == NULL)
89 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
90 
91     if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
92 	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
93 
94     if (desired_name == GSS_C_NO_NAME)
95 	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
96 
97     if (output_cred_handle == NULL)
98 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
99 
100     if (cred_usage != GSS_C_ACCEPT
101 	&& cred_usage != GSS_C_INITIATE
102 	&& cred_usage != GSS_C_BOTH) {
103 	if (minor_status) {
104 	    *minor_status = EINVAL;
105 	    map_errcode(minor_status);
106 	}
107 	return GSS_S_FAILURE;
108     }
109 
110     return (GSS_S_COMPLETE);
111 }
112 
113 
114 OM_uint32 KRB5_CALLCONV
gss_acquire_cred_impersonate_name(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)115 gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
116 				  const gss_cred_id_t impersonator_cred_handle,
117 				  const gss_name_t desired_name,
118 				  OM_uint32 time_req,
119 				  const gss_OID_set desired_mechs,
120 				  gss_cred_usage_t cred_usage,
121 				  gss_cred_id_t *output_cred_handle,
122 				  gss_OID_set *actual_mechs,
123 				  OM_uint32 *time_rec)
124 {
125     OM_uint32 major = GSS_S_FAILURE;
126     OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
127     gss_OID_set_desc default_OID_set;
128     gss_OID_set mechs;
129     gss_OID_desc default_OID;
130     gss_mechanism mech;
131     unsigned int i;
132     gss_union_cred_t creds;
133 
134     major = val_acq_cred_impersonate_name_args(minor_status,
135 					       impersonator_cred_handle,
136 					       desired_name,
137 					       time_req,
138 					       desired_mechs,
139 					       cred_usage,
140 					       output_cred_handle,
141 					       actual_mechs,
142 					       time_rec);
143     if (major != GSS_S_COMPLETE)
144 	return (major);
145 
146     /* Initial value needed below. */
147     major = GSS_S_FAILURE;
148 
149     /*
150      * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
151      * appropriate default.  We use the first mechanism in the
152      * mechanism list as the default. This set is created with
153      * statics thus needs not be freed
154      */
155     if(desired_mechs == GSS_C_NULL_OID_SET) {
156 	mech = gssint_get_mechanism(NULL);
157 	if (mech == NULL)
158 	    return (GSS_S_BAD_MECH);
159 
160 	mechs = &default_OID_set;
161 	default_OID_set.count = 1;
162 	default_OID_set.elements = &default_OID;
163 	default_OID.length = mech->mech_type.length;
164 	default_OID.elements = mech->mech_type.elements;
165     } else
166 	mechs = desired_mechs;
167 
168     if (mechs->count == 0)
169 	return (GSS_S_BAD_MECH);
170 
171     /* allocate the output credential structure */
172     creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
173     if (creds == NULL)
174 	return (GSS_S_FAILURE);
175 
176     /* initialize to 0s */
177     (void) memset(creds, 0, sizeof (gss_union_cred_desc));
178     creds->loopback = creds;
179 
180     /* for each requested mech attempt to obtain a credential */
181     for (i = 0; i < mechs->count; i++) {
182 	major = gss_add_cred_impersonate_name(minor_status,
183 					      (gss_cred_id_t)creds,
184 					      impersonator_cred_handle,
185 					      desired_name,
186 					      &mechs->elements[i],
187 					      cred_usage,
188 					      time_req,
189 					      time_req, NULL,
190 					      NULL,
191 					      &initTimeOut,
192 					      &acceptTimeOut);
193 	if (major == GSS_S_COMPLETE) {
194 	    /* update the credential's time */
195 	    if (cred_usage == GSS_C_ACCEPT) {
196 		if (outTime > acceptTimeOut)
197 		    outTime = acceptTimeOut;
198 	    } else if (cred_usage == GSS_C_INITIATE) {
199 		if (outTime > initTimeOut)
200 		    outTime = initTimeOut;
201 	    } else {
202 		/*
203 		 * time_rec is the lesser of the
204 		 * init/accept times
205 		 */
206 		if (initTimeOut > acceptTimeOut)
207 		    outTime = (outTime > acceptTimeOut) ?
208 			acceptTimeOut : outTime;
209 		else
210 		    outTime = (outTime > initTimeOut) ?
211 			initTimeOut : outTime;
212 	    }
213 	}
214     } /* for */
215 
216     /* ensure that we have at least one credential element */
217     if (creds->count < 1) {
218 	free(creds);
219 	return (major);
220     }
221 
222     /*
223      * fill in output parameters
224      * setup the actual mechs output parameter
225      */
226     if (actual_mechs != NULL) {
227 	gss_OID_set_desc oids;
228 
229 	oids.count = creds->count;
230 	oids.elements = creds->mechs_array;
231 
232 	major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
233 	if (GSS_ERROR(major)) {
234 	    (void) gss_release_cred(minor_status,
235 				    (gss_cred_id_t *)&creds);
236 	    return (major);
237 	}
238     }
239 
240     if (time_rec)
241 	*time_rec = outTime;
242 
243 
244     creds->loopback = creds;
245     *output_cred_handle = (gss_cred_id_t)creds;
246     return (GSS_S_COMPLETE);
247 }
248 
249 static OM_uint32
val_add_cred_impersonate_name_args(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,const gss_cred_id_t impersonator_cred_handle,gss_name_t desired_name,gss_OID desired_mech,gss_cred_usage_t cred_usage,OM_uint32 initiator_time_req,OM_uint32 acceptor_time_req,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * initiator_time_rec,OM_uint32 * acceptor_time_rec)250 val_add_cred_impersonate_name_args(
251     OM_uint32 *minor_status,
252     gss_cred_id_t input_cred_handle,
253     const gss_cred_id_t impersonator_cred_handle,
254     gss_name_t desired_name,
255     gss_OID desired_mech,
256     gss_cred_usage_t cred_usage,
257     OM_uint32 initiator_time_req,
258     OM_uint32 acceptor_time_req,
259     gss_cred_id_t *output_cred_handle,
260     gss_OID_set *actual_mechs,
261     OM_uint32 *initiator_time_rec,
262     OM_uint32 *acceptor_time_rec)
263 {
264 
265     /* Initialize outputs. */
266 
267     if (minor_status != NULL)
268 	*minor_status = 0;
269 
270     if (output_cred_handle != NULL)
271 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
272 
273     if (actual_mechs != NULL)
274 	*actual_mechs = GSS_C_NO_OID_SET;
275 
276     if (acceptor_time_rec != NULL)
277 	*acceptor_time_rec = 0;
278 
279     if (initiator_time_rec != NULL)
280 	*initiator_time_rec = 0;
281 
282     /* Validate arguments. */
283 
284     if (minor_status == NULL)
285 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
286 
287     if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
288 	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
289 
290     if (desired_name == GSS_C_NO_NAME)
291 	return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
292 
293     if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
294 	output_cred_handle == NULL)
295 	return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
296 
297     if (cred_usage != GSS_C_ACCEPT
298 	&& cred_usage != GSS_C_INITIATE
299 	&& cred_usage != GSS_C_BOTH) {
300 	if (minor_status) {
301 	    *minor_status = EINVAL;
302 	    map_errcode(minor_status);
303 	}
304 	return GSS_S_FAILURE;
305     }
306 
307     return (GSS_S_COMPLETE);
308 }
309 
310 
311 /* V2 KRB5_CALLCONV */
312 OM_uint32 KRB5_CALLCONV
gss_add_cred_impersonate_name(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,const gss_OID desired_mech,gss_cred_usage_t cred_usage,OM_uint32 initiator_time_req,OM_uint32 acceptor_time_req,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * initiator_time_rec,OM_uint32 * acceptor_time_rec)313 gss_add_cred_impersonate_name(OM_uint32 *minor_status,
314 			      gss_cred_id_t input_cred_handle,
315 			      const gss_cred_id_t impersonator_cred_handle,
316 			      const gss_name_t desired_name,
317 			      const gss_OID desired_mech,
318 			      gss_cred_usage_t cred_usage,
319 			      OM_uint32 initiator_time_req,
320 			      OM_uint32 acceptor_time_req,
321 			      gss_cred_id_t *output_cred_handle,
322 			      gss_OID_set *actual_mechs,
323 			      OM_uint32 *initiator_time_rec,
324 			      OM_uint32 *acceptor_time_rec)
325 {
326     OM_uint32		status, temp_minor_status;
327     OM_uint32		time_req, time_rec;
328     gss_union_name_t	union_name;
329     gss_union_cred_t	new_union_cred, union_cred;
330     gss_cred_id_t	mech_impersonator_cred;
331     gss_name_t		internal_name = GSS_C_NO_NAME;
332     gss_name_t		allocated_name = GSS_C_NO_NAME;
333     gss_mechanism	mech;
334     gss_cred_id_t	cred = NULL;
335     gss_OID		new_mechs_array = NULL;
336     gss_cred_id_t *	new_cred_array = NULL;
337     gss_OID_set		target_mechs = GSS_C_NO_OID_SET;
338     gss_OID		selected_mech = GSS_C_NO_OID;
339 
340     status = val_add_cred_impersonate_name_args(minor_status,
341 						input_cred_handle,
342 						impersonator_cred_handle,
343 						desired_name,
344 						desired_mech,
345 						cred_usage,
346 						initiator_time_req,
347 						acceptor_time_req,
348 						output_cred_handle,
349 						actual_mechs,
350 						initiator_time_rec,
351 						acceptor_time_rec);
352     if (status != GSS_S_COMPLETE)
353 	return (status);
354 
355     status = gssint_select_mech_type(minor_status, desired_mech,
356 				     &selected_mech);
357     if (status != GSS_S_COMPLETE)
358 	return status;
359 
360     mech = gssint_get_mechanism(selected_mech);
361     if (!mech)
362 	return GSS_S_BAD_MECH;
363     else if (!mech->gss_acquire_cred_impersonate_name)
364 	return (GSS_S_UNAVAILABLE);
365 
366     if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
367 	union_cred = malloc(sizeof (gss_union_cred_desc));
368 	if (union_cred == NULL)
369 	    return (GSS_S_FAILURE);
370 
371 	(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
372 
373 	/* for default credentials we will use GSS_C_NO_NAME */
374 	internal_name = GSS_C_NO_NAME;
375     } else {
376 	union_cred = (gss_union_cred_t)input_cred_handle;
377 	if (gssint_get_mechanism_cred(union_cred, selected_mech) !=
378 	    GSS_C_NO_CREDENTIAL)
379 	    return (GSS_S_DUPLICATE_ELEMENT);
380     }
381 
382     mech_impersonator_cred =
383 	gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle,
384 				  selected_mech);
385     if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL)
386 	return (GSS_S_NO_CRED);
387 
388     /* may need to create a mechanism specific name */
389     union_name = (gss_union_name_t)desired_name;
390     if (union_name->mech_type &&
391 	g_OID_equal(union_name->mech_type, selected_mech))
392 	internal_name = union_name->mech_name;
393     else {
394 	if (gssint_import_internal_name(minor_status,
395 					selected_mech, union_name,
396 					&allocated_name) != GSS_S_COMPLETE)
397 	    return (GSS_S_BAD_NAME);
398 	internal_name = allocated_name;
399     }
400 
401     if (cred_usage == GSS_C_ACCEPT)
402 	time_req = acceptor_time_req;
403     else if (cred_usage == GSS_C_INITIATE)
404 	time_req = initiator_time_req;
405     else if (cred_usage == GSS_C_BOTH)
406 	time_req = (acceptor_time_req > initiator_time_req) ?
407 	    acceptor_time_req : initiator_time_req;
408     else
409 	time_req = 0;
410 
411     status = gss_create_empty_oid_set(minor_status, &target_mechs);
412     if (status != GSS_S_COMPLETE)
413 	goto errout;
414 
415     status = gss_add_oid_set_member(minor_status,
416 				    gssint_get_public_oid(selected_mech),
417 				    &target_mechs);
418     if (status != GSS_S_COMPLETE)
419 	goto errout;
420 
421     status = mech->gss_acquire_cred_impersonate_name(minor_status,
422 						     mech_impersonator_cred,
423 						     internal_name,
424 						     time_req,
425 						     target_mechs,
426 						     cred_usage,
427 						     &cred,
428 						     NULL,
429 						     &time_rec);
430     if (status != GSS_S_COMPLETE) {
431 	map_error(minor_status, mech);
432 	goto errout;
433     }
434 
435     /* now add the new credential elements */
436     new_mechs_array = (gss_OID)
437 	malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
438 
439     new_cred_array = (gss_cred_id_t *)
440 	malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
441 
442     if (!new_mechs_array || !new_cred_array) {
443 	status = GSS_S_FAILURE;
444 	goto errout;
445     }
446 
447     if (acceptor_time_rec)
448 	if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
449 	    *acceptor_time_rec = time_rec;
450     if (initiator_time_rec)
451 	if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
452 	    *initiator_time_rec = time_rec;
453 
454     /*
455      * OK, expand the mechanism array and the credential array
456      */
457     (void) memcpy(new_mechs_array, union_cred->mechs_array,
458 		  sizeof (gss_OID_desc) * union_cred->count);
459     (void) memcpy(new_cred_array, union_cred->cred_array,
460 		  sizeof (gss_cred_id_t) * union_cred->count);
461 
462     new_cred_array[union_cred->count] = cred;
463     if ((new_mechs_array[union_cred->count].elements =
464 	 malloc(selected_mech->length)) == NULL)
465 	goto errout;
466 
467     g_OID_copy(&new_mechs_array[union_cred->count], selected_mech);
468 
469     if (actual_mechs != NULL) {
470 	status = gssint_make_public_oid_set(minor_status, new_mechs_array,
471 					    union_cred->count + 1,
472 					    actual_mechs);
473 	if (GSS_ERROR(status)) {
474 	    free(new_mechs_array[union_cred->count].elements);
475 	    goto errout;
476 	}
477     }
478 
479     if (output_cred_handle == NULL) {
480 	free(union_cred->mechs_array);
481 	free(union_cred->cred_array);
482 	new_union_cred = union_cred;
483     } else {
484 	new_union_cred = malloc(sizeof (gss_union_cred_desc));
485 	if (new_union_cred == NULL) {
486 	    free(new_mechs_array[union_cred->count].elements);
487 	    goto errout;
488 	}
489 	*new_union_cred = *union_cred;
490 	*output_cred_handle = (gss_cred_id_t)new_union_cred;
491     }
492 
493     new_union_cred->mechs_array = new_mechs_array;
494     new_union_cred->cred_array = new_cred_array;
495     new_union_cred->count++;
496     new_union_cred->loopback = new_union_cred;
497 
498     /* We're done with the internal name. Free it if we allocated it. */
499 
500     if (allocated_name)
501 	(void) gssint_release_internal_name(&temp_minor_status, selected_mech,
502 					   &allocated_name);
503 
504     if (target_mechs)
505 	(void) gss_release_oid_set(&temp_minor_status, &target_mechs);
506 
507     return (GSS_S_COMPLETE);
508 
509 errout:
510     if (new_mechs_array)
511 	free(new_mechs_array);
512     if (new_cred_array)
513 	free(new_cred_array);
514 
515     if (cred != NULL && mech->gss_release_cred)
516 	mech->gss_release_cred(&temp_minor_status, &cred);
517 
518     if (allocated_name)
519 	(void) gssint_release_internal_name(&temp_minor_status,
520 					    selected_mech, &allocated_name);
521 
522     if (target_mechs)
523 	(void) gss_release_oid_set(&temp_minor_status, &target_mechs);
524 
525     if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred)
526 	free(union_cred);
527 
528     return (status);
529 }
530