xref: /freebsd/lib/libgssapi/gss_accept_sec_context.c (revision b16d66754593dbf64047f5aafa32802660581adf)
1c0b9f4feSDoug Rabson /*-
25e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  *	$FreeBSD$
29c0b9f4feSDoug Rabson  */
30c0b9f4feSDoug Rabson 
31c0b9f4feSDoug Rabson #include <gssapi/gssapi.h>
32c0b9f4feSDoug Rabson #include <stdlib.h>
333aebdb89SAlexander Kabaev #include <string.h>
34c0b9f4feSDoug Rabson #include <errno.h>
35c0b9f4feSDoug Rabson 
36c0b9f4feSDoug Rabson #include "mech_switch.h"
37c0b9f4feSDoug Rabson #include "context.h"
38c0b9f4feSDoug Rabson #include "cred.h"
39c0b9f4feSDoug Rabson #include "name.h"
4033f12199SDoug Rabson #include "utils.h"
41c0b9f4feSDoug Rabson 
4233f12199SDoug Rabson static OM_uint32
4333f12199SDoug Rabson parse_header(const gss_buffer_t input_token, gss_OID mech_oid)
44c0b9f4feSDoug Rabson {
45c0b9f4feSDoug Rabson 	unsigned char *p = input_token->value;
46c0b9f4feSDoug Rabson 	size_t len = input_token->length;
47c0b9f4feSDoug Rabson 	size_t a, b;
48c0b9f4feSDoug Rabson 
49c0b9f4feSDoug Rabson 	/*
50c0b9f4feSDoug Rabson 	 * Token must start with [APPLICATION 0] SEQUENCE.
5133f12199SDoug Rabson 	 * But if it doesn't assume it is DCE-STYLE Kerberos!
52c0b9f4feSDoug Rabson 	 */
5333f12199SDoug Rabson 	if (len == 0)
54c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
5533f12199SDoug Rabson 
56c0b9f4feSDoug Rabson 	p++;
57c0b9f4feSDoug Rabson 	len--;
58c0b9f4feSDoug Rabson 
59c0b9f4feSDoug Rabson 	/*
60c0b9f4feSDoug Rabson 	 * Decode the length and make sure it agrees with the
61c0b9f4feSDoug Rabson 	 * token length.
62c0b9f4feSDoug Rabson 	 */
63c0b9f4feSDoug Rabson 	if (len == 0)
64c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
65c0b9f4feSDoug Rabson 	if ((*p & 0x80) == 0) {
66c0b9f4feSDoug Rabson 		a = *p;
67c0b9f4feSDoug Rabson 		p++;
68c0b9f4feSDoug Rabson 		len--;
69c0b9f4feSDoug Rabson 	} else {
70c0b9f4feSDoug Rabson 		b = *p & 0x7f;
71c0b9f4feSDoug Rabson 		p++;
72c0b9f4feSDoug Rabson 		len--;
73c0b9f4feSDoug Rabson 		if (len < b)
74c0b9f4feSDoug Rabson 		    return (GSS_S_DEFECTIVE_TOKEN);
75c0b9f4feSDoug Rabson 		a = 0;
76c0b9f4feSDoug Rabson 		while (b) {
77c0b9f4feSDoug Rabson 		    a = (a << 8) | *p;
78c0b9f4feSDoug Rabson 		    p++;
79c0b9f4feSDoug Rabson 		    len--;
80c0b9f4feSDoug Rabson 		    b--;
81c0b9f4feSDoug Rabson 		}
82c0b9f4feSDoug Rabson 	}
83c0b9f4feSDoug Rabson 	if (a != len)
84c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
85c0b9f4feSDoug Rabson 
86c0b9f4feSDoug Rabson 	/*
87c0b9f4feSDoug Rabson 	 * Decode the OID for the mechanism. Simplify life by
88c0b9f4feSDoug Rabson 	 * assuming that the OID length is less than 128 bytes.
89c0b9f4feSDoug Rabson 	 */
90c0b9f4feSDoug Rabson 	if (len < 2 || *p != 0x06)
91c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
92c0b9f4feSDoug Rabson 	if ((p[1] & 0x80) || p[1] > (len - 2))
93c0b9f4feSDoug Rabson 		return (GSS_S_DEFECTIVE_TOKEN);
9433f12199SDoug Rabson 	mech_oid->length = p[1];
95c0b9f4feSDoug Rabson 	p += 2;
96c0b9f4feSDoug Rabson 	len -= 2;
9733f12199SDoug Rabson 	mech_oid->elements = p;
9833f12199SDoug Rabson 
9933f12199SDoug Rabson 	return (GSS_S_COMPLETE);
10033f12199SDoug Rabson }
10133f12199SDoug Rabson 
10233f12199SDoug Rabson static gss_OID_desc krb5_mechanism =
103*b16d6675SBrooks Davis {9, __DECONST(void *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
10433f12199SDoug Rabson static gss_OID_desc ntlm_mechanism =
105*b16d6675SBrooks Davis {10, __DECONST(void *, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")};
10633f12199SDoug Rabson static gss_OID_desc spnego_mechanism =
107*b16d6675SBrooks Davis {6, __DECONST(void *, "\x2b\x06\x01\x05\x05\x02")};
10833f12199SDoug Rabson 
10933f12199SDoug Rabson static OM_uint32
11033f12199SDoug Rabson choose_mech(const gss_buffer_t input, gss_OID mech_oid)
11133f12199SDoug Rabson {
11233f12199SDoug Rabson 	OM_uint32 status;
11333f12199SDoug Rabson 
11433f12199SDoug Rabson 	/*
11533f12199SDoug Rabson 	 * First try to parse the gssapi token header and see if it's a
11633f12199SDoug Rabson 	 * correct header, use that in the first hand.
11733f12199SDoug Rabson 	 */
11833f12199SDoug Rabson 
11933f12199SDoug Rabson 	status = parse_header(input, mech_oid);
12033f12199SDoug Rabson 	if (status == GSS_S_COMPLETE)
12133f12199SDoug Rabson 		return (GSS_S_COMPLETE);
12233f12199SDoug Rabson 
12333f12199SDoug Rabson 	/*
12433f12199SDoug Rabson 	 * Lets guess what mech is really is, callback function to mech ??
12533f12199SDoug Rabson 	 */
12633f12199SDoug Rabson 
12733f12199SDoug Rabson 	if (input->length > 8 &&
12833f12199SDoug Rabson 	    memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
12933f12199SDoug Rabson 	{
13033f12199SDoug Rabson 		*mech_oid = ntlm_mechanism;
13133f12199SDoug Rabson 		return (GSS_S_COMPLETE);
13233f12199SDoug Rabson 	} else if (input->length != 0 &&
13333f12199SDoug Rabson 	    ((const char *)input->value)[0] == 0x6E)
13433f12199SDoug Rabson 	{
13533f12199SDoug Rabson 		/* Could be a raw AP-REQ (check for APPLICATION tag) */
13633f12199SDoug Rabson 		*mech_oid = krb5_mechanism;
13733f12199SDoug Rabson 		return (GSS_S_COMPLETE);
13833f12199SDoug Rabson 	} else if (input->length == 0) {
13933f12199SDoug Rabson 		/*
14075f46cf6SPedro F. Giffuni 		 * There is the a weird mode of SPNEGO (in CIFS and
14133f12199SDoug Rabson 		 * SASL GSS-SPENGO where the first token is zero
14233f12199SDoug Rabson 		 * length and the acceptor returns a mech_list, lets
14333f12199SDoug Rabson 		 * hope that is what is happening now.
14433f12199SDoug Rabson 		 */
14533f12199SDoug Rabson 		*mech_oid = spnego_mechanism;
14633f12199SDoug Rabson 		return (GSS_S_COMPLETE);
14733f12199SDoug Rabson 	}
14833f12199SDoug Rabson 	return (status);
14933f12199SDoug Rabson }
15033f12199SDoug Rabson 
15133f12199SDoug Rabson OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
15233f12199SDoug Rabson     gss_ctx_id_t *context_handle,
15333f12199SDoug Rabson     const gss_cred_id_t acceptor_cred_handle,
15433f12199SDoug Rabson     const gss_buffer_t input_token,
15533f12199SDoug Rabson     const gss_channel_bindings_t input_chan_bindings,
15633f12199SDoug Rabson     gss_name_t *src_name,
15733f12199SDoug Rabson     gss_OID *mech_type,
15833f12199SDoug Rabson     gss_buffer_t output_token,
15933f12199SDoug Rabson     OM_uint32 *ret_flags,
16033f12199SDoug Rabson     OM_uint32 *time_rec,
16133f12199SDoug Rabson     gss_cred_id_t *delegated_cred_handle)
16233f12199SDoug Rabson {
16333f12199SDoug Rabson 	OM_uint32 major_status, mech_ret_flags;
16433f12199SDoug Rabson 	struct _gss_mech_switch *m;
16533f12199SDoug Rabson 	struct _gss_context *ctx = (struct _gss_context *) *context_handle;
16633f12199SDoug Rabson 	struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
16733f12199SDoug Rabson 	struct _gss_mechanism_cred *mc;
16833f12199SDoug Rabson 	gss_cred_id_t acceptor_mc, delegated_mc;
16933f12199SDoug Rabson 	gss_name_t src_mn;
17033f12199SDoug Rabson 
17133f12199SDoug Rabson 	*minor_status = 0;
17233f12199SDoug Rabson 	if (src_name)
17333f12199SDoug Rabson 		*src_name = GSS_C_NO_NAME;
17433f12199SDoug Rabson 	if (mech_type)
17533f12199SDoug Rabson 		*mech_type = GSS_C_NO_OID;
17633f12199SDoug Rabson 	if (ret_flags)
17733f12199SDoug Rabson 		*ret_flags = 0;
17833f12199SDoug Rabson 	if (time_rec)
17933f12199SDoug Rabson 		*time_rec = 0;
18033f12199SDoug Rabson 	if (delegated_cred_handle)
18133f12199SDoug Rabson 		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
18233f12199SDoug Rabson 	_gss_buffer_zero(output_token);
18333f12199SDoug Rabson 
18433f12199SDoug Rabson 	/*
18533f12199SDoug Rabson 	 * If this is the first call (*context_handle is NULL), we must
18633f12199SDoug Rabson 	 * parse the input token to figure out the mechanism to use.
18733f12199SDoug Rabson 	 */
18833f12199SDoug Rabson 	if (*context_handle == GSS_C_NO_CONTEXT) {
18933f12199SDoug Rabson 		gss_OID_desc mech_oid;
19033f12199SDoug Rabson 
19133f12199SDoug Rabson 		major_status = choose_mech(input_token, &mech_oid);
19233f12199SDoug Rabson 		if (major_status != GSS_S_COMPLETE)
19333f12199SDoug Rabson 			return (major_status);
194c0b9f4feSDoug Rabson 
195c0b9f4feSDoug Rabson 		/*
196c0b9f4feSDoug Rabson 		 * Now that we have a mechanism, we can find the
197c0b9f4feSDoug Rabson 		 * implementation.
198c0b9f4feSDoug Rabson 		 */
199c0b9f4feSDoug Rabson 		ctx = malloc(sizeof(struct _gss_context));
200c0b9f4feSDoug Rabson 		if (!ctx) {
201c0b9f4feSDoug Rabson 			*minor_status = ENOMEM;
202c0b9f4feSDoug Rabson 			return (GSS_S_DEFECTIVE_TOKEN);
203c0b9f4feSDoug Rabson 		}
204c0b9f4feSDoug Rabson 		memset(ctx, 0, sizeof(struct _gss_context));
205c0b9f4feSDoug Rabson 		m = ctx->gc_mech = _gss_find_mech_switch(&mech_oid);
206c0b9f4feSDoug Rabson 		if (!m) {
207c0b9f4feSDoug Rabson 			free(ctx);
208c0b9f4feSDoug Rabson 			return (GSS_S_BAD_MECH);
209c0b9f4feSDoug Rabson 		}
210f2155136SMarcelo Araujo 	} else
211c0b9f4feSDoug Rabson 		m = ctx->gc_mech;
212c0b9f4feSDoug Rabson 
213c0b9f4feSDoug Rabson 	if (cred) {
214c0b9f4feSDoug Rabson 		SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
215c0b9f4feSDoug Rabson 			if (mc->gmc_mech == m)
216c0b9f4feSDoug Rabson 				break;
217c0b9f4feSDoug Rabson 		if (!mc)
218c0b9f4feSDoug Rabson 			return (GSS_S_BAD_MECH);
219c0b9f4feSDoug Rabson 		acceptor_mc = mc->gmc_cred;
220c0b9f4feSDoug Rabson 	} else {
221c0b9f4feSDoug Rabson 		acceptor_mc = GSS_C_NO_CREDENTIAL;
222c0b9f4feSDoug Rabson 	}
223c0b9f4feSDoug Rabson 	delegated_mc = GSS_C_NO_CREDENTIAL;
224c0b9f4feSDoug Rabson 
22533f12199SDoug Rabson 	mech_ret_flags = 0;
226c0b9f4feSDoug Rabson 	major_status = m->gm_accept_sec_context(minor_status,
227c0b9f4feSDoug Rabson 	    &ctx->gc_ctx,
228c0b9f4feSDoug Rabson 	    acceptor_mc,
229c0b9f4feSDoug Rabson 	    input_token,
230c0b9f4feSDoug Rabson 	    input_chan_bindings,
231c0b9f4feSDoug Rabson 	    &src_mn,
232c0b9f4feSDoug Rabson 	    mech_type,
233c0b9f4feSDoug Rabson 	    output_token,
23422a25490SDoug Rabson 	    &mech_ret_flags,
235c0b9f4feSDoug Rabson 	    time_rec,
236c0b9f4feSDoug Rabson 	    &delegated_mc);
237c0b9f4feSDoug Rabson 	if (major_status != GSS_S_COMPLETE &&
23833f12199SDoug Rabson 	    major_status != GSS_S_CONTINUE_NEEDED) {
23933f12199SDoug Rabson 		_gss_mg_error(m, major_status, *minor_status);
240c0b9f4feSDoug Rabson 		return (major_status);
24133f12199SDoug Rabson 	}
242c0b9f4feSDoug Rabson 
24333f12199SDoug Rabson 	if (src_name && src_mn) {
244c0b9f4feSDoug Rabson 		/*
245c0b9f4feSDoug Rabson 		 * Make a new name and mark it as an MN.
246c0b9f4feSDoug Rabson 		 */
247c0b9f4feSDoug Rabson 		struct _gss_name *name = _gss_make_name(m, src_mn);
248c0b9f4feSDoug Rabson 
249c0b9f4feSDoug Rabson 		if (!name) {
250c0b9f4feSDoug Rabson 			m->gm_release_name(minor_status, &src_mn);
251c0b9f4feSDoug Rabson 			return (GSS_S_FAILURE);
252c0b9f4feSDoug Rabson 		}
253c0b9f4feSDoug Rabson 		*src_name = (gss_name_t) name;
25433f12199SDoug Rabson 	} else if (src_mn) {
25533f12199SDoug Rabson 		m->gm_release_name(minor_status, &src_mn);
256c0b9f4feSDoug Rabson 	}
257c0b9f4feSDoug Rabson 
258e1a0d9efSDoug Rabson 	if (delegated_mc == GSS_C_NO_CREDENTIAL)
259e1a0d9efSDoug Rabson 		mech_ret_flags &= ~GSS_C_DELEG_FLAG;
260e1a0d9efSDoug Rabson 
26122a25490SDoug Rabson 	if (mech_ret_flags & GSS_C_DELEG_FLAG) {
262c0b9f4feSDoug Rabson 		if (!delegated_cred_handle) {
263c0b9f4feSDoug Rabson 			m->gm_release_cred(minor_status, &delegated_mc);
264e1a0d9efSDoug Rabson 			mech_ret_flags &= ~GSS_C_DELEG_FLAG;
265c0b9f4feSDoug Rabson 		} else {
26633f12199SDoug Rabson 			struct _gss_cred *dcred;
26733f12199SDoug Rabson 			struct _gss_mechanism_cred *dmc;
268c0b9f4feSDoug Rabson 
26933f12199SDoug Rabson 			dcred = malloc(sizeof(struct _gss_cred));
27033f12199SDoug Rabson 			if (!dcred) {
271c0b9f4feSDoug Rabson 				*minor_status = ENOMEM;
272c0b9f4feSDoug Rabson 				return (GSS_S_FAILURE);
273c0b9f4feSDoug Rabson 			}
27433f12199SDoug Rabson 			SLIST_INIT(&dcred->gc_mc);
27533f12199SDoug Rabson 			dmc = malloc(sizeof(struct _gss_mechanism_cred));
27633f12199SDoug Rabson 			if (!dmc) {
27733f12199SDoug Rabson 				free(dcred);
278c0b9f4feSDoug Rabson 				*minor_status = ENOMEM;
279c0b9f4feSDoug Rabson 				return (GSS_S_FAILURE);
280c0b9f4feSDoug Rabson 			}
28133f12199SDoug Rabson 			dmc->gmc_mech = m;
28233f12199SDoug Rabson 			dmc->gmc_mech_oid = &m->gm_mech_oid;
28333f12199SDoug Rabson 			dmc->gmc_cred = delegated_mc;
28433f12199SDoug Rabson 			SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
285c0b9f4feSDoug Rabson 
28633f12199SDoug Rabson 			*delegated_cred_handle = (gss_cred_id_t) dcred;
287c0b9f4feSDoug Rabson 		}
288c0b9f4feSDoug Rabson 	}
289c0b9f4feSDoug Rabson 
29022a25490SDoug Rabson 	if (ret_flags)
29122a25490SDoug Rabson 		*ret_flags = mech_ret_flags;
292c0b9f4feSDoug Rabson 	*context_handle = (gss_ctx_id_t) ctx;
293c0b9f4feSDoug Rabson 	return (major_status);
294c0b9f4feSDoug Rabson }
295