xref: /illumos-gate/usr/src/lib/libgss/g_accept_sec_context.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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_accept_sec_context
28  */
29 
30 #include <mechglueP.h>
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #include <string.h>
35 #include <errno.h>
36 
37 static OM_uint32
38 val_acc_sec_ctx_args(
39 	OM_uint32 *minor_status,
40 	gss_ctx_id_t *context_handle,
41 	gss_buffer_t input_token_buffer,
42 	gss_name_t *src_name,
43 	gss_OID *mech_type,
44 	gss_buffer_t output_token,
45 	gss_cred_id_t *d_cred)
46 {
47 
48 	/* Initialize outputs. */
49 
50 	if (minor_status != NULL)
51 		*minor_status = 0;
52 
53 	if (src_name != NULL)
54 		*src_name = GSS_C_NO_NAME;
55 
56 	if (mech_type != NULL)
57 		*mech_type = GSS_C_NO_OID;
58 
59 	if (output_token != GSS_C_NO_BUFFER) {
60 		output_token->length = 0;
61 		output_token->value = NULL;
62 	}
63 
64 	if (d_cred != NULL)
65 		*d_cred = GSS_C_NO_CREDENTIAL;
66 
67 	/* Validate arguments. */
68 
69 	if (minor_status == NULL)
70 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
71 
72 	if (context_handle == NULL)
73 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
74 
75 	if (input_token_buffer == GSS_C_NO_BUFFER)
76 		return (GSS_S_CALL_INACCESSIBLE_READ);
77 
78 	if (output_token == GSS_C_NO_BUFFER)
79 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
80 
81 	return (GSS_S_COMPLETE);
82 }
83 
84 OM_uint32
85 gss_accept_sec_context(minor_status,
86 			context_handle,
87 			verifier_cred_handle,
88 			input_token_buffer,
89 			input_chan_bindings,
90 			src_name,
91 			mech_type,
92 			output_token,
93 			ret_flags,
94 			time_rec,
95 			d_cred)
96 
97 OM_uint32 			*minor_status;
98 gss_ctx_id_t			*context_handle;
99 const gss_cred_id_t		verifier_cred_handle;
100 const gss_buffer_t		input_token_buffer;
101 const gss_channel_bindings_t	input_chan_bindings;
102 gss_name_t			*src_name;
103 gss_OID				*mech_type;
104 gss_buffer_t			output_token;
105 OM_uint32			*ret_flags;
106 OM_uint32			*time_rec;
107 gss_cred_id_t			*d_cred; /* delegated cred handle */
108 
109 {
110 	OM_uint32		status, temp_status, t_minstat;
111 	gss_union_ctx_id_t	union_ctx_id;
112 	gss_union_cred_t	union_cred;
113 	gss_cred_id_t	input_cred_handle = GSS_C_NO_CREDENTIAL;
114 	gss_cred_id_t	tmp_d_cred = GSS_C_NO_CREDENTIAL;
115 	gss_name_t		internal_name = GSS_C_NO_NAME;
116 	gss_name_t		tmp_src_name = GSS_C_NO_NAME;
117 	gss_OID_desc	token_mech_type_desc;
118 	gss_OID		token_mech_type = &token_mech_type_desc;
119 	gss_OID		actual_mech = GSS_C_NO_OID;
120 	OM_uint32	flags;
121 	gss_mechanism	mech;
122 
123 	status = val_acc_sec_ctx_args(minor_status,
124 				context_handle,
125 				input_token_buffer,
126 				src_name,
127 				mech_type,
128 				output_token,
129 				d_cred);
130 	if (status != GSS_S_COMPLETE)
131 		return (status);
132 
133 	/*
134 	 * if context_handle is GSS_C_NO_CONTEXT, allocate a union context
135 	 * descriptor to hold the mech type information as well as the
136 	 * underlying mechanism context handle. Otherwise, cast the
137 	 * value of *context_handle to the union context variable.
138 	 */
139 
140 	if (*context_handle == GSS_C_NO_CONTEXT) {
141 
142 		if (input_token_buffer == GSS_C_NO_BUFFER)
143 			return (GSS_S_CALL_INACCESSIBLE_READ);
144 
145 		/* Get the token mech type */
146 		status = __gss_get_mech_type(token_mech_type,
147 					input_token_buffer);
148 
149 		if (status)
150 			return (status);
151 
152 		status = GSS_S_FAILURE;
153 		union_ctx_id = (gss_union_ctx_id_t)
154 			malloc(sizeof (gss_union_ctx_id_desc));
155 		if (!union_ctx_id)
156 			return (GSS_S_FAILURE);
157 
158 		union_ctx_id->internal_ctx_id = GSS_C_NO_CONTEXT;
159 		status = generic_gss_copy_oid(&t_minstat,
160 					token_mech_type,
161 					&union_ctx_id->mech_type);
162 		if (status != GSS_S_COMPLETE) {
163 			free(union_ctx_id);
164 			return (status);
165 		}
166 
167 		/* set the new context handle to caller's data */
168 		*context_handle = (gss_ctx_id_t)union_ctx_id;
169 	} else {
170 		union_ctx_id = (gss_union_ctx_id_t)*context_handle;
171 		token_mech_type = union_ctx_id->mech_type;
172 	}
173 
174 	/*
175 	 * get the appropriate cred handle from the union cred struct.
176 	 * defaults to GSS_C_NO_CREDENTIAL if there is no cred, which will
177 	 * use the default credential.
178 	 */
179 	union_cred = (gss_union_cred_t)verifier_cred_handle;
180 	input_cred_handle = __gss_get_mechanism_cred(union_cred,
181 						token_mech_type);
182 
183 	/*
184 	 * now select the approprate underlying mechanism routine and
185 	 * call it.
186 	 */
187 
188 	mech = __gss_get_mechanism(token_mech_type);
189 	if (mech && mech->gss_accept_sec_context) {
190 		status = mech->gss_accept_sec_context(
191 					mech->context,
192 					minor_status,
193 					&union_ctx_id->internal_ctx_id,
194 					input_cred_handle,
195 					input_token_buffer,
196 					input_chan_bindings,
197 					&internal_name,
198 					&actual_mech,
199 					output_token,
200 					&flags,
201 					time_rec,
202 					d_cred ? &tmp_d_cred : NULL);
203 
204 		/* If there's more work to do, keep going... */
205 		if (status == GSS_S_CONTINUE_NEEDED)
206 			return (GSS_S_CONTINUE_NEEDED);
207 
208 		/* if the call failed, return with failure */
209 		if (status != GSS_S_COMPLETE)
210 			goto error_out;
211 
212 		if (mech_type != NULL)
213 			*mech_type = actual_mech;
214 
215 		/*
216 		 * if src_name is non-NULL,
217 		 * convert internal_name into a union name equivalent
218 		 * First call the mechanism specific display_name()
219 		 * then call gss_import_name() to create
220 		 * the union name struct cast to src_name
221 		 */
222 		if (internal_name != NULL) {
223 			temp_status = __gss_convert_name_to_union_name(
224 				&t_minstat, mech,
225 				internal_name, &tmp_src_name);
226 			if (temp_status != GSS_S_COMPLETE) {
227 				*minor_status = t_minstat;
228 				if (output_token->length)
229 					(void) gss_release_buffer(
230 						&t_minstat,
231 						output_token);
232 				if (internal_name != GSS_C_NO_NAME)
233 					mech->gss_release_name(
234 						mech->context,
235 						&t_minstat,
236 						&internal_name);
237 				return (temp_status);
238 			}
239 			if (src_name != NULL) {
240 				*src_name = tmp_src_name;
241 			}
242 		} else if (src_name != NULL) {
243 			*src_name = GSS_C_NO_NAME;
244 		}
245 
246 		/* Ensure we're returning correct creds format */
247 		if ((flags & GSS_C_DELEG_FLAG) &&
248 		    tmp_d_cred != GSS_C_NO_CREDENTIAL) {
249 			/*
250 			 * If we got back an OID different from the original
251 			 * token OID, assume the delegated_cred is already
252 			 * a proper union_cred and just return it.  Don't
253 			 * try to re-wrap it.  This is for SPNEGO or other
254 			 * pseudo-mechanisms.
255 			 */
256 			if (actual_mech != GSS_C_NO_OID &&
257 			    token_mech_type != GSS_C_NO_OID &&
258 			    !g_OID_equal(actual_mech, token_mech_type)) {
259 				*d_cred = tmp_d_cred;
260 			} else {
261 				gss_union_cred_t d_u_cred = NULL;
262 
263 				d_u_cred = malloc(sizeof (gss_union_cred_desc));
264 				if (d_u_cred == NULL) {
265 					status = GSS_S_FAILURE;
266 					goto error_out;
267 				}
268 				(void) memset(d_u_cred, 0,
269 					    sizeof (gss_union_cred_desc));
270 
271 				d_u_cred->count = 1;
272 
273 				status = generic_gss_copy_oid(
274 					&t_minstat,
275 					actual_mech,
276 					&d_u_cred->mechs_array);
277 
278 				if (status != GSS_S_COMPLETE) {
279 					free(d_u_cred);
280 					goto error_out;
281 				}
282 
283 				d_u_cred->cred_array = malloc(
284 						sizeof (gss_cred_id_t));
285 				if (d_u_cred->cred_array != NULL) {
286 					d_u_cred->cred_array[0] = tmp_d_cred;
287 				} else {
288 					free(d_u_cred);
289 					status = GSS_S_FAILURE;
290 					goto error_out;
291 				}
292 
293 				if (status != GSS_S_COMPLETE) {
294 					free(d_u_cred->cred_array);
295 					free(d_u_cred);
296 					goto error_out;
297 				}
298 
299 				internal_name = GSS_C_NO_NAME;
300 
301 				d_u_cred->auxinfo.creation_time = time(0);
302 				d_u_cred->auxinfo.time_rec = 0;
303 
304 				if (mech->gss_inquire_cred) {
305 					status = mech->gss_inquire_cred(
306 						mech->context,
307 						minor_status,
308 						tmp_d_cred,
309 						&internal_name,
310 						&d_u_cred->auxinfo.time_rec,
311 						&d_u_cred->auxinfo.cred_usage,
312 						NULL);
313 				}
314 
315 				if (internal_name != NULL) {
316 					temp_status =
317 					    __gss_convert_name_to_union_name(
318 						&t_minstat, mech,
319 						internal_name, &tmp_src_name);
320 					if (temp_status != GSS_S_COMPLETE) {
321 						*minor_status = t_minstat;
322 						if (output_token->length)
323 						    (void) gss_release_buffer(
324 								&t_minstat,
325 								output_token);
326 						free(d_u_cred->cred_array);
327 						free(d_u_cred);
328 						return (temp_status);
329 					}
330 				}
331 
332 				if (tmp_src_name != NULL) {
333 					status = gss_display_name(
334 						&t_minstat,
335 						tmp_src_name,
336 						&d_u_cred->auxinfo.name,
337 						&d_u_cred->auxinfo.name_type);
338 				}
339 
340 				*d_cred = (gss_cred_id_t)d_u_cred;
341 			}
342 		}
343 		if (ret_flags != NULL) {
344 			*ret_flags = flags;
345 		}
346 
347 		if (src_name == NULL && tmp_src_name != NULL)
348 			(void) gss_release_name(&t_minstat,
349 					&tmp_src_name);
350 		return	(status);
351 	} else {
352 
353 		status = GSS_S_BAD_MECH;
354 	}
355 
356 error_out:
357 	if (union_ctx_id) {
358 		if (union_ctx_id->mech_type) {
359 			if (union_ctx_id->mech_type->elements)
360 				free(union_ctx_id->mech_type->elements);
361 			free(union_ctx_id->mech_type);
362 		}
363 		free(union_ctx_id);
364 		*context_handle = GSS_C_NO_CONTEXT;
365 	}
366 
367 #if 0
368 	/*
369 	 * Solaris Kerberos
370 	 * Don't release, it causes a problem with error token.
371 	 */
372 	if (output_token->length)
373 		(void) gss_release_buffer(&t_minstat, output_token);
374 #endif
375 
376 	if (src_name)
377 		*src_name = GSS_C_NO_NAME;
378 
379 	if (tmp_src_name != GSS_C_NO_NAME)
380 		(void) gss_release_buffer(&t_minstat,
381 			(gss_buffer_t)tmp_src_name);
382 
383 	return (status);
384 }
385