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