1153838Sdfr/*-
2153838Sdfr * Copyright (c) 2005 Doug Rabson
3153838Sdfr * All rights reserved.
4153838Sdfr *
5153838Sdfr * Redistribution and use in source and binary forms, with or without
6153838Sdfr * modification, are permitted provided that the following conditions
7153838Sdfr * are met:
8153838Sdfr * 1. Redistributions of source code must retain the above copyright
9153838Sdfr *    notice, this list of conditions and the following disclaimer.
10153838Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11153838Sdfr *    notice, this list of conditions and the following disclaimer in the
12153838Sdfr *    documentation and/or other materials provided with the distribution.
13153838Sdfr *
14153838Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15153838Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16153838Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17153838Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18153838Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19153838Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20153838Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21153838Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22153838Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23153838Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24153838Sdfr * SUCH DAMAGE.
25153838Sdfr *
26153838Sdfr *	$FreeBSD$
27153838Sdfr */
28153838Sdfr
29153838Sdfr#include <gssapi/gssapi.h>
30153838Sdfr#include <stdlib.h>
31168340Skan#include <string.h>
32153838Sdfr#include <errno.h>
33153838Sdfr
34153838Sdfr#include "mech_switch.h"
35153838Sdfr#include "context.h"
36153838Sdfr#include "cred.h"
37153838Sdfr#include "name.h"
38178828Sdfr#include "utils.h"
39153838Sdfr
40178828Sdfrstatic OM_uint32
41178828Sdfrparse_header(const gss_buffer_t input_token, gss_OID mech_oid)
42178828Sdfr{
43178828Sdfr	unsigned char *p = input_token->value;
44178828Sdfr	size_t len = input_token->length;
45178828Sdfr	size_t a, b;
46178828Sdfr
47178828Sdfr	/*
48178828Sdfr	 * Token must start with [APPLICATION 0] SEQUENCE.
49178828Sdfr	 * But if it doesn't assume it is DCE-STYLE Kerberos!
50178828Sdfr	 */
51178828Sdfr	if (len == 0)
52178828Sdfr		return (GSS_S_DEFECTIVE_TOKEN);
53178828Sdfr
54178828Sdfr	p++;
55178828Sdfr	len--;
56178828Sdfr
57178828Sdfr	/*
58178828Sdfr	 * Decode the length and make sure it agrees with the
59178828Sdfr	 * token length.
60178828Sdfr	 */
61178828Sdfr	if (len == 0)
62178828Sdfr		return (GSS_S_DEFECTIVE_TOKEN);
63178828Sdfr	if ((*p & 0x80) == 0) {
64178828Sdfr		a = *p;
65178828Sdfr		p++;
66178828Sdfr		len--;
67178828Sdfr	} else {
68178828Sdfr		b = *p & 0x7f;
69178828Sdfr		p++;
70178828Sdfr		len--;
71178828Sdfr		if (len < b)
72178828Sdfr		    return (GSS_S_DEFECTIVE_TOKEN);
73178828Sdfr		a = 0;
74178828Sdfr		while (b) {
75178828Sdfr		    a = (a << 8) | *p;
76178828Sdfr		    p++;
77178828Sdfr		    len--;
78178828Sdfr		    b--;
79178828Sdfr		}
80178828Sdfr	}
81178828Sdfr	if (a != len)
82178828Sdfr		return (GSS_S_DEFECTIVE_TOKEN);
83178828Sdfr
84178828Sdfr	/*
85178828Sdfr	 * Decode the OID for the mechanism. Simplify life by
86178828Sdfr	 * assuming that the OID length is less than 128 bytes.
87178828Sdfr	 */
88178828Sdfr	if (len < 2 || *p != 0x06)
89178828Sdfr		return (GSS_S_DEFECTIVE_TOKEN);
90178828Sdfr	if ((p[1] & 0x80) || p[1] > (len - 2))
91178828Sdfr		return (GSS_S_DEFECTIVE_TOKEN);
92178828Sdfr	mech_oid->length = p[1];
93178828Sdfr	p += 2;
94178828Sdfr	len -= 2;
95178828Sdfr	mech_oid->elements = p;
96178828Sdfr
97178828Sdfr	return (GSS_S_COMPLETE);
98178828Sdfr}
99178828Sdfr
100178828Sdfrstatic gss_OID_desc krb5_mechanism =
101178828Sdfr{9, (void *)(uintptr_t) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
102178828Sdfrstatic gss_OID_desc ntlm_mechanism =
103178828Sdfr{10, (void *)(uintptr_t) "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
104178828Sdfrstatic gss_OID_desc spnego_mechanism =
105178828Sdfr{6, (void *)(uintptr_t) "\x2b\x06\x01\x05\x05\x02"};
106178828Sdfr
107178828Sdfrstatic OM_uint32
108178828Sdfrchoose_mech(const gss_buffer_t input, gss_OID mech_oid)
109178828Sdfr{
110178828Sdfr	OM_uint32 status;
111178828Sdfr
112178828Sdfr	/*
113178828Sdfr	 * First try to parse the gssapi token header and see if it's a
114178828Sdfr	 * correct header, use that in the first hand.
115178828Sdfr	 */
116178828Sdfr
117178828Sdfr	status = parse_header(input, mech_oid);
118178828Sdfr	if (status == GSS_S_COMPLETE)
119178828Sdfr		return (GSS_S_COMPLETE);
120178828Sdfr
121178828Sdfr	/*
122178828Sdfr	 * Lets guess what mech is really is, callback function to mech ??
123178828Sdfr	 */
124178828Sdfr
125178828Sdfr	if (input->length > 8 &&
126178828Sdfr	    memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
127178828Sdfr	{
128178828Sdfr		*mech_oid = ntlm_mechanism;
129178828Sdfr		return (GSS_S_COMPLETE);
130178828Sdfr	} else if (input->length != 0 &&
131178828Sdfr	    ((const char *)input->value)[0] == 0x6E)
132178828Sdfr	{
133178828Sdfr		/* Could be a raw AP-REQ (check for APPLICATION tag) */
134178828Sdfr		*mech_oid = krb5_mechanism;
135178828Sdfr		return (GSS_S_COMPLETE);
136178828Sdfr	} else if (input->length == 0) {
137178828Sdfr		/*
138178828Sdfr		 * There is the a wierd mode of SPNEGO (in CIFS and
139178828Sdfr		 * SASL GSS-SPENGO where the first token is zero
140178828Sdfr		 * length and the acceptor returns a mech_list, lets
141178828Sdfr		 * hope that is what is happening now.
142178828Sdfr		 */
143178828Sdfr		*mech_oid = spnego_mechanism;
144178828Sdfr		return (GSS_S_COMPLETE);
145178828Sdfr	}
146178828Sdfr	return (status);
147178828Sdfr}
148178828Sdfr
149153838SdfrOM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
150153838Sdfr    gss_ctx_id_t *context_handle,
151153838Sdfr    const gss_cred_id_t acceptor_cred_handle,
152153838Sdfr    const gss_buffer_t input_token,
153153838Sdfr    const gss_channel_bindings_t input_chan_bindings,
154153838Sdfr    gss_name_t *src_name,
155153838Sdfr    gss_OID *mech_type,
156153838Sdfr    gss_buffer_t output_token,
157153838Sdfr    OM_uint32 *ret_flags,
158153838Sdfr    OM_uint32 *time_rec,
159153838Sdfr    gss_cred_id_t *delegated_cred_handle)
160153838Sdfr{
161171112Sdfr	OM_uint32 major_status, mech_ret_flags;
162153838Sdfr	struct _gss_mech_switch *m;
163153838Sdfr	struct _gss_context *ctx = (struct _gss_context *) *context_handle;
164153838Sdfr	struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
165153838Sdfr	struct _gss_mechanism_cred *mc;
166153838Sdfr	gss_cred_id_t acceptor_mc, delegated_mc;
167153838Sdfr	gss_name_t src_mn;
168153838Sdfr	int allocated_ctx;
169153838Sdfr
170153838Sdfr	*minor_status = 0;
171178828Sdfr	if (src_name)
172178828Sdfr		*src_name = GSS_C_NO_NAME;
173178828Sdfr	if (mech_type)
174178828Sdfr		*mech_type = GSS_C_NO_OID;
175178828Sdfr	if (ret_flags)
176178828Sdfr		*ret_flags = 0;
177178828Sdfr	if (time_rec)
178178828Sdfr		*time_rec = 0;
179178828Sdfr	if (delegated_cred_handle)
180178828Sdfr		*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
181178828Sdfr	_gss_buffer_zero(output_token);
182153838Sdfr
183153838Sdfr	/*
184153838Sdfr	 * If this is the first call (*context_handle is NULL), we must
185153838Sdfr	 * parse the input token to figure out the mechanism to use.
186153838Sdfr	 */
187153838Sdfr	if (*context_handle == GSS_C_NO_CONTEXT) {
188153838Sdfr		gss_OID_desc mech_oid;
189153838Sdfr
190178828Sdfr		major_status = choose_mech(input_token, &mech_oid);
191178828Sdfr		if (major_status != GSS_S_COMPLETE)
192178828Sdfr			return (major_status);
193153838Sdfr
194153838Sdfr		/*
195153838Sdfr		 * Now that we have a mechanism, we can find the
196153838Sdfr		 * implementation.
197153838Sdfr		 */
198153838Sdfr		ctx = malloc(sizeof(struct _gss_context));
199153838Sdfr		if (!ctx) {
200153838Sdfr			*minor_status = ENOMEM;
201153838Sdfr			return (GSS_S_DEFECTIVE_TOKEN);
202153838Sdfr		}
203153838Sdfr		memset(ctx, 0, sizeof(struct _gss_context));
204153838Sdfr		m = ctx->gc_mech = _gss_find_mech_switch(&mech_oid);
205153838Sdfr		if (!m) {
206153838Sdfr			free(ctx);
207153838Sdfr			return (GSS_S_BAD_MECH);
208153838Sdfr		}
209153838Sdfr		allocated_ctx = 1;
210153838Sdfr	} else {
211153838Sdfr		m = ctx->gc_mech;
212153838Sdfr		allocated_ctx = 0;
213153838Sdfr	}
214153838Sdfr
215153838Sdfr	if (cred) {
216153838Sdfr		SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
217153838Sdfr			if (mc->gmc_mech == m)
218153838Sdfr				break;
219153838Sdfr		if (!mc)
220153838Sdfr			return (GSS_S_BAD_MECH);
221153838Sdfr		acceptor_mc = mc->gmc_cred;
222153838Sdfr	} else {
223153838Sdfr		acceptor_mc = GSS_C_NO_CREDENTIAL;
224153838Sdfr	}
225153838Sdfr	delegated_mc = GSS_C_NO_CREDENTIAL;
226153838Sdfr
227178828Sdfr	mech_ret_flags = 0;
228153838Sdfr	major_status = m->gm_accept_sec_context(minor_status,
229153838Sdfr	    &ctx->gc_ctx,
230153838Sdfr	    acceptor_mc,
231153838Sdfr	    input_token,
232153838Sdfr	    input_chan_bindings,
233153838Sdfr	    &src_mn,
234153838Sdfr	    mech_type,
235153838Sdfr	    output_token,
236171112Sdfr	    &mech_ret_flags,
237153838Sdfr	    time_rec,
238153838Sdfr	    &delegated_mc);
239153838Sdfr	if (major_status != GSS_S_COMPLETE &&
240178828Sdfr	    major_status != GSS_S_CONTINUE_NEEDED) {
241178828Sdfr		_gss_mg_error(m, major_status, *minor_status);
242153838Sdfr		return (major_status);
243178828Sdfr	}
244153838Sdfr
245178828Sdfr	if (src_name && src_mn) {
246153838Sdfr		/*
247153838Sdfr		 * Make a new name and mark it as an MN.
248153838Sdfr		 */
249153838Sdfr		struct _gss_name *name = _gss_make_name(m, src_mn);
250153838Sdfr
251153838Sdfr		if (!name) {
252153838Sdfr			m->gm_release_name(minor_status, &src_mn);
253153838Sdfr			return (GSS_S_FAILURE);
254153838Sdfr		}
255153838Sdfr		*src_name = (gss_name_t) name;
256178828Sdfr	} else if (src_mn) {
257178828Sdfr		m->gm_release_name(minor_status, &src_mn);
258153838Sdfr	}
259153838Sdfr
260178692Sdfr	if (delegated_mc == GSS_C_NO_CREDENTIAL)
261178692Sdfr		mech_ret_flags &= ~GSS_C_DELEG_FLAG;
262178692Sdfr
263171112Sdfr	if (mech_ret_flags & GSS_C_DELEG_FLAG) {
264153838Sdfr		if (!delegated_cred_handle) {
265153838Sdfr			m->gm_release_cred(minor_status, &delegated_mc);
266178692Sdfr			mech_ret_flags &= ~GSS_C_DELEG_FLAG;
267153838Sdfr		} else {
268178828Sdfr			struct _gss_cred *dcred;
269178828Sdfr			struct _gss_mechanism_cred *dmc;
270153838Sdfr
271178828Sdfr			dcred = malloc(sizeof(struct _gss_cred));
272178828Sdfr			if (!dcred) {
273153838Sdfr				*minor_status = ENOMEM;
274153838Sdfr				return (GSS_S_FAILURE);
275153838Sdfr			}
276178828Sdfr			SLIST_INIT(&dcred->gc_mc);
277178828Sdfr			dmc = malloc(sizeof(struct _gss_mechanism_cred));
278178828Sdfr			if (!dmc) {
279178828Sdfr				free(dcred);
280153838Sdfr				*minor_status = ENOMEM;
281153838Sdfr				return (GSS_S_FAILURE);
282153838Sdfr			}
283178828Sdfr			dmc->gmc_mech = m;
284178828Sdfr			dmc->gmc_mech_oid = &m->gm_mech_oid;
285178828Sdfr			dmc->gmc_cred = delegated_mc;
286178828Sdfr			SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
287153838Sdfr
288178828Sdfr			*delegated_cred_handle = (gss_cred_id_t) dcred;
289153838Sdfr		}
290153838Sdfr	}
291153838Sdfr
292171112Sdfr	if (ret_flags)
293171112Sdfr		*ret_flags = mech_ret_flags;
294153838Sdfr	*context_handle = (gss_ctx_id_t) ctx;
295153838Sdfr	return (major_status);
296153838Sdfr}
297