xref: /freebsd/lib/libgssapi/gss_accept_sec_context.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1c0b9f4feSDoug Rabson /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
4c0b9f4feSDoug Rabson  * Copyright (c) 2005 Doug Rabson
5c0b9f4feSDoug Rabson  * All rights reserved.
6c0b9f4feSDoug Rabson  *
7c0b9f4feSDoug Rabson  * Redistribution and use in source and binary forms, with or without
8c0b9f4feSDoug Rabson  * modification, are permitted provided that the following conditions
9c0b9f4feSDoug Rabson  * are met:
10c0b9f4feSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11c0b9f4feSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12c0b9f4feSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
13c0b9f4feSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
14c0b9f4feSDoug Rabson  *    documentation and/or other materials provided with the distribution.
15c0b9f4feSDoug Rabson  *
16c0b9f4feSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c0b9f4feSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c0b9f4feSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c0b9f4feSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c0b9f4feSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c0b9f4feSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c0b9f4feSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c0b9f4feSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c0b9f4feSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c0b9f4feSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c0b9f4feSDoug Rabson  * SUCH DAMAGE.
27c0b9f4feSDoug Rabson  */
28c0b9f4feSDoug Rabson 
29c0b9f4feSDoug Rabson #include <gssapi/gssapi.h>
30c0b9f4feSDoug Rabson #include <stdlib.h>
313aebdb89SAlexander Kabaev #include <string.h>
32c0b9f4feSDoug Rabson #include <errno.h>
33c0b9f4feSDoug Rabson 
34c0b9f4feSDoug Rabson #include "mech_switch.h"
35c0b9f4feSDoug Rabson #include "context.h"
36c0b9f4feSDoug Rabson #include "cred.h"
37c0b9f4feSDoug Rabson #include "name.h"
3833f12199SDoug Rabson #include "utils.h"
39c0b9f4feSDoug Rabson 
4033f12199SDoug Rabson static OM_uint32
parse_header(const gss_buffer_t input_token,gss_OID mech_oid)4133f12199SDoug Rabson parse_header(const gss_buffer_t input_token, gss_OID mech_oid)
42c0b9f4feSDoug Rabson {
43c0b9f4feSDoug Rabson 	unsigned char *p = input_token->value;
44c0b9f4feSDoug Rabson 	size_t len = input_token->length;
45c0b9f4feSDoug Rabson 	size_t a, b;
46c0b9f4feSDoug Rabson 
47c0b9f4feSDoug Rabson 	/*
48c0b9f4feSDoug Rabson 	 * Token must start with [APPLICATION 0] SEQUENCE.
4933f12199SDoug Rabson 	 * But if it doesn't assume it is DCE-STYLE Kerberos!
50c0b9f4feSDoug Rabson 	 */
5133f12199SDoug Rabson 	if (len == 0)
52c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
5333f12199SDoug Rabson 
54c0b9f4feSDoug Rabson 	p++;
55c0b9f4feSDoug Rabson 	len--;
56c0b9f4feSDoug Rabson 
57c0b9f4feSDoug Rabson 	/*
58c0b9f4feSDoug Rabson 	 * Decode the length and make sure it agrees with the
59c0b9f4feSDoug Rabson 	 * token length.
60c0b9f4feSDoug Rabson 	 */
61c0b9f4feSDoug Rabson 	if (len == 0)
62c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
63c0b9f4feSDoug Rabson 	if ((*p & 0x80) == 0) {
64c0b9f4feSDoug Rabson 		a = *p;
65c0b9f4feSDoug Rabson 		p++;
66c0b9f4feSDoug Rabson 		len--;
67c0b9f4feSDoug Rabson 	} else {
68c0b9f4feSDoug Rabson 		b = *p & 0x7f;
69c0b9f4feSDoug Rabson 		p++;
70c0b9f4feSDoug Rabson 		len--;
71c0b9f4feSDoug Rabson 		if (len < b)
72c0b9f4feSDoug Rabson 		    return (GSS_S_DEFECTIVE_TOKEN);
73c0b9f4feSDoug Rabson 		a = 0;
74c0b9f4feSDoug Rabson 		while (b) {
75c0b9f4feSDoug Rabson 		    a = (a << 8) | *p;
76c0b9f4feSDoug Rabson 		    p++;
77c0b9f4feSDoug Rabson 		    len--;
78c0b9f4feSDoug Rabson 		    b--;
79c0b9f4feSDoug Rabson 		}
80c0b9f4feSDoug Rabson 	}
81c0b9f4feSDoug Rabson 	if (a != len)
82c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
83c0b9f4feSDoug Rabson 
84c0b9f4feSDoug Rabson 	/*
85c0b9f4feSDoug Rabson 	 * Decode the OID for the mechanism. Simplify life by
86c0b9f4feSDoug Rabson 	 * assuming that the OID length is less than 128 bytes.
87c0b9f4feSDoug Rabson 	 */
88c0b9f4feSDoug Rabson 	if (len < 2 || *p != 0x06)
89c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
90c0b9f4feSDoug Rabson 	if ((p[1] & 0x80) || p[1] > (len - 2))
91c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
9233f12199SDoug Rabson 	mech_oid->length = p[1];
93c0b9f4feSDoug Rabson 	p += 2;
94c0b9f4feSDoug Rabson 	len -= 2;
9533f12199SDoug Rabson 	mech_oid->elements = p;
9633f12199SDoug Rabson 
9733f12199SDoug Rabson 	return (GSS_S_COMPLETE);
9833f12199SDoug Rabson }
9933f12199SDoug Rabson 
10033f12199SDoug Rabson static gss_OID_desc krb5_mechanism =
101b16d6675SBrooks Davis {9, __DECONST(void *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
10233f12199SDoug Rabson static gss_OID_desc ntlm_mechanism =
103b16d6675SBrooks Davis {10, __DECONST(void *, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")};
10433f12199SDoug Rabson static gss_OID_desc spnego_mechanism =
105b16d6675SBrooks Davis {6, __DECONST(void *, "\x2b\x06\x01\x05\x05\x02")};
10633f12199SDoug Rabson 
10733f12199SDoug Rabson static OM_uint32
choose_mech(const gss_buffer_t input,gss_OID mech_oid)10833f12199SDoug Rabson choose_mech(const gss_buffer_t input, gss_OID mech_oid)
10933f12199SDoug Rabson {
11033f12199SDoug Rabson 	OM_uint32 status;
11133f12199SDoug Rabson 
11233f12199SDoug Rabson 	/*
11333f12199SDoug Rabson 	 * First try to parse the gssapi token header and see if it's a
11433f12199SDoug Rabson 	 * correct header, use that in the first hand.
11533f12199SDoug Rabson 	 */
11633f12199SDoug Rabson 
11733f12199SDoug Rabson 	status = parse_header(input, mech_oid);
11833f12199SDoug Rabson 	if (status == GSS_S_COMPLETE)
11933f12199SDoug Rabson 		return (GSS_S_COMPLETE);
12033f12199SDoug Rabson 
12133f12199SDoug Rabson 	/*
12233f12199SDoug Rabson 	 * Lets guess what mech is really is, callback function to mech ??
12333f12199SDoug Rabson 	 */
12433f12199SDoug Rabson 
12533f12199SDoug Rabson 	if (input->length > 8 &&
12633f12199SDoug Rabson 	    memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
12733f12199SDoug Rabson 	{
12833f12199SDoug Rabson 		*mech_oid = ntlm_mechanism;
12933f12199SDoug Rabson 		return (GSS_S_COMPLETE);
13033f12199SDoug Rabson 	} else if (input->length != 0 &&
13133f12199SDoug Rabson 	    ((const char *)input->value)[0] == 0x6E)
13233f12199SDoug Rabson 	{
13333f12199SDoug Rabson 		/* Could be a raw AP-REQ (check for APPLICATION tag) */
13433f12199SDoug Rabson 		*mech_oid = krb5_mechanism;
13533f12199SDoug Rabson 		return (GSS_S_COMPLETE);
13633f12199SDoug Rabson 	} else if (input->length == 0) {
13733f12199SDoug Rabson 		/*
13875f46cf6SPedro F. Giffuni 		 * There is the a weird mode of SPNEGO (in CIFS and
13933f12199SDoug Rabson 		 * SASL GSS-SPENGO where the first token is zero
14033f12199SDoug Rabson 		 * length and the acceptor returns a mech_list, lets
14133f12199SDoug Rabson 		 * hope that is what is happening now.
14233f12199SDoug Rabson 		 */
14333f12199SDoug Rabson 		*mech_oid = spnego_mechanism;
14433f12199SDoug Rabson 		return (GSS_S_COMPLETE);
14533f12199SDoug Rabson 	}
14633f12199SDoug Rabson 	return (status);
14733f12199SDoug Rabson }
14833f12199SDoug Rabson 
gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,const gss_cred_id_t acceptor_cred_handle,const gss_buffer_t input_token,const 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 * delegated_cred_handle)14933f12199SDoug Rabson OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
15033f12199SDoug Rabson     gss_ctx_id_t *context_handle,
15133f12199SDoug Rabson     const gss_cred_id_t acceptor_cred_handle,
15233f12199SDoug Rabson     const gss_buffer_t input_token,
15333f12199SDoug Rabson     const gss_channel_bindings_t input_chan_bindings,
15433f12199SDoug Rabson     gss_name_t *src_name,
15533f12199SDoug Rabson     gss_OID *mech_type,
15633f12199SDoug Rabson     gss_buffer_t output_token,
15733f12199SDoug Rabson     OM_uint32 *ret_flags,
15833f12199SDoug Rabson     OM_uint32 *time_rec,
15933f12199SDoug Rabson     gss_cred_id_t *delegated_cred_handle)
16033f12199SDoug Rabson {
16133f12199SDoug Rabson 	OM_uint32 major_status, mech_ret_flags;
16233f12199SDoug Rabson 	struct _gss_mech_switch *m;
16333f12199SDoug Rabson 	struct _gss_context *ctx = (struct _gss_context *) *context_handle;
16433f12199SDoug Rabson 	struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
16533f12199SDoug Rabson 	struct _gss_mechanism_cred *mc;
16633f12199SDoug Rabson 	gss_cred_id_t acceptor_mc, delegated_mc;
16733f12199SDoug Rabson 	gss_name_t src_mn;
16833f12199SDoug Rabson 
16933f12199SDoug Rabson 	*minor_status = 0;
17033f12199SDoug Rabson 	if (src_name)
17133f12199SDoug Rabson 		*src_name = GSS_C_NO_NAME;
17233f12199SDoug Rabson 	if (mech_type)
17333f12199SDoug Rabson 		*mech_type = GSS_C_NO_OID;
17433f12199SDoug Rabson 	if (ret_flags)
17533f12199SDoug Rabson 		*ret_flags = 0;
17633f12199SDoug Rabson 	if (time_rec)
17733f12199SDoug Rabson 		*time_rec = 0;
17833f12199SDoug Rabson 	if (delegated_cred_handle)
17933f12199SDoug Rabson 		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
18033f12199SDoug Rabson 	_gss_buffer_zero(output_token);
18133f12199SDoug Rabson 
18233f12199SDoug Rabson 	/*
18333f12199SDoug Rabson 	 * If this is the first call (*context_handle is NULL), we must
18433f12199SDoug Rabson 	 * parse the input token to figure out the mechanism to use.
18533f12199SDoug Rabson 	 */
18633f12199SDoug Rabson 	if (*context_handle == GSS_C_NO_CONTEXT) {
18733f12199SDoug Rabson 		gss_OID_desc mech_oid;
18833f12199SDoug Rabson 
18933f12199SDoug Rabson 		major_status = choose_mech(input_token, &mech_oid);
19033f12199SDoug Rabson 		if (major_status != GSS_S_COMPLETE)
19133f12199SDoug Rabson 			return (major_status);
192c0b9f4feSDoug Rabson 
193c0b9f4feSDoug Rabson 		/*
194c0b9f4feSDoug Rabson 		 * Now that we have a mechanism, we can find the
195c0b9f4feSDoug Rabson 		 * implementation.
196c0b9f4feSDoug Rabson 		 */
197c0b9f4feSDoug Rabson 		ctx = malloc(sizeof(struct _gss_context));
198c0b9f4feSDoug Rabson 		if (!ctx) {
199c0b9f4feSDoug Rabson 			*minor_status = ENOMEM;
200c0b9f4feSDoug Rabson 			return (GSS_S_DEFECTIVE_TOKEN);
201c0b9f4feSDoug Rabson 		}
202c0b9f4feSDoug Rabson 		memset(ctx, 0, sizeof(struct _gss_context));
203c0b9f4feSDoug Rabson 		m = ctx->gc_mech = _gss_find_mech_switch(&mech_oid);
204c0b9f4feSDoug Rabson 		if (!m) {
205c0b9f4feSDoug Rabson 			free(ctx);
206c0b9f4feSDoug Rabson 			return (GSS_S_BAD_MECH);
207c0b9f4feSDoug Rabson 		}
208f2155136SMarcelo Araujo 	} else
209c0b9f4feSDoug Rabson 		m = ctx->gc_mech;
210c0b9f4feSDoug Rabson 
211c0b9f4feSDoug Rabson 	if (cred) {
212c0b9f4feSDoug Rabson 		SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
213c0b9f4feSDoug Rabson 			if (mc->gmc_mech == m)
214c0b9f4feSDoug Rabson 				break;
215c0b9f4feSDoug Rabson 		if (!mc)
216c0b9f4feSDoug Rabson 			return (GSS_S_BAD_MECH);
217c0b9f4feSDoug Rabson 		acceptor_mc = mc->gmc_cred;
218c0b9f4feSDoug Rabson 	} else {
219c0b9f4feSDoug Rabson 		acceptor_mc = GSS_C_NO_CREDENTIAL;
220c0b9f4feSDoug Rabson 	}
221c0b9f4feSDoug Rabson 	delegated_mc = GSS_C_NO_CREDENTIAL;
222c0b9f4feSDoug Rabson 
22333f12199SDoug Rabson 	mech_ret_flags = 0;
224c0b9f4feSDoug Rabson 	major_status = m->gm_accept_sec_context(minor_status,
225c0b9f4feSDoug Rabson 	    &ctx->gc_ctx,
226c0b9f4feSDoug Rabson 	    acceptor_mc,
227c0b9f4feSDoug Rabson 	    input_token,
228c0b9f4feSDoug Rabson 	    input_chan_bindings,
229c0b9f4feSDoug Rabson 	    &src_mn,
230c0b9f4feSDoug Rabson 	    mech_type,
231c0b9f4feSDoug Rabson 	    output_token,
23222a25490SDoug Rabson 	    &mech_ret_flags,
233c0b9f4feSDoug Rabson 	    time_rec,
234c0b9f4feSDoug Rabson 	    &delegated_mc);
235c0b9f4feSDoug Rabson 	if (major_status != GSS_S_COMPLETE &&
23633f12199SDoug Rabson 	    major_status != GSS_S_CONTINUE_NEEDED) {
23733f12199SDoug Rabson 		_gss_mg_error(m, major_status, *minor_status);
238c0b9f4feSDoug Rabson 		return (major_status);
23933f12199SDoug Rabson 	}
240c0b9f4feSDoug Rabson 
24133f12199SDoug Rabson 	if (src_name && src_mn) {
242c0b9f4feSDoug Rabson 		/*
243c0b9f4feSDoug Rabson 		 * Make a new name and mark it as an MN.
244c0b9f4feSDoug Rabson 		 */
245c0b9f4feSDoug Rabson 		struct _gss_name *name = _gss_make_name(m, src_mn);
246c0b9f4feSDoug Rabson 
247c0b9f4feSDoug Rabson 		if (!name) {
248c0b9f4feSDoug Rabson 			m->gm_release_name(minor_status, &src_mn);
249c0b9f4feSDoug Rabson 			return (GSS_S_FAILURE);
250c0b9f4feSDoug Rabson 		}
251c0b9f4feSDoug Rabson 		*src_name = (gss_name_t) name;
25233f12199SDoug Rabson 	} else if (src_mn) {
25333f12199SDoug Rabson 		m->gm_release_name(minor_status, &src_mn);
254c0b9f4feSDoug Rabson 	}
255c0b9f4feSDoug Rabson 
256e1a0d9efSDoug Rabson 	if (delegated_mc == GSS_C_NO_CREDENTIAL)
257e1a0d9efSDoug Rabson 		mech_ret_flags &= ~GSS_C_DELEG_FLAG;
258e1a0d9efSDoug Rabson 
25922a25490SDoug Rabson 	if (mech_ret_flags & GSS_C_DELEG_FLAG) {
260c0b9f4feSDoug Rabson 		if (!delegated_cred_handle) {
261c0b9f4feSDoug Rabson 			m->gm_release_cred(minor_status, &delegated_mc);
262e1a0d9efSDoug Rabson 			mech_ret_flags &= ~GSS_C_DELEG_FLAG;
263c0b9f4feSDoug Rabson 		} else {
26433f12199SDoug Rabson 			struct _gss_cred *dcred;
26533f12199SDoug Rabson 			struct _gss_mechanism_cred *dmc;
266c0b9f4feSDoug Rabson 
26733f12199SDoug Rabson 			dcred = malloc(sizeof(struct _gss_cred));
26833f12199SDoug Rabson 			if (!dcred) {
269c0b9f4feSDoug Rabson 				*minor_status = ENOMEM;
270c0b9f4feSDoug Rabson 				return (GSS_S_FAILURE);
271c0b9f4feSDoug Rabson 			}
27233f12199SDoug Rabson 			SLIST_INIT(&dcred->gc_mc);
27333f12199SDoug Rabson 			dmc = malloc(sizeof(struct _gss_mechanism_cred));
27433f12199SDoug Rabson 			if (!dmc) {
27533f12199SDoug Rabson 				free(dcred);
276c0b9f4feSDoug Rabson 				*minor_status = ENOMEM;
277c0b9f4feSDoug Rabson 				return (GSS_S_FAILURE);
278c0b9f4feSDoug Rabson 			}
27933f12199SDoug Rabson 			dmc->gmc_mech = m;
28033f12199SDoug Rabson 			dmc->gmc_mech_oid = &m->gm_mech_oid;
28133f12199SDoug Rabson 			dmc->gmc_cred = delegated_mc;
28233f12199SDoug Rabson 			SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
283c0b9f4feSDoug Rabson 
28433f12199SDoug Rabson 			*delegated_cred_handle = (gss_cred_id_t) dcred;
285c0b9f4feSDoug Rabson 		}
286c0b9f4feSDoug Rabson 	}
287c0b9f4feSDoug Rabson 
28822a25490SDoug Rabson 	if (ret_flags)
28922a25490SDoug Rabson 		*ret_flags = mech_ret_flags;
290c0b9f4feSDoug Rabson 	*context_handle = (gss_ctx_id_t) ctx;
291c0b9f4feSDoug Rabson 	return (major_status);
292c0b9f4feSDoug Rabson }
293