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