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