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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  *  glue routine for gss_acquire_cred
28  */
29 #include <mechglueP.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 	gss_cred_id_t *output_cred_handle,
81 	gss_OID_set *actual_mechs,
82 	OM_uint32 *time_rec)
83 {
84 
85 	/* Initialize outputs. */
86 
87 	if (minor_status != NULL)
88 		*minor_status = 0;
89 
90 	if (output_cred_handle != NULL)
91 		*output_cred_handle = GSS_C_NO_CREDENTIAL;
92 
93 	if (actual_mechs != NULL)
94 		*actual_mechs = GSS_C_NULL_OID_SET;
95 
96 	if (time_rec != NULL)
97 		*time_rec = 0;
98 
99 	/* Validate arguments. */
100 
101 	if (minor_status == NULL)
102 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
103 
104 	if (output_cred_handle == NULL)
105 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
106 
107 	return (GSS_S_COMPLETE);
108 }
109 
110 OM_uint32
111 gss_acquire_cred(minor_status,
112 			desired_name,
113 			time_req,
114 			desired_mechs,
115 			cred_usage,
116 			output_cred_handle,
117 			actual_mechs,
118 			time_rec)
119 
120 OM_uint32 *		minor_status;
121 const gss_name_t	desired_name;
122 OM_uint32		time_req;
123 const gss_OID_set	desired_mechs;
124 int			cred_usage;
125 gss_cred_id_t 		*output_cred_handle;
126 gss_OID_set *		actual_mechs;
127 OM_uint32 *		time_rec;
128 
129 {
130 	OM_uint32 major = GSS_S_FAILURE;
131 	OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
132 	gss_OID_set_desc default_OID_set;
133 	gss_OID_set mechs;
134 	gss_OID_desc default_OID;
135 	gss_mechanism mech;
136 	int i;
137 	gss_union_cred_t creds;
138 
139 	major = val_acq_cred_args(minor_status,
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 	 * mechansim 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 = __gss_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 == NULL)
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 
179 	/* for each requested mech attempt to obtain a credential */
180 	for (i = 0; i < mechs->count; i++) {
181 		major = gss_add_cred(minor_status, (gss_cred_id_t)creds,
182 				desired_name,
183 				&mechs->elements[i],
184 				cred_usage, time_req, time_req, NULL,
185 				NULL, &initTimeOut, &acceptTimeOut);
186 		if (major == GSS_S_COMPLETE) {
187 			/* update the credential's time */
188 			if (cred_usage == GSS_C_ACCEPT) {
189 				if (outTime > acceptTimeOut)
190 					outTime = acceptTimeOut;
191 			} else if (cred_usage == GSS_C_INITIATE) {
192 				if (outTime > initTimeOut)
193 					outTime = initTimeOut;
194 			} else {
195 				/*
196 				 * time_rec is the lesser of the
197 				 * init/accept times
198 				 */
199 				if (initTimeOut > acceptTimeOut)
200 					outTime = (outTime > acceptTimeOut) ?
201 						acceptTimeOut : outTime;
202 				else
203 					outTime = (outTime > initTimeOut) ?
204 						initTimeOut : outTime;
205 			}
206 		}
207 	} /* for */
208 
209 	/* ensure that we have at least one credential element */
210 	if (creds->count < 1) {
211 		free(creds);
212 		return (major);
213 	}
214 
215 	/*
216 	 * fill in output parameters
217 	 * setup the actual mechs output parameter
218 	 */
219 	if (actual_mechs != NULL) {
220 		if ((*actual_mechs = create_actual_mechs(creds->mechs_array,
221 					creds->count)) == NULL) {
222 			(void) gss_release_cred(minor_status,
223 				(gss_cred_id_t *)&creds);
224 			*minor_status = 0;
225 			return (GSS_S_FAILURE);
226 		}
227 	}
228 
229 	if (time_rec)
230 		*time_rec = outTime;
231 
232 
233 	*output_cred_handle = (gss_cred_id_t)creds;
234 	return (GSS_S_COMPLETE);
235 }
236 
237 static OM_uint32
238 val_add_cred_args(
239 	OM_uint32 *minor_status,
240 	gss_cred_id_t input_cred_handle,
241 	gss_cred_id_t *output_cred_handle,
242 	gss_OID_set *actual_mechs,
243 	OM_uint32 *initiator_time_rec,
244 	OM_uint32 *acceptor_time_rec)
245 {
246 
247 	/* Initialize outputs. */
248 
249 	if (minor_status != NULL)
250 		*minor_status = 0;
251 
252 	if (output_cred_handle != NULL)
253 		*output_cred_handle = GSS_C_NO_CREDENTIAL;
254 
255 	if (actual_mechs != NULL)
256 		*actual_mechs = GSS_C_NO_OID_SET;
257 
258 	if (acceptor_time_rec != NULL)
259 		*acceptor_time_rec = 0;
260 
261 	if (initiator_time_rec != NULL)
262 		*initiator_time_rec = 0;
263 
264 	/* Validate arguments. */
265 
266 	if (minor_status == NULL)
267 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
268 
269 	if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
270 		output_cred_handle == NULL)
271 
272 		return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
273 
274 	return (GSS_S_COMPLETE);
275 }
276 
277 /* V2 INTERFACE */
278 OM_uint32
279 gss_add_cred(minor_status, input_cred_handle,
280 			desired_name, desired_mech, cred_usage,
281 			initiator_time_req, acceptor_time_req,
282 			output_cred_handle, actual_mechs,
283 			initiator_time_rec, acceptor_time_rec)
284 	OM_uint32		*minor_status;
285 	const gss_cred_id_t	input_cred_handle;
286 	const gss_name_t	desired_name;
287 	const gss_OID		desired_mech;
288 	gss_cred_usage_t	cred_usage;
289 	OM_uint32		initiator_time_req;
290 	OM_uint32		acceptor_time_req;
291 	gss_cred_id_t		*output_cred_handle;
292 	gss_OID_set		*actual_mechs;
293 	OM_uint32		*initiator_time_rec;
294 	OM_uint32		*acceptor_time_rec;
295 {
296 	OM_uint32		status, time_req, time_rec, temp_minor_status;
297 	gss_mechanism 		mech;
298 	gss_union_name_t	union_name = NULL;
299 	gss_union_cred_t	union_cred, new_union_cred;
300 	gss_name_t		internal_name = GSS_C_NO_NAME;
301 	gss_name_t		allocated_name = GSS_C_NO_NAME;
302 	gss_cred_id_t		cred = NULL;
303 	gss_OID			new_mechs_array = NULL;
304 	gss_cred_id_t		*new_cred_array = NULL;
305 
306 	status = val_add_cred_args(minor_status,
307 				input_cred_handle,
308 				output_cred_handle,
309 				actual_mechs,
310 				initiator_time_rec,
311 				acceptor_time_rec);
312 	if (status != GSS_S_COMPLETE)
313 		return (status);
314 
315 	mech = __gss_get_mechanism(desired_mech);
316 	if (!mech)
317 		return (GSS_S_BAD_MECH);
318 	else if (!mech->gss_acquire_cred)
319 		return (GSS_S_UNAVAILABLE);
320 
321 	if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
322 		union_cred = malloc(sizeof (gss_union_cred_desc));
323 		if (union_cred == NULL)
324 			return (GSS_S_FAILURE);
325 
326 		(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
327 	} else {
328 		/* Input Cred is non-NULL */
329 		union_cred = (gss_union_cred_t)input_cred_handle;
330 
331 		if (__gss_get_mechanism_cred(union_cred, desired_mech) !=
332 			GSS_C_NO_CREDENTIAL) {
333 			status = GSS_S_DUPLICATE_ELEMENT;
334 			goto errout;
335 		}
336 
337 		/*
338 		 * If no name was given, determine the name from the
339 		 * existing credential.
340 		 */
341 		if (desired_name == GSS_C_NO_NAME) {
342 			if (gss_import_name(minor_status,
343 				&union_cred->auxinfo.name,
344 				union_cred->auxinfo.name_type,
345 				&allocated_name) == GSS_S_COMPLETE &&
346 			    (gss_canonicalize_name(minor_status,
347 					allocated_name,
348 					&mech->mech_type,
349 					NULL) == GSS_S_COMPLETE)) {
350 				internal_name = allocated_name;
351 			}
352 		} /* else, get the name from the desired_name below */
353 	}
354 	if (desired_name != GSS_C_NO_NAME) {
355 		/* may need to create a mechanism specific name */
356 		union_name = (gss_union_name_t)desired_name;
357 
358 		if (union_name->mech_type &&
359 			g_OID_equal(union_name->mech_type,
360 					&mech->mech_type))
361 			internal_name = union_name->mech_name;
362 		else {
363 			if (__gss_import_internal_name(minor_status,
364 				&mech->mech_type, union_name,
365 				&allocated_name) != GSS_S_COMPLETE) {
366 				status = GSS_S_BAD_NAME;
367 				goto errout;
368 			}
369 			internal_name = allocated_name;
370 		}
371 	}
372 
373 	if (cred_usage == GSS_C_ACCEPT)
374 		time_req = acceptor_time_req;
375 	else if (cred_usage == GSS_C_INITIATE)
376 		time_req = initiator_time_req;
377 	else if (cred_usage == GSS_C_BOTH)
378 		time_req = (acceptor_time_req > initiator_time_req) ?
379 			acceptor_time_req : initiator_time_req;
380 
381 	status = mech->gss_acquire_cred(mech->context, minor_status,
382 				internal_name, time_req,
383 				GSS_C_NULL_OID_SET, cred_usage,
384 				&cred, NULL, &time_rec);
385 
386 	if (status != GSS_S_COMPLETE)
387 		goto errout;
388 
389 	/* may need to set credential auxinfo structure */
390 	if (union_cred->auxinfo.creation_time == 0) {
391 		union_cred->auxinfo.creation_time = time(NULL);
392 		union_cred->auxinfo.time_rec = time_rec;
393 		union_cred->auxinfo.cred_usage = cred_usage;
394 
395 	/*
396 	 * If internal_name is GSS_C_NO_NAME a cred with no associated
397 	 * name was requested: don't set auxinfo.name or auxinfo.name_type.
398 	 */
399 		if (internal_name != GSS_C_NO_NAME) {
400 			if ((status = mech->gss_display_name(mech->context,
401 					&temp_minor_status, internal_name,
402 					&union_cred->auxinfo.name,
403 					&union_cred->auxinfo.name_type)) !=
404 				GSS_S_COMPLETE)
405 				goto errout;
406 		}
407 	}
408 
409 	/* now add the new credential elements */
410 	new_mechs_array = (gss_OID)
411 		malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
412 
413 	new_cred_array = (gss_cred_id_t *)
414 		malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
415 
416 	if (!new_mechs_array || !new_cred_array) {
417 		status = GSS_S_FAILURE;
418 		goto errout;
419 	}
420 
421 	if (acceptor_time_rec)
422 		if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
423 			*acceptor_time_rec = time_rec;
424 	if (initiator_time_rec)
425 		if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
426 			*initiator_time_rec = time_rec;
427 
428 	/*
429 	 * OK, expand the mechanism array and the credential array
430 	 */
431 	(void) memcpy(new_mechs_array, union_cred->mechs_array,
432 		sizeof (gss_OID_desc) * union_cred->count);
433 	(void) memcpy(new_cred_array, union_cred->cred_array,
434 		sizeof (gss_cred_id_t) * union_cred->count);
435 
436 	new_cred_array[union_cred->count] = cred;
437 	if ((new_mechs_array[union_cred->count].elements =
438 			malloc(mech->mech_type.length)) == NULL)
439 		goto errout;
440 
441 	g_OID_copy(&new_mechs_array[union_cred->count],
442 			&mech->mech_type);
443 
444 	if (actual_mechs) {
445 		*actual_mechs = create_actual_mechs(new_mechs_array,
446 					union_cred->count + 1);
447 		if (*actual_mechs == NULL) {
448 			free(new_mechs_array[union_cred->count].elements);
449 			goto errout;
450 		}
451 	}
452 
453 	if (output_cred_handle == NULL) {
454 		free(union_cred->mechs_array);
455 		free(union_cred->cred_array);
456 		new_union_cred = union_cred;
457 	} else {
458 		new_union_cred = malloc(sizeof (gss_union_cred_desc));
459 		if (new_union_cred == NULL) {
460 			free(new_mechs_array[union_cred->count].elements);
461 			goto errout;
462 		}
463 		*new_union_cred = *union_cred;
464 		*output_cred_handle = (gss_cred_id_t)new_union_cred;
465 	}
466 
467 	new_union_cred->mechs_array = new_mechs_array;
468 	new_union_cred->cred_array = new_cred_array;
469 	new_union_cred->count++;
470 
471 	/* We're done with the internal name. Free it if we allocated it. */
472 
473 	if (allocated_name)
474 		(void) __gss_release_internal_name(&temp_minor_status,
475 					&mech->mech_type,
476 					&allocated_name);
477 
478 	return (GSS_S_COMPLETE);
479 
480 errout:
481 	if (new_mechs_array)
482 		free(new_mechs_array);
483 	if (new_cred_array)
484 		free(new_cred_array);
485 
486 	if (cred != NULL && mech->gss_release_cred)
487 		mech->gss_release_cred(mech->context,
488 				&temp_minor_status, &cred);
489 
490 	if (allocated_name)
491 		(void) __gss_release_internal_name(&temp_minor_status,
492 					&mech->mech_type,
493 					&allocated_name);
494 
495 	if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
496 		if (union_cred->auxinfo.name.value)
497 			free(union_cred->auxinfo.name.value);
498 		free(union_cred);
499 	}
500 
501 	return (status);
502 }
503