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