xref: /illumos-gate/usr/src/lib/libgss/g_acquire_cred.c (revision eb6b10e69fa5ba733da194d3ad71a0e63338be29)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  *  glue routine for gss_acquire_cred
27  */
28 #include <mechglueP.h>
29 #include "gssapiP_generic.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 /* local functions */
39 static gss_OID_set create_actual_mechs(const gss_OID, int);
40 
41 static gss_OID_set
42 create_actual_mechs(mechs_array, count)
43 	const gss_OID	mechs_array;
44 	int count;
45 {
46 	gss_OID_set 	actual_mechs;
47 	int		i;
48 	OM_uint32	minor;
49 
50 	actual_mechs = (gss_OID_set) malloc(sizeof (gss_OID_set_desc));
51 	if (!actual_mechs)
52 		return (NULL);
53 
54 	actual_mechs->elements = (gss_OID)
55 		malloc(sizeof (gss_OID_desc) * count);
56 	if (!actual_mechs->elements) {
57 		free(actual_mechs);
58 		return (NULL);
59 	}
60 
61 	actual_mechs->count = 0;
62 
63 	for (i = 0; i < count; i++) {
64 		actual_mechs->elements[i].elements = (void *)
65 			malloc(mechs_array[i].length);
66 		if (actual_mechs->elements[i].elements == NULL) {
67 			(void) gss_release_oid_set(&minor, &actual_mechs);
68 			return (NULL);
69 		}
70 		g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]);
71 		actual_mechs->count++;
72 	}
73 
74 	return (actual_mechs);
75 }
76 
77 static OM_uint32
78 val_acq_cred_args(
79     OM_uint32 *minor_status,
80     /*LINTED*/
81     gss_name_t desired_name,
82     /*LINTED*/
83     OM_uint32 time_req,
84     /*LINTED*/
85     gss_OID_set desired_mechs,
86     int cred_usage,
87     gss_cred_id_t *output_cred_handle,
88     gss_OID_set *actual_mechs,
89     OM_uint32 *time_rec)
90 {
91 
92    /* Initialize outputs. */
93 
94     if (minor_status != NULL)
95         *minor_status = 0;
96 
97     if (output_cred_handle != NULL)
98         *output_cred_handle = GSS_C_NO_CREDENTIAL;
99 
100     if (actual_mechs != NULL)
101         *actual_mechs = GSS_C_NULL_OID_SET;
102 
103     if (time_rec != NULL)
104         *time_rec = 0;
105 
106     /* Validate arguments. */
107 
108     if (minor_status == NULL)
109         return (GSS_S_CALL_INACCESSIBLE_WRITE);
110 
111     if (output_cred_handle == NULL)
112         return (GSS_S_CALL_INACCESSIBLE_WRITE);
113 
114     if (cred_usage != GSS_C_ACCEPT
115         && cred_usage != GSS_C_INITIATE
116         && cred_usage != GSS_C_BOTH) {
117         if (minor_status) {
118             *minor_status = EINVAL;
119             map_errcode(minor_status);
120         }
121         return GSS_S_FAILURE;
122     }
123 
124     return (GSS_S_COMPLETE);
125 }
126 
127 OM_uint32
128 gss_acquire_cred(minor_status,
129 			desired_name,
130 			time_req,
131 			desired_mechs,
132 			cred_usage,
133 			output_cred_handle,
134 			actual_mechs,
135 			time_rec)
136 
137 OM_uint32 *		minor_status;
138 const gss_name_t	desired_name;
139 OM_uint32		time_req;
140 const gss_OID_set	desired_mechs;
141 int			cred_usage;
142 gss_cred_id_t 		*output_cred_handle;
143 gss_OID_set *		actual_mechs;
144 OM_uint32 *		time_rec;
145 
146 {
147 	OM_uint32 major = GSS_S_FAILURE;
148 	OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
149 	gss_OID_set_desc default_OID_set;
150 	gss_OID_set mechs;
151 	gss_OID_desc default_OID;
152 	gss_mechanism mech;
153 	unsigned int i;
154 	gss_union_cred_t creds;
155 
156 	major = val_acq_cred_args(minor_status,
157 				desired_name,
158 				time_req,
159 				desired_mechs,
160 				cred_usage,
161 				output_cred_handle,
162 				actual_mechs,
163 				time_rec);
164 	if (major != GSS_S_COMPLETE)
165 		return (major);
166 
167 	/* Initial value needed below. */
168 	major = GSS_S_FAILURE;
169 
170 	/*
171 	 * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
172 	 * appropriate default.  We use the first mechanism in the
173 	 * mechansim list as the default. This set is created with
174 	 * statics thus needs not be freed
175 	 */
176 	if (desired_mechs == GSS_C_NULL_OID_SET) {
177 		mech = __gss_get_mechanism(NULL);
178 		if (mech == NULL)
179 			return (GSS_S_BAD_MECH);
180 
181 		mechs = &default_OID_set;
182 		default_OID_set.count = 1;
183 		default_OID_set.elements = &default_OID;
184 		default_OID.length = mech->mech_type.length;
185 		default_OID.elements = mech->mech_type.elements;
186 	} else
187 		mechs = desired_mechs;
188 
189 	if (mechs->count == 0)
190 		return (GSS_S_BAD_MECH);
191 
192 	/* allocate the output credential structure */
193 	creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
194 	if (creds == NULL)
195 		return (GSS_S_FAILURE);
196 
197 	/* initialize to 0s */
198 	(void) memset(creds, 0, sizeof (gss_union_cred_desc));
199 
200 	/* for each requested mech attempt to obtain a credential */
201 	for (i = 0; i < mechs->count; i++) {
202 		major = gss_add_cred(minor_status, (gss_cred_id_t)creds,
203 				desired_name,
204 				&mechs->elements[i],
205 				cred_usage, time_req, time_req, NULL,
206 				NULL, &initTimeOut, &acceptTimeOut);
207 		if (major == GSS_S_COMPLETE) {
208 			/* update the credential's time */
209 			if (cred_usage == GSS_C_ACCEPT) {
210 				if (outTime > acceptTimeOut)
211 					outTime = acceptTimeOut;
212 			} else if (cred_usage == GSS_C_INITIATE) {
213 				if (outTime > initTimeOut)
214 					outTime = initTimeOut;
215 			} else {
216 				/*
217 				 * time_rec is the lesser of the
218 				 * init/accept times
219 				 */
220 				if (initTimeOut > acceptTimeOut)
221 					outTime = (outTime > acceptTimeOut) ?
222 						acceptTimeOut : outTime;
223 				else
224 					outTime = (outTime > initTimeOut) ?
225 						initTimeOut : outTime;
226 			}
227 		}
228 	} /* for */
229 
230 	/* ensure that we have at least one credential element */
231 	if (creds->count < 1) {
232 		free(creds);
233 		return (major);
234 	}
235 
236 	/*
237 	 * fill in output parameters
238 	 * setup the actual mechs output parameter
239 	 */
240 	if (actual_mechs != NULL) {
241 		if ((*actual_mechs = create_actual_mechs(creds->mechs_array,
242 					creds->count)) == NULL) {
243 			(void) gss_release_cred(minor_status,
244 				(gss_cred_id_t *)&creds);
245 			*minor_status = 0;
246 			return (GSS_S_FAILURE);
247 		}
248 	}
249 
250 	if (time_rec)
251 		*time_rec = outTime;
252 
253 
254 	*output_cred_handle = (gss_cred_id_t)creds;
255 	return (GSS_S_COMPLETE);
256 }
257 
258 static OM_uint32
259 val_add_cred_args(
260 	OM_uint32 *minor_status,
261 	gss_cred_id_t input_cred_handle,
262 	/*LINTED*/
263 	gss_name_t desired_name,
264 	/*LINTED*/
265 	gss_OID desired_mech,
266 	gss_cred_usage_t cred_usage,
267 	/*LINTED*/
268 	OM_uint32 initiator_time_req,
269 	/*LINTED*/
270 	OM_uint32 acceptor_time_req,
271 	gss_cred_id_t *output_cred_handle,
272 	gss_OID_set *actual_mechs,
273 	OM_uint32 *initiator_time_rec,
274 	OM_uint32 *acceptor_time_rec)
275 {
276 
277 	/* Initialize outputs. */
278 
279 	if (minor_status != NULL)
280 		*minor_status = 0;
281 
282 	if (output_cred_handle != NULL)
283 		*output_cred_handle = GSS_C_NO_CREDENTIAL;
284 
285 	if (actual_mechs != NULL)
286 		*actual_mechs = GSS_C_NO_OID_SET;
287 
288 	if (acceptor_time_rec != NULL)
289 		*acceptor_time_rec = 0;
290 
291 	if (initiator_time_rec != NULL)
292 		*initiator_time_rec = 0;
293 	/* Validate arguments. */
294 
295 	if (minor_status == NULL)
296 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
297 
298 	if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
299 	    output_cred_handle == NULL)
300 		return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
301 
302 	if (cred_usage != GSS_C_ACCEPT
303 	    && cred_usage != GSS_C_INITIATE
304 	    && cred_usage != GSS_C_BOTH) {
305 		if (minor_status) {
306 			*minor_status = EINVAL;
307 			map_errcode(minor_status);
308 		}
309 		return GSS_S_FAILURE;
310 	}
311 
312 	return (GSS_S_COMPLETE);
313 }
314 
315 /* V2 INTERFACE */
316 OM_uint32
317 gss_add_cred(minor_status, input_cred_handle,
318 			desired_name, desired_mech, cred_usage,
319 			initiator_time_req, acceptor_time_req,
320 			output_cred_handle, actual_mechs,
321 			initiator_time_rec, acceptor_time_rec)
322 	OM_uint32		*minor_status;
323 	const gss_cred_id_t	input_cred_handle;
324 	const gss_name_t	desired_name;
325 	const gss_OID		desired_mech;
326 	gss_cred_usage_t	cred_usage;
327 	OM_uint32		initiator_time_req;
328 	OM_uint32		acceptor_time_req;
329 	gss_cred_id_t		*output_cred_handle;
330 	gss_OID_set		*actual_mechs;
331 	OM_uint32		*initiator_time_rec;
332 	OM_uint32		*acceptor_time_rec;
333 {
334 	OM_uint32		status, time_req, time_rec, temp_minor_status;
335 	gss_mechanism 		mech;
336 	gss_union_name_t	union_name = NULL;
337 	gss_union_cred_t	union_cred, new_union_cred;
338 	gss_name_t		internal_name = GSS_C_NO_NAME;
339 	gss_name_t		allocated_name = GSS_C_NO_NAME;
340 	gss_cred_id_t		cred = NULL;
341 	gss_OID			new_mechs_array = NULL;
342 	gss_cred_id_t		*new_cred_array = NULL;
343 
344 	status = val_add_cred_args(minor_status,
345 				input_cred_handle,
346 				desired_name,
347 				desired_mech,
348 				cred_usage,
349 				initiator_time_req,
350 				acceptor_time_req,
351 				output_cred_handle,
352 				actual_mechs,
353 				initiator_time_rec,
354 				acceptor_time_rec);
355 	if (status != GSS_S_COMPLETE)
356 		return (status);
357 
358 	mech = __gss_get_mechanism(desired_mech);
359 	if (!mech)
360 		return (GSS_S_BAD_MECH);
361 	else if (!mech->gss_acquire_cred)
362 		return (GSS_S_UNAVAILABLE);
363 
364 	if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
365 		union_cred = malloc(sizeof (gss_union_cred_desc));
366 		if (union_cred == NULL)
367 			return (GSS_S_FAILURE);
368 
369 		(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
370 	} else {
371 		/* Input Cred is non-NULL */
372 		union_cred = (gss_union_cred_t)input_cred_handle;
373 
374 		if (__gss_get_mechanism_cred(union_cred, desired_mech) !=
375 			GSS_C_NO_CREDENTIAL) {
376 			status = GSS_S_DUPLICATE_ELEMENT;
377 			goto errout;
378 		}
379 
380 		/*
381 		 * If no name was given, determine the name from the
382 		 * existing credential.
383 		 */
384 		if (desired_name == GSS_C_NO_NAME) {
385 			if (gss_import_name(minor_status,
386 				&union_cred->auxinfo.name,
387 				union_cred->auxinfo.name_type,
388 				&allocated_name) == GSS_S_COMPLETE &&
389 			    (gss_canonicalize_name(minor_status,
390 					allocated_name,
391 					&mech->mech_type,
392 					NULL) == GSS_S_COMPLETE)) {
393 				internal_name = allocated_name;
394 			}
395 		} /* else, get the name from the desired_name below */
396 	}
397 	if (desired_name != GSS_C_NO_NAME) {
398 		/* may need to create a mechanism specific name */
399 		union_name = (gss_union_name_t)desired_name;
400 
401 		if (union_name->mech_type &&
402 			g_OID_equal(union_name->mech_type,
403 					&mech->mech_type))
404 			internal_name = union_name->mech_name;
405 		else {
406 			if (__gss_import_internal_name(minor_status,
407 				&mech->mech_type, union_name,
408 				&allocated_name) != GSS_S_COMPLETE) {
409 				status = GSS_S_BAD_NAME;
410 				goto errout;
411 			}
412 			internal_name = allocated_name;
413 		}
414 	}
415 
416 	if (cred_usage == GSS_C_ACCEPT)
417 		time_req = acceptor_time_req;
418 	else if (cred_usage == GSS_C_INITIATE)
419 		time_req = initiator_time_req;
420 	else if (cred_usage == GSS_C_BOTH)
421 		time_req = (acceptor_time_req > initiator_time_req) ?
422 			acceptor_time_req : initiator_time_req;
423 	else
424 		time_req = 0;
425 
426 	status = mech->gss_acquire_cred(mech->context, minor_status,
427 				internal_name, time_req,
428 				GSS_C_NULL_OID_SET, cred_usage,
429 				&cred, NULL, &time_rec);
430 
431 	if (status != GSS_S_COMPLETE) {
432 		map_error(minor_status, mech);
433 		goto errout;
434 	}
435 
436 	/* may need to set credential auxinfo structure */
437 	if (union_cred->auxinfo.creation_time == 0) {
438 		union_cred->auxinfo.creation_time = time(NULL);
439 		union_cred->auxinfo.time_rec = time_rec;
440 		union_cred->auxinfo.cred_usage = cred_usage;
441 
442 	/*
443 	 * If internal_name is GSS_C_NO_NAME a cred with no associated
444 	 * name was requested: don't set auxinfo.name or auxinfo.name_type.
445 	 */
446 		if (internal_name != GSS_C_NO_NAME) {
447 			if ((status = mech->gss_display_name(mech->context,
448 					&temp_minor_status, internal_name,
449 					&union_cred->auxinfo.name,
450 					&union_cred->auxinfo.name_type)) !=
451 				GSS_S_COMPLETE)
452 				goto errout;
453 		}
454 	}
455 
456 	/* now add the new credential elements */
457 	new_mechs_array = (gss_OID)
458 		malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
459 
460 	new_cred_array = (gss_cred_id_t *)
461 		malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
462 
463 	if (!new_mechs_array || !new_cred_array) {
464 		status = GSS_S_FAILURE;
465 		goto errout;
466 	}
467 
468 	if (acceptor_time_rec)
469 		if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
470 			*acceptor_time_rec = time_rec;
471 	if (initiator_time_rec)
472 		if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
473 			*initiator_time_rec = time_rec;
474 
475 	/*
476 	 * OK, expand the mechanism array and the credential array
477 	 */
478 	(void) memcpy(new_mechs_array, union_cred->mechs_array,
479 		sizeof (gss_OID_desc) * union_cred->count);
480 	(void) memcpy(new_cred_array, union_cred->cred_array,
481 		sizeof (gss_cred_id_t) * union_cred->count);
482 
483 	new_cred_array[union_cred->count] = cred;
484 	if ((new_mechs_array[union_cred->count].elements =
485 			malloc(mech->mech_type.length)) == NULL)
486 		goto errout;
487 
488 	g_OID_copy(&new_mechs_array[union_cred->count],
489 			&mech->mech_type);
490 
491 	if (actual_mechs) {
492 		*actual_mechs = create_actual_mechs(new_mechs_array,
493 					union_cred->count + 1);
494 		if (*actual_mechs == NULL) {
495 			free(new_mechs_array[union_cred->count].elements);
496 			goto errout;
497 		}
498 	}
499 
500 	if (output_cred_handle == NULL) {
501 		free(union_cred->mechs_array);
502 		free(union_cred->cred_array);
503 		new_union_cred = union_cred;
504 	} else {
505 		new_union_cred = malloc(sizeof (gss_union_cred_desc));
506 		if (new_union_cred == NULL) {
507 			free(new_mechs_array[union_cred->count].elements);
508 			goto errout;
509 		}
510 		*new_union_cred = *union_cred;
511 		*output_cred_handle = (gss_cred_id_t)new_union_cred;
512 	}
513 
514 	new_union_cred->mechs_array = new_mechs_array;
515 	new_union_cred->cred_array = new_cred_array;
516 	new_union_cred->count++;
517 
518 	/* We're done with the internal name. Free it if we allocated it. */
519 
520 	if (allocated_name)
521 		(void) __gss_release_internal_name(&temp_minor_status,
522 					&mech->mech_type,
523 					&allocated_name);
524 
525 	return (GSS_S_COMPLETE);
526 
527 errout:
528 	if (new_mechs_array)
529 		free(new_mechs_array);
530 	if (new_cred_array)
531 		free(new_cred_array);
532 
533 	if (cred != NULL && mech->gss_release_cred)
534 		mech->gss_release_cred(mech->context,
535 				&temp_minor_status, &cred);
536 
537 	if (allocated_name)
538 		(void) __gss_release_internal_name(&temp_minor_status,
539 					&mech->mech_type,
540 					&allocated_name);
541 
542 	if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
543 		if (union_cred->auxinfo.name.value)
544 			free(union_cred->auxinfo.name.value);
545 		free(union_cred);
546 	}
547 
548 	return (status);
549 }
550