xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_accept_sec_context.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* #pragma ident	"@(#)g_accept_sec_context.c	1.19	04/02/23 SMI" */
2 
3 /*
4  * Copyright 1996 by Sun Microsystems, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear in
10  * supporting documentation, and that the name of Sun Microsystems not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission. Sun Microsystems makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 /*
26  *  glue routine for gss_accept_sec_context
27  */
28 
29 #include "mglueP.h"
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 #include <string.h>
34 #include <errno.h>
35 #include <time.h>
36 
37 #ifndef LEAN_CLIENT
38 static OM_uint32
val_acc_sec_ctx_args(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token_buffer,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * d_cred)39 val_acc_sec_ctx_args(
40     OM_uint32 *minor_status,
41     gss_ctx_id_t *context_handle,
42     gss_cred_id_t verifier_cred_handle,
43     gss_buffer_t input_token_buffer,
44     gss_channel_bindings_t input_chan_bindings,
45     gss_name_t *src_name,
46     gss_OID *mech_type,
47     gss_buffer_t output_token,
48     OM_uint32 *ret_flags,
49     OM_uint32 *time_rec,
50     gss_cred_id_t *d_cred)
51 {
52 
53     /* Initialize outputs. */
54 
55     if (minor_status != NULL)
56 	*minor_status = 0;
57 
58     if (src_name != NULL)
59 	*src_name = GSS_C_NO_NAME;
60 
61     if (mech_type != NULL)
62 	*mech_type = GSS_C_NO_OID;
63 
64     if (output_token != GSS_C_NO_BUFFER) {
65 	output_token->length = 0;
66 	output_token->value = NULL;
67     }
68 
69     if (ret_flags != NULL)
70 	*ret_flags = 0;
71 
72     if (time_rec != NULL)
73 	*time_rec = 0;
74 
75     if (d_cred != NULL)
76 	*d_cred = GSS_C_NO_CREDENTIAL;
77 
78     /* Validate arguments. */
79 
80     if (minor_status == NULL)
81 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
82 
83     if (context_handle == NULL)
84 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
85 
86     if (input_token_buffer == GSS_C_NO_BUFFER)
87 	return (GSS_S_CALL_INACCESSIBLE_READ);
88 
89     if (output_token == GSS_C_NO_BUFFER)
90 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
91 
92     return (GSS_S_COMPLETE);
93 }
94 
95 /* Return true if mech should be accepted with no acceptor credential. */
96 static int
allow_mech_by_default(gss_OID mech)97 allow_mech_by_default(gss_OID mech)
98 {
99     OM_uint32 status, minor;
100     gss_OID_set attrs;
101     int reject = 0, p;
102 
103     /* Whether we accept an interposer mech depends on whether we accept the
104      * mech it interposes. */
105     mech = gssint_get_public_oid(mech);
106     if (mech == GSS_C_NO_OID)
107 	return 0;
108 
109     status = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
110     if (status)
111 	return 0;
112 
113     /* If the mechanism doesn't support RFC 5587, don't exclude it. */
114     if (attrs == GSS_C_NO_OID_SET)
115 	return 1;
116 
117     /* Check for each attribute which would cause us to exclude this mech from
118      * the default credential. */
119     if (generic_gss_test_oid_set_member(&minor, GSS_C_MA_DEPRECATED,
120 					attrs, &p) != GSS_S_COMPLETE || p)
121 	reject = 1;
122     else if (generic_gss_test_oid_set_member(&minor, GSS_C_MA_NOT_DFLT_MECH,
123 					     attrs, &p) != GSS_S_COMPLETE || p)
124 	reject = 1;
125 
126     (void) gss_release_oid_set(&minor, &attrs);
127     return !reject;
128 }
129 
130 OM_uint32 KRB5_CALLCONV
gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token_buffer,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * d_cred)131 gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
132 		       gss_cred_id_t verifier_cred_handle,
133 		       gss_buffer_t input_token_buffer,
134 		       gss_channel_bindings_t input_chan_bindings,
135 		       gss_name_t *src_name, gss_OID *mech_type,
136 		       gss_buffer_t output_token, OM_uint32 *ret_flags,
137 		       OM_uint32 *time_rec, gss_cred_id_t *d_cred)
138 {
139     OM_uint32		status, temp_status, temp_minor_status;
140     OM_uint32		temp_ret_flags = 0;
141     gss_union_ctx_id_t	union_ctx_id = NULL;
142     gss_cred_id_t	input_cred_handle = GSS_C_NO_CREDENTIAL;
143     gss_cred_id_t	tmp_d_cred = GSS_C_NO_CREDENTIAL;
144     gss_name_t		internal_name = GSS_C_NO_NAME;
145     gss_name_t		tmp_src_name = GSS_C_NO_NAME;
146     gss_OID_desc	token_mech_type_desc;
147     gss_OID		token_mech_type = &token_mech_type_desc;
148     gss_OID		actual_mech = GSS_C_NO_OID;
149     gss_OID		selected_mech = GSS_C_NO_OID;
150     gss_OID		public_mech;
151     gss_mechanism	mech = NULL;
152     gss_union_cred_t	uc;
153     int			i;
154 
155     status = val_acc_sec_ctx_args(minor_status,
156 				  context_handle,
157 				  verifier_cred_handle,
158 				  input_token_buffer,
159 				  input_chan_bindings,
160 				  src_name,
161 				  mech_type,
162 				  output_token,
163 				  ret_flags,
164 				  time_rec,
165 				  d_cred);
166     if (status != GSS_S_COMPLETE)
167 	return (status);
168 
169     /*
170      * if context_handle is GSS_C_NO_CONTEXT, allocate a union context
171      * descriptor to hold the mech type information as well as the
172      * underlying mechanism context handle. Otherwise, cast the
173      * value of *context_handle to the union context variable.
174      */
175 
176     if(*context_handle == GSS_C_NO_CONTEXT) {
177 
178 	if (input_token_buffer == GSS_C_NO_BUFFER)
179 	    return (GSS_S_CALL_INACCESSIBLE_READ);
180 
181 	/* Get the token mech type */
182 	status = gssint_get_mech_type(token_mech_type, input_token_buffer);
183 	if (status)
184 	    return status;
185 
186 	/*
187 	 * An interposer calling back into the mechglue can't pass in a special
188 	 * mech, so we have to recognize it using verifier_cred_handle.  Use
189 	 * the mechanism for which we have matching creds, if available.
190 	 */
191 	if (verifier_cred_handle != GSS_C_NO_CREDENTIAL) {
192 	    uc = (gss_union_cred_t)verifier_cred_handle;
193 	    for (i = 0; i < uc->count; i++) {
194 		public_mech = gssint_get_public_oid(&uc->mechs_array[i]);
195 		if (public_mech && g_OID_equal(token_mech_type, public_mech)) {
196 		    selected_mech = &uc->mechs_array[i];
197 		    break;
198 		}
199 	    }
200 	}
201 
202 	if (selected_mech == GSS_C_NO_OID) {
203 	    status = gssint_select_mech_type(minor_status, token_mech_type,
204 					     &selected_mech);
205 	    if (status)
206 		return status;
207 	}
208 
209     } else {
210 	union_ctx_id = (gss_union_ctx_id_t)*context_handle;
211 	selected_mech = union_ctx_id->mech_type;
212 	if (union_ctx_id->internal_ctx_id == GSS_C_NO_CONTEXT)
213 	    return (GSS_S_NO_CONTEXT);
214     }
215 
216     /* Now create a new context if we didn't get one. */
217     if (*context_handle == GSS_C_NO_CONTEXT) {
218 	status = gssint_create_union_context(minor_status, selected_mech,
219 					     &union_ctx_id);
220 	if (status != GSS_S_COMPLETE)
221 	    return (status);
222     }
223 
224     /*
225      * get the appropriate cred handle from the union cred struct.
226      */
227     if (verifier_cred_handle != GSS_C_NO_CREDENTIAL) {
228 	input_cred_handle =
229 	    gssint_get_mechanism_cred((gss_union_cred_t)verifier_cred_handle,
230 				      selected_mech);
231 	if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
232 	    /* verifier credential specified but no acceptor credential found */
233 	    status = GSS_S_NO_CRED;
234 	    goto error_out;
235 	}
236     } else if (!allow_mech_by_default(selected_mech)) {
237 	status = GSS_S_NO_CRED;
238 	goto error_out;
239     }
240 
241     /*
242      * now select the approprate underlying mechanism routine and
243      * call it.
244      */
245 
246     mech = gssint_get_mechanism(selected_mech);
247     if (mech && mech->gss_accept_sec_context) {
248 
249 	    status = mech->gss_accept_sec_context(minor_status,
250 						  &union_ctx_id->internal_ctx_id,
251 						  input_cred_handle,
252 						  input_token_buffer,
253 						  input_chan_bindings,
254 						  src_name ? &internal_name : NULL,
255 						  &actual_mech,
256 						  output_token,
257 						  &temp_ret_flags,
258 						  time_rec,
259 					d_cred ? &tmp_d_cred : NULL);
260 
261 	    /* If there's more work to do, keep going... */
262 	    if (status == GSS_S_CONTINUE_NEEDED) {
263 		*context_handle = (gss_ctx_id_t)union_ctx_id;
264 		return GSS_S_CONTINUE_NEEDED;
265 	    }
266 
267 	    /* if the call failed, return with failure */
268 	    if (status != GSS_S_COMPLETE) {
269 		map_error(minor_status, mech);
270 		goto error_out;
271 	    }
272 
273 	    /*
274 	     * if src_name is non-NULL,
275 	     * convert internal_name into a union name equivalent
276 	     * First call the mechanism specific display_name()
277 	     * then call gss_import_name() to create
278 	     * the union name struct cast to src_name
279 	     */
280 	    if (src_name != NULL) {
281 		if (internal_name != GSS_C_NO_NAME) {
282 		    /* consumes internal_name regardless of success */
283 		    temp_status = gssint_convert_name_to_union_name(
284 			    &temp_minor_status, mech,
285 			    internal_name, &tmp_src_name);
286 		    if (temp_status != GSS_S_COMPLETE) {
287 			status = temp_status;
288 			*minor_status = temp_minor_status;
289 			map_error(minor_status, mech);
290 			if (output_token->length)
291 			    (void) gss_release_buffer(&temp_minor_status,
292 						      output_token);
293 			goto error_out;
294 		    }
295 		    *src_name = tmp_src_name;
296 		} else
297 		    *src_name = GSS_C_NO_NAME;
298 	    }
299 
300 #define g_OID_prefix_equal(o1, o2) \
301         (((o1)->length >= (o2)->length) && \
302         (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0))
303 
304 	    /* Ensure we're returning correct creds format */
305 	    if ((temp_ret_flags & GSS_C_DELEG_FLAG) &&
306 		tmp_d_cred != GSS_C_NO_CREDENTIAL) {
307 		public_mech = gssint_get_public_oid(selected_mech);
308 		if (actual_mech != GSS_C_NO_OID &&
309 		    public_mech != GSS_C_NO_OID &&
310 		    !g_OID_prefix_equal(actual_mech, public_mech)) {
311 		    *d_cred = tmp_d_cred; /* unwrapped pseudo-mech */
312 		} else {
313 		    gss_union_cred_t d_u_cred = NULL;
314 
315 		    d_u_cred = malloc(sizeof (gss_union_cred_desc));
316 		    if (d_u_cred == NULL) {
317 			status = GSS_S_FAILURE;
318 			goto error_out;
319 		    }
320 		    (void) memset(d_u_cred, 0, sizeof (gss_union_cred_desc));
321 
322 		    d_u_cred->count = 1;
323 
324 		    status = generic_gss_copy_oid(&temp_minor_status,
325 						  selected_mech,
326 						  &d_u_cred->mechs_array);
327 
328 		    if (status != GSS_S_COMPLETE) {
329 			free(d_u_cred);
330 			goto error_out;
331 		    }
332 
333 		    d_u_cred->cred_array = malloc(sizeof(gss_cred_id_t));
334 		    if (d_u_cred->cred_array != NULL) {
335 			d_u_cred->cred_array[0] = tmp_d_cred;
336 		    } else {
337 			free(d_u_cred);
338 			status = GSS_S_FAILURE;
339 			goto error_out;
340 		    }
341 
342 		    d_u_cred->loopback = d_u_cred;
343 		    *d_cred = (gss_cred_id_t)d_u_cred;
344 		}
345 	    }
346 
347 	    if (mech_type != NULL)
348 		*mech_type = gssint_get_public_oid(actual_mech);
349 	    if (ret_flags != NULL)
350 		*ret_flags = temp_ret_flags;
351 	    *context_handle = (gss_ctx_id_t)union_ctx_id;
352 	    return GSS_S_COMPLETE;
353     } else {
354 
355 	status = GSS_S_BAD_MECH;
356     }
357 
358 error_out:
359 	/*
360 	 * RFC 2744 5.1 requires that we not create a context on a failed first
361 	 * call to accept, and recommends that on a failed subsequent call we
362 	 * make the caller responsible for calling gss_delete_sec_context.
363 	 * Even if the mech deleted its context, keep the union context around
364 	 * for the caller to delete.
365 	 */
366     if (union_ctx_id && *context_handle == GSS_C_NO_CONTEXT) {
367 	if (union_ctx_id->mech_type) {
368 	    if (union_ctx_id->mech_type->elements)
369 		free(union_ctx_id->mech_type->elements);
370 	    free(union_ctx_id->mech_type);
371 	}
372 	if (union_ctx_id->internal_ctx_id && mech &&
373 	    mech->gss_delete_sec_context) {
374 	    mech->gss_delete_sec_context(&temp_minor_status,
375 					 &union_ctx_id->internal_ctx_id,
376 					 GSS_C_NO_BUFFER);
377 	}
378 	free(union_ctx_id);
379     }
380 
381     if (src_name)
382 	*src_name = GSS_C_NO_NAME;
383 
384     if (tmp_src_name != GSS_C_NO_NAME)
385 	(void) gss_release_buffer(&temp_minor_status,
386 				  (gss_buffer_t)tmp_src_name);
387 
388     return (status);
389 }
390 #endif /* LEAN_CLIENT */
391