1226031Sstas/*-
2226031Sstas * Copyright (c) 2005 Doug Rabson
3226031Sstas * All rights reserved.
4226031Sstas *
5226031Sstas * Redistribution and use in source and binary forms, with or without
6226031Sstas * modification, are permitted provided that the following conditions
7226031Sstas * are met:
8226031Sstas * 1. Redistributions of source code must retain the above copyright
9226031Sstas *    notice, this list of conditions and the following disclaimer.
10226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer in the
12226031Sstas *    documentation and/or other materials provided with the distribution.
13226031Sstas *
14226031Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24226031Sstas * SUCH DAMAGE.
25226031Sstas *
26226031Sstas *	$FreeBSD: src/lib/libgssapi/gss_accept_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27226031Sstas */
28226031Sstas
29226031Sstas#include "mech_locl.h"
30226031Sstas
31226031Sstasstatic OM_uint32
32226031Sstasparse_header(const gss_buffer_t input_token, gss_OID mech_oid)
33226031Sstas{
34226031Sstas	unsigned char *p = input_token->value;
35226031Sstas	size_t len = input_token->length;
36226031Sstas	size_t a, b;
37226031Sstas
38226031Sstas	/*
39226031Sstas	 * Token must start with [APPLICATION 0] SEQUENCE.
40226031Sstas	 * But if it doesn't assume it is DCE-STYLE Kerberos!
41226031Sstas	 */
42226031Sstas	if (len == 0)
43226031Sstas		return (GSS_S_DEFECTIVE_TOKEN);
44226031Sstas
45226031Sstas	p++;
46226031Sstas	len--;
47226031Sstas
48226031Sstas	/*
49226031Sstas	 * Decode the length and make sure it agrees with the
50226031Sstas	 * token length.
51226031Sstas	 */
52226031Sstas	if (len == 0)
53226031Sstas		return (GSS_S_DEFECTIVE_TOKEN);
54226031Sstas	if ((*p & 0x80) == 0) {
55226031Sstas		a = *p;
56226031Sstas		p++;
57226031Sstas		len--;
58226031Sstas	} else {
59226031Sstas		b = *p & 0x7f;
60226031Sstas		p++;
61226031Sstas		len--;
62226031Sstas		if (len < b)
63226031Sstas		    return (GSS_S_DEFECTIVE_TOKEN);
64226031Sstas		a = 0;
65226031Sstas		while (b) {
66226031Sstas		    a = (a << 8) | *p;
67226031Sstas		    p++;
68226031Sstas		    len--;
69226031Sstas		    b--;
70226031Sstas		}
71226031Sstas	}
72226031Sstas	if (a != len)
73226031Sstas		return (GSS_S_DEFECTIVE_TOKEN);
74226031Sstas
75226031Sstas	/*
76226031Sstas	 * Decode the OID for the mechanism. Simplify life by
77226031Sstas	 * assuming that the OID length is less than 128 bytes.
78226031Sstas	 */
79226031Sstas	if (len < 2 || *p != 0x06)
80226031Sstas		return (GSS_S_DEFECTIVE_TOKEN);
81226031Sstas	if ((p[1] & 0x80) || p[1] > (len - 2))
82226031Sstas		return (GSS_S_DEFECTIVE_TOKEN);
83226031Sstas	mech_oid->length = p[1];
84226031Sstas	p += 2;
85226031Sstas	len -= 2;
86226031Sstas	mech_oid->elements = p;
87226031Sstas
88226031Sstas	return GSS_S_COMPLETE;
89226031Sstas}
90226031Sstas
91226031Sstasstatic gss_OID_desc krb5_mechanism =
92226031Sstas    {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
93226031Sstasstatic gss_OID_desc ntlm_mechanism =
94226031Sstas    {10, rk_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")};
95226031Sstasstatic gss_OID_desc spnego_mechanism =
96226031Sstas    {6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02")};
97226031Sstas
98226031Sstasstatic OM_uint32
99226031Sstaschoose_mech(const gss_buffer_t input, gss_OID mech_oid)
100226031Sstas{
101226031Sstas	OM_uint32 status;
102226031Sstas
103226031Sstas	/*
104226031Sstas	 * First try to parse the gssapi token header and see if it's a
105226031Sstas	 * correct header, use that in the first hand.
106226031Sstas	 */
107226031Sstas
108226031Sstas	status = parse_header(input, mech_oid);
109226031Sstas	if (status == GSS_S_COMPLETE)
110226031Sstas	    return GSS_S_COMPLETE;
111226031Sstas
112226031Sstas	/*
113226031Sstas	 * Lets guess what mech is really is, callback function to mech ??
114226031Sstas	 */
115226031Sstas
116226031Sstas	if (input->length > 8 &&
117226031Sstas	    memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
118226031Sstas	{
119226031Sstas		*mech_oid = ntlm_mechanism;
120226031Sstas		return GSS_S_COMPLETE;
121226031Sstas	} else if (input->length != 0 &&
122226031Sstas		   ((const char *)input->value)[0] == 0x6E)
123226031Sstas	{
124226031Sstas		/* Could be a raw AP-REQ (check for APPLICATION tag) */
125226031Sstas		*mech_oid = krb5_mechanism;
126226031Sstas		return GSS_S_COMPLETE;
127226031Sstas	} else if (input->length == 0) {
128226031Sstas		/*
129226031Sstas		 * There is the a wierd mode of SPNEGO (in CIFS and
130226031Sstas		 * SASL GSS-SPENGO where the first token is zero
131226031Sstas		 * length and the acceptor returns a mech_list, lets
132226031Sstas		 * hope that is what is happening now.
133226031Sstas		 *
134226031Sstas		 * http://msdn.microsoft.com/en-us/library/cc213114.aspx
135226031Sstas		 * "NegTokenInit2 Variation for Server-Initiation"
136226031Sstas		 */
137226031Sstas		*mech_oid = spnego_mechanism;
138226031Sstas		return GSS_S_COMPLETE;
139226031Sstas	}
140226031Sstas	return status;
141226031Sstas}
142226031Sstas
143226031Sstas
144226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
145226031Sstasgss_accept_sec_context(OM_uint32 *minor_status,
146226031Sstas    gss_ctx_id_t *context_handle,
147226031Sstas    const gss_cred_id_t acceptor_cred_handle,
148226031Sstas    const gss_buffer_t input_token,
149226031Sstas    const gss_channel_bindings_t input_chan_bindings,
150226031Sstas    gss_name_t *src_name,
151226031Sstas    gss_OID *mech_type,
152226031Sstas    gss_buffer_t output_token,
153226031Sstas    OM_uint32 *ret_flags,
154226031Sstas    OM_uint32 *time_rec,
155226031Sstas    gss_cred_id_t *delegated_cred_handle)
156226031Sstas{
157226031Sstas	OM_uint32 major_status, mech_ret_flags, junk;
158226031Sstas	gssapi_mech_interface m;
159226031Sstas	struct _gss_context *ctx = (struct _gss_context *) *context_handle;
160226031Sstas	struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
161226031Sstas	struct _gss_mechanism_cred *mc;
162226031Sstas	gss_cred_id_t acceptor_mc, delegated_mc;
163226031Sstas	gss_name_t src_mn;
164226031Sstas	gss_OID mech_ret_type = NULL;
165226031Sstas
166226031Sstas	*minor_status = 0;
167226031Sstas	if (src_name)
168226031Sstas	    *src_name = GSS_C_NO_NAME;
169226031Sstas	if (mech_type)
170226031Sstas	    *mech_type = GSS_C_NO_OID;
171226031Sstas	if (ret_flags)
172226031Sstas	    *ret_flags = 0;
173226031Sstas	if (time_rec)
174226031Sstas	    *time_rec = 0;
175226031Sstas	if (delegated_cred_handle)
176226031Sstas	    *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
177226031Sstas	_mg_buffer_zero(output_token);
178226031Sstas
179226031Sstas
180226031Sstas	/*
181226031Sstas	 * If this is the first call (*context_handle is NULL), we must
182226031Sstas	 * parse the input token to figure out the mechanism to use.
183226031Sstas	 */
184226031Sstas	if (*context_handle == GSS_C_NO_CONTEXT) {
185226031Sstas		gss_OID_desc mech_oid;
186226031Sstas
187226031Sstas		major_status = choose_mech(input_token, &mech_oid);
188226031Sstas		if (major_status != GSS_S_COMPLETE)
189226031Sstas			return major_status;
190226031Sstas
191226031Sstas		/*
192226031Sstas		 * Now that we have a mechanism, we can find the
193226031Sstas		 * implementation.
194226031Sstas		 */
195226031Sstas		ctx = malloc(sizeof(struct _gss_context));
196226031Sstas		if (!ctx) {
197226031Sstas			*minor_status = ENOMEM;
198226031Sstas			return (GSS_S_DEFECTIVE_TOKEN);
199226031Sstas		}
200226031Sstas		memset(ctx, 0, sizeof(struct _gss_context));
201226031Sstas		m = ctx->gc_mech = __gss_get_mechanism(&mech_oid);
202226031Sstas		if (!m) {
203226031Sstas			free(ctx);
204226031Sstas			return (GSS_S_BAD_MECH);
205226031Sstas		}
206226031Sstas		*context_handle = (gss_ctx_id_t) ctx;
207226031Sstas	} else {
208226031Sstas		m = ctx->gc_mech;
209226031Sstas	}
210226031Sstas
211226031Sstas	if (cred) {
212226031Sstas		HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
213226031Sstas			if (mc->gmc_mech == m)
214226031Sstas				break;
215226031Sstas		if (!mc) {
216226031Sstas		        gss_delete_sec_context(&junk, context_handle, NULL);
217226031Sstas			return (GSS_S_BAD_MECH);
218226031Sstas		}
219226031Sstas		acceptor_mc = mc->gmc_cred;
220226031Sstas	} else {
221226031Sstas		acceptor_mc = GSS_C_NO_CREDENTIAL;
222226031Sstas	}
223226031Sstas	delegated_mc = GSS_C_NO_CREDENTIAL;
224226031Sstas
225226031Sstas	mech_ret_flags = 0;
226226031Sstas	major_status = m->gm_accept_sec_context(minor_status,
227226031Sstas	    &ctx->gc_ctx,
228226031Sstas	    acceptor_mc,
229226031Sstas	    input_token,
230226031Sstas	    input_chan_bindings,
231226031Sstas	    &src_mn,
232226031Sstas	    &mech_ret_type,
233226031Sstas	    output_token,
234226031Sstas	    &mech_ret_flags,
235226031Sstas	    time_rec,
236226031Sstas	    &delegated_mc);
237226031Sstas	if (major_status != GSS_S_COMPLETE &&
238226031Sstas	    major_status != GSS_S_CONTINUE_NEEDED)
239226031Sstas	{
240226031Sstas		_gss_mg_error(m, major_status, *minor_status);
241226031Sstas		gss_delete_sec_context(&junk, context_handle, NULL);
242226031Sstas		return (major_status);
243226031Sstas	}
244226031Sstas
245226031Sstas	if (mech_type)
246226031Sstas	    *mech_type = mech_ret_type;
247226031Sstas
248226031Sstas	if (src_name && src_mn) {
249226031Sstas		/*
250226031Sstas		 * Make a new name and mark it as an MN.
251226031Sstas		 */
252226031Sstas		struct _gss_name *name = _gss_make_name(m, src_mn);
253226031Sstas
254226031Sstas		if (!name) {
255226031Sstas			m->gm_release_name(minor_status, &src_mn);
256226031Sstas		        gss_delete_sec_context(&junk, context_handle, NULL);
257226031Sstas			return (GSS_S_FAILURE);
258226031Sstas		}
259226031Sstas		*src_name = (gss_name_t) name;
260226031Sstas	} else if (src_mn) {
261226031Sstas		m->gm_release_name(minor_status, &src_mn);
262226031Sstas	}
263226031Sstas
264226031Sstas	if (mech_ret_flags & GSS_C_DELEG_FLAG) {
265226031Sstas		if (!delegated_cred_handle) {
266226031Sstas			m->gm_release_cred(minor_status, &delegated_mc);
267226031Sstas			mech_ret_flags &=
268226031Sstas			    ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
269226031Sstas		} else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
270226031Sstas			/*
271226031Sstas			 * If the returned mech_type is not the same
272226031Sstas			 * as the mech, assume its pseudo mech type
273226031Sstas			 * and the returned type is already a
274226031Sstas			 * mech-glue object
275226031Sstas			 */
276226031Sstas			*delegated_cred_handle = delegated_mc;
277226031Sstas
278226031Sstas		} else if (delegated_mc) {
279226031Sstas			struct _gss_cred *dcred;
280226031Sstas			struct _gss_mechanism_cred *dmc;
281226031Sstas
282226031Sstas			dcred = malloc(sizeof(struct _gss_cred));
283226031Sstas			if (!dcred) {
284226031Sstas				*minor_status = ENOMEM;
285226031Sstas				gss_delete_sec_context(&junk, context_handle, NULL);
286226031Sstas				return (GSS_S_FAILURE);
287226031Sstas			}
288226031Sstas			HEIM_SLIST_INIT(&dcred->gc_mc);
289226031Sstas			dmc = malloc(sizeof(struct _gss_mechanism_cred));
290226031Sstas			if (!dmc) {
291226031Sstas				free(dcred);
292226031Sstas				*minor_status = ENOMEM;
293226031Sstas				gss_delete_sec_context(&junk, context_handle, NULL);
294226031Sstas				return (GSS_S_FAILURE);
295226031Sstas			}
296226031Sstas			dmc->gmc_mech = m;
297226031Sstas			dmc->gmc_mech_oid = &m->gm_mech_oid;
298226031Sstas			dmc->gmc_cred = delegated_mc;
299226031Sstas			HEIM_SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
300226031Sstas
301226031Sstas			*delegated_cred_handle = (gss_cred_id_t) dcred;
302226031Sstas		}
303226031Sstas	}
304226031Sstas
305226031Sstas	if (ret_flags)
306226031Sstas	    *ret_flags = mech_ret_flags;
307226031Sstas	return (major_status);
308226031Sstas}
309