1178825Sdfr/*
2233294Sstas * Copyright (c) 1997 - 2006 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34233294Sstas#include "spnego_locl.h"
35178825Sdfr
36178825Sdfrstatic OM_uint32
37178825Sdfrsend_reject (OM_uint32 *minor_status,
38178825Sdfr	     gss_buffer_t output_token)
39178825Sdfr{
40178825Sdfr    NegotiationToken nt;
41178825Sdfr    size_t size;
42178825Sdfr
43178825Sdfr    nt.element = choice_NegotiationToken_negTokenResp;
44178825Sdfr
45178825Sdfr    ALLOC(nt.u.negTokenResp.negResult, 1);
46178825Sdfr    if (nt.u.negTokenResp.negResult == NULL) {
47178825Sdfr	*minor_status = ENOMEM;
48178825Sdfr	return GSS_S_FAILURE;
49178825Sdfr    }
50178825Sdfr    *(nt.u.negTokenResp.negResult)  = reject;
51178825Sdfr    nt.u.negTokenResp.supportedMech = NULL;
52178825Sdfr    nt.u.negTokenResp.responseToken = NULL;
53178825Sdfr    nt.u.negTokenResp.mechListMIC   = NULL;
54233294Sstas
55178825Sdfr    ASN1_MALLOC_ENCODE(NegotiationToken,
56178825Sdfr		       output_token->value, output_token->length, &nt,
57178825Sdfr		       &size, *minor_status);
58178825Sdfr    free_NegotiationToken(&nt);
59178825Sdfr    if (*minor_status != 0)
60178825Sdfr	return GSS_S_FAILURE;
61178825Sdfr
62178825Sdfr    return GSS_S_BAD_MECH;
63178825Sdfr}
64178825Sdfr
65178825Sdfrstatic OM_uint32
66178825Sdfracceptor_approved(gss_name_t target_name, gss_OID mech)
67178825Sdfr{
68178825Sdfr    gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
69178825Sdfr    gss_OID_set oidset;
70178825Sdfr    OM_uint32 junk, ret;
71178825Sdfr
72178825Sdfr    if (target_name == GSS_C_NO_NAME)
73178825Sdfr	return GSS_S_COMPLETE;
74178825Sdfr
75178825Sdfr    gss_create_empty_oid_set(&junk, &oidset);
76178825Sdfr    gss_add_oid_set_member(&junk, mech, &oidset);
77233294Sstas
78178825Sdfr    ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
79178825Sdfr			   GSS_C_ACCEPT, &cred, NULL, NULL);
80178825Sdfr    gss_release_oid_set(&junk, &oidset);
81178825Sdfr    if (ret != GSS_S_COMPLETE)
82178825Sdfr	return ret;
83178825Sdfr    gss_release_cred(&junk, &cred);
84233294Sstas
85178825Sdfr    return GSS_S_COMPLETE;
86178825Sdfr}
87178825Sdfr
88178825Sdfrstatic OM_uint32
89178825Sdfrsend_supported_mechs (OM_uint32 *minor_status,
90178825Sdfr		      gss_buffer_t output_token)
91178825Sdfr{
92178825Sdfr    NegotiationTokenWin nt;
93233294Sstas    size_t buf_len = 0;
94178825Sdfr    gss_buffer_desc data;
95178825Sdfr    OM_uint32 ret;
96178825Sdfr
97178825Sdfr    memset(&nt, 0, sizeof(nt));
98178825Sdfr
99178825Sdfr    nt.element = choice_NegotiationTokenWin_negTokenInit;
100178825Sdfr    nt.u.negTokenInit.reqFlags = NULL;
101178825Sdfr    nt.u.negTokenInit.mechToken = NULL;
102178825Sdfr    nt.u.negTokenInit.negHints = NULL;
103178825Sdfr
104178825Sdfr    ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
105178825Sdfr					    acceptor_approved, 1, NULL,
106178825Sdfr					    &nt.u.negTokenInit.mechTypes, NULL);
107178825Sdfr    if (ret != GSS_S_COMPLETE) {
108178825Sdfr	return ret;
109178825Sdfr    }
110178825Sdfr
111178825Sdfr    ALLOC(nt.u.negTokenInit.negHints, 1);
112178825Sdfr    if (nt.u.negTokenInit.negHints == NULL) {
113178825Sdfr	*minor_status = ENOMEM;
114178825Sdfr	free_NegotiationTokenWin(&nt);
115178825Sdfr	return GSS_S_FAILURE;
116178825Sdfr    }
117178825Sdfr
118178825Sdfr    ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
119178825Sdfr    if (nt.u.negTokenInit.negHints->hintName == NULL) {
120178825Sdfr	*minor_status = ENOMEM;
121178825Sdfr	free_NegotiationTokenWin(&nt);
122178825Sdfr	return GSS_S_FAILURE;
123178825Sdfr    }
124178825Sdfr
125233294Sstas    *nt.u.negTokenInit.negHints->hintName = strdup("not_defined_in_RFC4178@please_ignore");
126178825Sdfr    nt.u.negTokenInit.negHints->hintAddress = NULL;
127178825Sdfr
128233294Sstas    ASN1_MALLOC_ENCODE(NegotiationTokenWin,
129178825Sdfr		       data.value, data.length, &nt, &buf_len, ret);
130178825Sdfr    free_NegotiationTokenWin(&nt);
131178825Sdfr    if (ret) {
132233294Sstas	*minor_status = ret;
133233294Sstas	return GSS_S_FAILURE;
134178825Sdfr    }
135233294Sstas    if (data.length != buf_len) {
136178825Sdfr	abort();
137233294Sstas        UNREACHABLE(return GSS_S_FAILURE);
138233294Sstas    }
139178825Sdfr
140178825Sdfr    ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
141178825Sdfr
142178825Sdfr    free (data.value);
143178825Sdfr
144178825Sdfr    if (ret != GSS_S_COMPLETE)
145178825Sdfr	return ret;
146178825Sdfr
147178825Sdfr    *minor_status = 0;
148178825Sdfr
149178825Sdfr    return GSS_S_CONTINUE_NEEDED;
150178825Sdfr}
151178825Sdfr
152178825Sdfrstatic OM_uint32
153178825Sdfrsend_accept (OM_uint32 *minor_status,
154178825Sdfr	     gssspnego_ctx context_handle,
155178825Sdfr	     gss_buffer_t mech_token,
156178825Sdfr	     int initial_response,
157178825Sdfr	     gss_buffer_t mech_buf,
158178825Sdfr	     gss_buffer_t output_token)
159178825Sdfr{
160178825Sdfr    NegotiationToken nt;
161178825Sdfr    OM_uint32 ret;
162178825Sdfr    gss_buffer_desc mech_mic_buf;
163178825Sdfr    size_t size;
164178825Sdfr
165178825Sdfr    memset(&nt, 0, sizeof(nt));
166178825Sdfr
167178825Sdfr    nt.element = choice_NegotiationToken_negTokenResp;
168178825Sdfr
169178825Sdfr    ALLOC(nt.u.negTokenResp.negResult, 1);
170178825Sdfr    if (nt.u.negTokenResp.negResult == NULL) {
171178825Sdfr	*minor_status = ENOMEM;
172178825Sdfr	return GSS_S_FAILURE;
173178825Sdfr    }
174178825Sdfr
175178825Sdfr    if (context_handle->open) {
176178825Sdfr	if (mech_token != GSS_C_NO_BUFFER
177178825Sdfr	    && mech_token->length != 0
178178825Sdfr	    && mech_buf != GSS_C_NO_BUFFER)
179178825Sdfr	    *(nt.u.negTokenResp.negResult)  = accept_incomplete;
180178825Sdfr	else
181178825Sdfr	    *(nt.u.negTokenResp.negResult)  = accept_completed;
182178825Sdfr    } else {
183178825Sdfr	if (initial_response && context_handle->require_mic)
184178825Sdfr	    *(nt.u.negTokenResp.negResult)  = request_mic;
185178825Sdfr	else
186178825Sdfr	    *(nt.u.negTokenResp.negResult)  = accept_incomplete;
187178825Sdfr    }
188178825Sdfr
189178825Sdfr    if (initial_response) {
190178825Sdfr	ALLOC(nt.u.negTokenResp.supportedMech, 1);
191178825Sdfr	if (nt.u.negTokenResp.supportedMech == NULL) {
192178825Sdfr	    free_NegotiationToken(&nt);
193178825Sdfr	    *minor_status = ENOMEM;
194178825Sdfr	    return GSS_S_FAILURE;
195178825Sdfr	}
196178825Sdfr
197178825Sdfr	ret = der_get_oid(context_handle->preferred_mech_type->elements,
198178825Sdfr			  context_handle->preferred_mech_type->length,
199178825Sdfr			  nt.u.negTokenResp.supportedMech,
200178825Sdfr			  NULL);
201178825Sdfr	if (ret) {
202178825Sdfr	    free_NegotiationToken(&nt);
203178825Sdfr	    *minor_status = ENOMEM;
204178825Sdfr	    return GSS_S_FAILURE;
205178825Sdfr	}
206178825Sdfr    } else {
207178825Sdfr	nt.u.negTokenResp.supportedMech = NULL;
208178825Sdfr    }
209178825Sdfr
210178825Sdfr    if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
211178825Sdfr	ALLOC(nt.u.negTokenResp.responseToken, 1);
212178825Sdfr	if (nt.u.negTokenResp.responseToken == NULL) {
213178825Sdfr	    free_NegotiationToken(&nt);
214178825Sdfr	    *minor_status = ENOMEM;
215178825Sdfr	    return GSS_S_FAILURE;
216178825Sdfr	}
217178825Sdfr	nt.u.negTokenResp.responseToken->length = mech_token->length;
218178825Sdfr	nt.u.negTokenResp.responseToken->data   = mech_token->value;
219178825Sdfr	mech_token->length = 0;
220178825Sdfr	mech_token->value  = NULL;
221178825Sdfr    } else {
222178825Sdfr	nt.u.negTokenResp.responseToken = NULL;
223178825Sdfr    }
224178825Sdfr
225178825Sdfr    if (mech_buf != GSS_C_NO_BUFFER) {
226178825Sdfr	ret = gss_get_mic(minor_status,
227178825Sdfr			  context_handle->negotiated_ctx_id,
228178825Sdfr			  0,
229178825Sdfr			  mech_buf,
230178825Sdfr			  &mech_mic_buf);
231178825Sdfr	if (ret == GSS_S_COMPLETE) {
232178825Sdfr	    ALLOC(nt.u.negTokenResp.mechListMIC, 1);
233178825Sdfr	    if (nt.u.negTokenResp.mechListMIC == NULL) {
234178825Sdfr		gss_release_buffer(minor_status, &mech_mic_buf);
235178825Sdfr		free_NegotiationToken(&nt);
236178825Sdfr		*minor_status = ENOMEM;
237178825Sdfr		return GSS_S_FAILURE;
238178825Sdfr	    }
239178825Sdfr	    nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
240178825Sdfr	    nt.u.negTokenResp.mechListMIC->data   = mech_mic_buf.value;
241178825Sdfr	} else if (ret == GSS_S_UNAVAILABLE) {
242178825Sdfr	    nt.u.negTokenResp.mechListMIC = NULL;
243178825Sdfr	} else {
244178825Sdfr	    free_NegotiationToken(&nt);
245178825Sdfr	    return ret;
246178825Sdfr	}
247178825Sdfr
248178825Sdfr    } else
249178825Sdfr	nt.u.negTokenResp.mechListMIC = NULL;
250233294Sstas
251178825Sdfr    ASN1_MALLOC_ENCODE(NegotiationToken,
252178825Sdfr		       output_token->value, output_token->length,
253178825Sdfr		       &nt, &size, ret);
254178825Sdfr    if (ret) {
255178825Sdfr	free_NegotiationToken(&nt);
256178825Sdfr	*minor_status = ret;
257178825Sdfr	return GSS_S_FAILURE;
258178825Sdfr    }
259178825Sdfr
260178825Sdfr    /*
261178825Sdfr     * The response should not be encapsulated, because
262178825Sdfr     * it is a SubsequentContextToken (note though RFC 1964
263178825Sdfr     * specifies encapsulation for all _Kerberos_ tokens).
264178825Sdfr     */
265178825Sdfr
266178825Sdfr    if (*(nt.u.negTokenResp.negResult) == accept_completed)
267178825Sdfr	ret = GSS_S_COMPLETE;
268178825Sdfr    else
269178825Sdfr	ret = GSS_S_CONTINUE_NEEDED;
270178825Sdfr    free_NegotiationToken(&nt);
271178825Sdfr    return ret;
272178825Sdfr}
273178825Sdfr
274178825Sdfr
275178825Sdfrstatic OM_uint32
276178825Sdfrverify_mechlist_mic
277178825Sdfr	   (OM_uint32 *minor_status,
278178825Sdfr	    gssspnego_ctx context_handle,
279178825Sdfr	    gss_buffer_t mech_buf,
280178825Sdfr	    heim_octet_string *mechListMIC
281178825Sdfr	   )
282178825Sdfr{
283178825Sdfr    OM_uint32 ret;
284178825Sdfr    gss_buffer_desc mic_buf;
285178825Sdfr
286178825Sdfr    if (context_handle->verified_mic) {
287178825Sdfr	/* This doesn't make sense, we've already verified it? */
288178825Sdfr	*minor_status = 0;
289178825Sdfr	return GSS_S_DUPLICATE_TOKEN;
290178825Sdfr    }
291178825Sdfr
292178825Sdfr    if (mechListMIC == NULL) {
293178825Sdfr	*minor_status = 0;
294178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
295178825Sdfr    }
296178825Sdfr
297178825Sdfr    mic_buf.length = mechListMIC->length;
298178825Sdfr    mic_buf.value  = mechListMIC->data;
299178825Sdfr
300178825Sdfr    ret = gss_verify_mic(minor_status,
301178825Sdfr			 context_handle->negotiated_ctx_id,
302178825Sdfr			 mech_buf,
303178825Sdfr			 &mic_buf,
304178825Sdfr			 NULL);
305178825Sdfr
306178825Sdfr    if (ret != GSS_S_COMPLETE)
307178825Sdfr	ret = GSS_S_DEFECTIVE_TOKEN;
308178825Sdfr
309178825Sdfr    return ret;
310178825Sdfr}
311178825Sdfr
312178825Sdfrstatic OM_uint32
313178825Sdfrselect_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
314178825Sdfr	    gss_OID *mech_p)
315178825Sdfr{
316178825Sdfr    char mechbuf[64];
317178825Sdfr    size_t mech_len;
318178825Sdfr    gss_OID_desc oid;
319178828Sdfr    gss_OID oidp;
320178828Sdfr    gss_OID_set mechs;
321233294Sstas    size_t i;
322178825Sdfr    OM_uint32 ret, junk;
323178825Sdfr
324178825Sdfr    ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
325178825Sdfr		       sizeof(mechbuf),
326178825Sdfr		       mechType,
327178825Sdfr		       &mech_len);
328178825Sdfr    if (ret) {
329178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
330178825Sdfr    }
331178825Sdfr
332178825Sdfr    oid.length   = mech_len;
333178825Sdfr    oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
334178825Sdfr
335178825Sdfr    if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
336178825Sdfr	return GSS_S_BAD_MECH;
337178825Sdfr    }
338178825Sdfr
339178825Sdfr    *minor_status = 0;
340178825Sdfr
341178825Sdfr    /* Translate broken MS Kebreros OID */
342178828Sdfr    if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
343178828Sdfr	    oidp = &_gss_spnego_krb5_mechanism_oid_desc;
344178828Sdfr    else
345178828Sdfr	    oidp = &oid;
346178825Sdfr
347178825Sdfr
348178828Sdfr    ret = gss_indicate_mechs(&junk, &mechs);
349178828Sdfr    if (ret)
350178828Sdfr	    return (ret);
351178825Sdfr
352178828Sdfr    for (i = 0; i < mechs->count; i++)
353178828Sdfr	    if (gss_oid_equal(&mechs->elements[i], oidp))
354178828Sdfr		    break;
355178828Sdfr
356178828Sdfr    if (i == mechs->count) {
357178828Sdfr	    gss_release_oid_set(&junk, &mechs);
358178825Sdfr	    return GSS_S_BAD_MECH;
359178825Sdfr    }
360178828Sdfr    gss_release_oid_set(&junk, &mechs);
361178825Sdfr
362178828Sdfr    ret = gss_duplicate_oid(minor_status,
363178828Sdfr			    &oid, /* possibly this should be oidp */
364178828Sdfr			    mech_p);
365178828Sdfr
366178825Sdfr    if (verify_p) {
367178825Sdfr	gss_name_t name = GSS_C_NO_NAME;
368178825Sdfr	gss_buffer_desc namebuf;
369178825Sdfr	char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
370178825Sdfr
371178825Sdfr	host = getenv("GSSAPI_SPNEGO_NAME");
372178825Sdfr	if (host == NULL || issuid()) {
373233294Sstas	    int rv;
374178825Sdfr	    if (gethostname(hostname, sizeof(hostname)) != 0) {
375178825Sdfr		*minor_status = errno;
376178825Sdfr		return GSS_S_FAILURE;
377178825Sdfr	    }
378233294Sstas	    rv = asprintf(&str, "host@%s", hostname);
379233294Sstas	    if (rv < 0 || str == NULL) {
380233294Sstas		*minor_status = ENOMEM;
381233294Sstas		return GSS_S_FAILURE;
382233294Sstas	    }
383178825Sdfr	    host = str;
384178825Sdfr	}
385178825Sdfr
386178825Sdfr	namebuf.length = strlen(host);
387178825Sdfr	namebuf.value = host;
388178825Sdfr
389178825Sdfr	ret = gss_import_name(minor_status, &namebuf,
390178825Sdfr			      GSS_C_NT_HOSTBASED_SERVICE, &name);
391178825Sdfr	if (str)
392178825Sdfr	    free(str);
393178825Sdfr	if (ret != GSS_S_COMPLETE)
394178825Sdfr	    return ret;
395178825Sdfr
396178825Sdfr	ret = acceptor_approved(name, *mech_p);
397178825Sdfr	gss_release_name(&junk, &name);
398178825Sdfr    }
399178825Sdfr
400178825Sdfr    return ret;
401178825Sdfr}
402178825Sdfr
403178825Sdfr
404178825Sdfrstatic OM_uint32
405178825Sdfracceptor_complete(OM_uint32 * minor_status,
406178825Sdfr		  gssspnego_ctx ctx,
407178825Sdfr		  int *get_mic,
408178825Sdfr		  gss_buffer_t mech_buf,
409178825Sdfr		  gss_buffer_t mech_input_token,
410178825Sdfr		  gss_buffer_t mech_output_token,
411178825Sdfr		  heim_octet_string *mic,
412178825Sdfr		  gss_buffer_t output_token)
413178825Sdfr{
414178825Sdfr    OM_uint32 ret;
415178825Sdfr    int require_mic, verify_mic;
416178825Sdfr
417178825Sdfr    ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
418178825Sdfr    if (ret)
419178825Sdfr	return ret;
420233294Sstas
421178825Sdfr    ctx->require_mic = require_mic;
422178825Sdfr
423178825Sdfr    if (mic != NULL)
424178825Sdfr	require_mic = 1;
425233294Sstas
426178825Sdfr    if (ctx->open && require_mic) {
427178825Sdfr	if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
428178825Sdfr	    verify_mic = 1;
429178825Sdfr	    *get_mic = 0;
430178825Sdfr	} else if (mech_output_token != GSS_C_NO_BUFFER &&
431178825Sdfr		   mech_output_token->length == 0) { /* Odd */
432178825Sdfr	    *get_mic = verify_mic = 1;
433178825Sdfr	} else { /* Even/One */
434178825Sdfr	    verify_mic = 0;
435178825Sdfr	    *get_mic = 1;
436178825Sdfr	}
437233294Sstas
438233294Sstas	if (verify_mic || *get_mic) {
439178825Sdfr	    int eret;
440233294Sstas	    size_t buf_len = 0;
441233294Sstas
442233294Sstas	    ASN1_MALLOC_ENCODE(MechTypeList,
443178825Sdfr			       mech_buf->value, mech_buf->length,
444178825Sdfr			       &ctx->initiator_mech_types, &buf_len, eret);
445178825Sdfr	    if (eret) {
446178825Sdfr		*minor_status = eret;
447178825Sdfr		return GSS_S_FAILURE;
448178825Sdfr	    }
449233294Sstas	    heim_assert(mech_buf->length == buf_len, "Internal ASN.1 error");
450233294Sstas	    UNREACHABLE(return GSS_S_FAILURE);
451178825Sdfr	}
452233294Sstas
453178825Sdfr	if (verify_mic) {
454178825Sdfr	    ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
455178825Sdfr	    if (ret) {
456233294Sstas		if (*get_mic)
457178825Sdfr		    send_reject (minor_status, output_token);
458178825Sdfr		return ret;
459178825Sdfr	    }
460178825Sdfr	    ctx->verified_mic = 1;
461178825Sdfr	}
462233294Sstas    } else
463233294Sstas	*get_mic = 0;
464178825Sdfr
465178825Sdfr    return GSS_S_COMPLETE;
466178825Sdfr}
467178825Sdfr
468178825Sdfr
469233294Sstasstatic OM_uint32 GSSAPI_CALLCONV
470178825Sdfracceptor_start
471178825Sdfr	   (OM_uint32 * minor_status,
472178825Sdfr	    gss_ctx_id_t * context_handle,
473178825Sdfr	    const gss_cred_id_t acceptor_cred_handle,
474178825Sdfr	    const gss_buffer_t input_token_buffer,
475178825Sdfr	    const gss_channel_bindings_t input_chan_bindings,
476178825Sdfr	    gss_name_t * src_name,
477178825Sdfr	    gss_OID * mech_type,
478178825Sdfr	    gss_buffer_t output_token,
479178825Sdfr	    OM_uint32 * ret_flags,
480178825Sdfr	    OM_uint32 * time_rec,
481178825Sdfr	    gss_cred_id_t *delegated_cred_handle
482178825Sdfr	   )
483178825Sdfr{
484233294Sstas    OM_uint32 ret, junk;
485178825Sdfr    NegotiationToken nt;
486178825Sdfr    size_t nt_len;
487178825Sdfr    NegTokenInit *ni;
488178825Sdfr    gss_buffer_desc data;
489178825Sdfr    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
490178825Sdfr    gss_buffer_desc mech_output_token;
491178825Sdfr    gss_buffer_desc mech_buf;
492178825Sdfr    gss_OID preferred_mech_type = GSS_C_NO_OID;
493178825Sdfr    gssspnego_ctx ctx;
494178825Sdfr    int get_mic = 0;
495178825Sdfr    int first_ok = 0;
496178825Sdfr
497178825Sdfr    mech_output_token.value = NULL;
498178825Sdfr    mech_output_token.length = 0;
499178825Sdfr    mech_buf.value = NULL;
500178825Sdfr
501178825Sdfr    if (input_token_buffer->length == 0)
502178825Sdfr	return send_supported_mechs (minor_status, output_token);
503233294Sstas
504178825Sdfr    ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
505178825Sdfr    if (ret != GSS_S_COMPLETE)
506178825Sdfr	return ret;
507178825Sdfr
508178825Sdfr    ctx = (gssspnego_ctx)*context_handle;
509178825Sdfr
510178825Sdfr    /*
511178825Sdfr     * The GSS-API encapsulation is only present on the initial
512178825Sdfr     * context token (negTokenInit).
513178825Sdfr     */
514178825Sdfr    ret = gss_decapsulate_token (input_token_buffer,
515178825Sdfr				 GSS_SPNEGO_MECHANISM,
516178825Sdfr				 &data);
517178825Sdfr    if (ret)
518178825Sdfr	return ret;
519178825Sdfr
520178825Sdfr    ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
521178825Sdfr    gss_release_buffer(minor_status, &data);
522178825Sdfr    if (ret) {
523178825Sdfr	*minor_status = ret;
524178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
525178825Sdfr    }
526178825Sdfr    if (nt.element != choice_NegotiationToken_negTokenInit) {
527178825Sdfr	*minor_status = 0;
528178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
529178825Sdfr    }
530178825Sdfr    ni = &nt.u.negTokenInit;
531178825Sdfr
532178825Sdfr    if (ni->mechTypes.len < 1) {
533178825Sdfr	free_NegotiationToken(&nt);
534178825Sdfr	*minor_status = 0;
535178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
536178825Sdfr    }
537178825Sdfr
538178825Sdfr    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
539178825Sdfr
540178825Sdfr    ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
541178825Sdfr    if (ret) {
542178825Sdfr	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
543178825Sdfr	free_NegotiationToken(&nt);
544178825Sdfr	*minor_status = ret;
545178825Sdfr	return GSS_S_FAILURE;
546178825Sdfr    }
547178825Sdfr
548178825Sdfr    /*
549178825Sdfr     * First we try the opportunistic token if we have support for it,
550178825Sdfr     * don't try to verify we have credential for the token,
551233294Sstas     * gss_accept_sec_context() will (hopefully) tell us that.
552233294Sstas     * If that failes,
553178825Sdfr     */
554178825Sdfr
555178825Sdfr    ret = select_mech(minor_status,
556233294Sstas		      &ni->mechTypes.val[0],
557178825Sdfr		      0,
558178825Sdfr		      &preferred_mech_type);
559178825Sdfr
560178825Sdfr    if (ret == 0 && ni->mechToken != NULL) {
561178825Sdfr	gss_buffer_desc ibuf;
562178825Sdfr
563178825Sdfr	ibuf.length = ni->mechToken->length;
564178825Sdfr	ibuf.value = ni->mechToken->data;
565178825Sdfr	mech_input_token = &ibuf;
566178825Sdfr
567178825Sdfr	if (ctx->mech_src_name != GSS_C_NO_NAME)
568233294Sstas	    gss_release_name(&junk, &ctx->mech_src_name);
569233294Sstas
570233294Sstas	ret = gss_accept_sec_context(minor_status,
571178825Sdfr				     &ctx->negotiated_ctx_id,
572233294Sstas				     acceptor_cred_handle,
573178825Sdfr				     mech_input_token,
574178825Sdfr				     input_chan_bindings,
575178825Sdfr				     &ctx->mech_src_name,
576178825Sdfr				     &ctx->negotiated_mech_type,
577178825Sdfr				     &mech_output_token,
578178825Sdfr				     &ctx->mech_flags,
579178825Sdfr				     &ctx->mech_time_rec,
580233294Sstas				     delegated_cred_handle);
581233294Sstas
582178825Sdfr	if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
583178825Sdfr	    ctx->preferred_mech_type = preferred_mech_type;
584178825Sdfr	    if (ret == GSS_S_COMPLETE)
585178825Sdfr		ctx->open = 1;
586178825Sdfr
587178825Sdfr	    ret = acceptor_complete(minor_status,
588178825Sdfr				    ctx,
589178825Sdfr				    &get_mic,
590178825Sdfr				    &mech_buf,
591178825Sdfr				    mech_input_token,
592178825Sdfr				    &mech_output_token,
593178825Sdfr				    ni->mechListMIC,
594178825Sdfr				    output_token);
595178825Sdfr	    if (ret != GSS_S_COMPLETE)
596178825Sdfr		goto out;
597178825Sdfr
598178825Sdfr	    first_ok = 1;
599233294Sstas	} else {
600233294Sstas	    gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
601178825Sdfr	}
602178825Sdfr    }
603178825Sdfr
604178825Sdfr    /*
605178825Sdfr     * If opportunistic token failed, lets try the other mechs.
606178825Sdfr     */
607178825Sdfr
608233294Sstas    if (!first_ok && ni->mechToken != NULL) {
609233294Sstas	size_t j;
610178825Sdfr
611233294Sstas	preferred_mech_type = GSS_C_NO_OID;
612233294Sstas
613178825Sdfr	/* Call glue layer to find first mech we support */
614233294Sstas	for (j = 1; j < ni->mechTypes.len; ++j) {
615178825Sdfr	    ret = select_mech(minor_status,
616233294Sstas			      &ni->mechTypes.val[j],
617178825Sdfr			      1,
618178825Sdfr			      &preferred_mech_type);
619178825Sdfr	    if (ret == 0)
620178825Sdfr		break;
621178825Sdfr	}
622178825Sdfr	if (preferred_mech_type == GSS_C_NO_OID) {
623178825Sdfr	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
624178825Sdfr	    free_NegotiationToken(&nt);
625233294Sstas	    return ret;
626178825Sdfr	}
627178825Sdfr
628178825Sdfr	ctx->preferred_mech_type = preferred_mech_type;
629178825Sdfr    }
630178825Sdfr
631178825Sdfr    /*
632178825Sdfr     * The initial token always have a response
633178825Sdfr     */
634178825Sdfr
635178825Sdfr    ret = send_accept (minor_status,
636178825Sdfr		       ctx,
637178825Sdfr		       &mech_output_token,
638178825Sdfr		       1,
639178825Sdfr		       get_mic ? &mech_buf : NULL,
640178825Sdfr		       output_token);
641178825Sdfr    if (ret)
642178825Sdfr	goto out;
643233294Sstas
644178825Sdfrout:
645178825Sdfr    if (mech_output_token.value != NULL)
646233294Sstas	gss_release_buffer(&junk, &mech_output_token);
647178825Sdfr    if (mech_buf.value != NULL) {
648178825Sdfr	free(mech_buf.value);
649178825Sdfr	mech_buf.value = NULL;
650178825Sdfr    }
651178825Sdfr    free_NegotiationToken(&nt);
652178825Sdfr
653178825Sdfr
654178825Sdfr    if (ret == GSS_S_COMPLETE) {
655178825Sdfr	if (src_name != NULL && ctx->mech_src_name != NULL) {
656178825Sdfr	    spnego_name name;
657178825Sdfr
658178825Sdfr	    name = calloc(1, sizeof(*name));
659178825Sdfr	    if (name) {
660178825Sdfr		name->mech = ctx->mech_src_name;
661178825Sdfr		ctx->mech_src_name = NULL;
662178825Sdfr		*src_name = (gss_name_t)name;
663178825Sdfr	    }
664178825Sdfr	}
665178825Sdfr    }
666233294Sstas
667178825Sdfr    if (mech_type != NULL)
668178825Sdfr	*mech_type = ctx->negotiated_mech_type;
669178825Sdfr    if (ret_flags != NULL)
670178825Sdfr	*ret_flags = ctx->mech_flags;
671178825Sdfr    if (time_rec != NULL)
672178825Sdfr	*time_rec = ctx->mech_time_rec;
673178825Sdfr
674178825Sdfr    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
675178825Sdfr	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
676178825Sdfr 	return ret;
677178825Sdfr    }
678178825Sdfr
679233294Sstas    _gss_spnego_internal_delete_sec_context(&junk, context_handle,
680178825Sdfr					    GSS_C_NO_BUFFER);
681233294Sstas
682178825Sdfr    return ret;
683178825Sdfr}
684178825Sdfr
685178825Sdfr
686233294Sstasstatic OM_uint32 GSSAPI_CALLCONV
687178825Sdfracceptor_continue
688178825Sdfr	   (OM_uint32 * minor_status,
689178825Sdfr	    gss_ctx_id_t * context_handle,
690178825Sdfr	    const gss_cred_id_t acceptor_cred_handle,
691178825Sdfr	    const gss_buffer_t input_token_buffer,
692178825Sdfr	    const gss_channel_bindings_t input_chan_bindings,
693178825Sdfr	    gss_name_t * src_name,
694178825Sdfr	    gss_OID * mech_type,
695178825Sdfr	    gss_buffer_t output_token,
696178825Sdfr	    OM_uint32 * ret_flags,
697178825Sdfr	    OM_uint32 * time_rec,
698178825Sdfr	    gss_cred_id_t *delegated_cred_handle
699178825Sdfr	   )
700178825Sdfr{
701178825Sdfr    OM_uint32 ret, ret2, minor;
702178825Sdfr    NegotiationToken nt;
703178825Sdfr    size_t nt_len;
704178825Sdfr    NegTokenResp *na;
705178825Sdfr    unsigned int negResult = accept_incomplete;
706178825Sdfr    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
707178825Sdfr    gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
708178825Sdfr    gss_buffer_desc mech_buf;
709178825Sdfr    gssspnego_ctx ctx;
710178825Sdfr
711178825Sdfr    mech_buf.value = NULL;
712178825Sdfr
713178825Sdfr    ctx = (gssspnego_ctx)*context_handle;
714178825Sdfr
715178825Sdfr    /*
716178825Sdfr     * The GSS-API encapsulation is only present on the initial
717178825Sdfr     * context token (negTokenInit).
718178825Sdfr     */
719178825Sdfr
720233294Sstas    ret = decode_NegotiationToken(input_token_buffer->value,
721178825Sdfr				  input_token_buffer->length,
722178825Sdfr				  &nt, &nt_len);
723178825Sdfr    if (ret) {
724178825Sdfr	*minor_status = ret;
725178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
726178825Sdfr    }
727178825Sdfr    if (nt.element != choice_NegotiationToken_negTokenResp) {
728178825Sdfr	*minor_status = 0;
729178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
730178825Sdfr    }
731178825Sdfr    na = &nt.u.negTokenResp;
732178825Sdfr
733178825Sdfr    if (na->negResult != NULL) {
734178825Sdfr	negResult = *(na->negResult);
735178825Sdfr    }
736178825Sdfr
737178825Sdfr    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
738178825Sdfr
739178825Sdfr    {
740178825Sdfr	gss_buffer_desc ibuf, obuf;
741178825Sdfr	int require_mic, get_mic = 0;
742178825Sdfr	int require_response;
743178825Sdfr	heim_octet_string *mic;
744178825Sdfr
745178825Sdfr	if (na->responseToken != NULL) {
746178825Sdfr	    ibuf.length = na->responseToken->length;
747178825Sdfr	    ibuf.value = na->responseToken->data;
748178825Sdfr	    mech_input_token = &ibuf;
749178825Sdfr	} else {
750178825Sdfr	    ibuf.value = NULL;
751178825Sdfr	    ibuf.length = 0;
752178825Sdfr	}
753178825Sdfr
754178825Sdfr	if (mech_input_token != GSS_C_NO_BUFFER) {
755178825Sdfr
756178825Sdfr	    if (ctx->mech_src_name != GSS_C_NO_NAME)
757178825Sdfr		gss_release_name(&minor, &ctx->mech_src_name);
758178825Sdfr
759178825Sdfr	    ret = gss_accept_sec_context(&minor,
760178825Sdfr					 &ctx->negotiated_ctx_id,
761233294Sstas					 acceptor_cred_handle,
762178825Sdfr					 mech_input_token,
763178825Sdfr					 input_chan_bindings,
764178825Sdfr					 &ctx->mech_src_name,
765178825Sdfr					 &ctx->negotiated_mech_type,
766178825Sdfr					 &obuf,
767178825Sdfr					 &ctx->mech_flags,
768178825Sdfr					 &ctx->mech_time_rec,
769233294Sstas					 delegated_cred_handle);
770233294Sstas
771178825Sdfr	    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
772178825Sdfr		mech_output_token = &obuf;
773178825Sdfr	    }
774178825Sdfr	    if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
775178825Sdfr		free_NegotiationToken(&nt);
776233294Sstas		gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
777178825Sdfr		send_reject (minor_status, output_token);
778178825Sdfr		HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
779178825Sdfr		return ret;
780178825Sdfr	    }
781178825Sdfr	    if (ret == GSS_S_COMPLETE)
782178825Sdfr		ctx->open = 1;
783178825Sdfr	} else
784178825Sdfr	    ret = GSS_S_COMPLETE;
785178825Sdfr
786233294Sstas	ret2 = _gss_spnego_require_mechlist_mic(minor_status,
787178825Sdfr						ctx,
788178825Sdfr						&require_mic);
789178825Sdfr	if (ret2)
790178825Sdfr	    goto out;
791178825Sdfr
792178825Sdfr	ctx->require_mic = require_mic;
793178825Sdfr
794178825Sdfr	mic = na->mechListMIC;
795178825Sdfr	if (mic != NULL)
796178825Sdfr	    require_mic = 1;
797178825Sdfr
798178825Sdfr	if (ret == GSS_S_COMPLETE)
799178825Sdfr	    ret = acceptor_complete(minor_status,
800178825Sdfr				    ctx,
801178825Sdfr				    &get_mic,
802178825Sdfr				    &mech_buf,
803178825Sdfr				    mech_input_token,
804178825Sdfr				    mech_output_token,
805178825Sdfr				    na->mechListMIC,
806178825Sdfr				    output_token);
807178825Sdfr
808178825Sdfr	if (ctx->mech_flags & GSS_C_DCE_STYLE)
809178825Sdfr	    require_response = (negResult != accept_completed);
810178825Sdfr	else
811178825Sdfr	    require_response = 0;
812178825Sdfr
813178825Sdfr	/*
814178825Sdfr	 * Check whether we need to send a result: there should be only
815178825Sdfr	 * one accept_completed response sent in the entire negotiation
816178825Sdfr	 */
817178825Sdfr	if ((mech_output_token != GSS_C_NO_BUFFER &&
818178825Sdfr	     mech_output_token->length != 0)
819178825Sdfr	    || (ctx->open && negResult == accept_incomplete)
820178825Sdfr	    || require_response
821178825Sdfr	    || get_mic) {
822178825Sdfr	    ret2 = send_accept (minor_status,
823178825Sdfr				ctx,
824178825Sdfr				mech_output_token,
825178825Sdfr				0,
826178825Sdfr				get_mic ? &mech_buf : NULL,
827178825Sdfr				output_token);
828178825Sdfr	    if (ret2)
829178825Sdfr		goto out;
830178825Sdfr	}
831178825Sdfr
832178825Sdfr     out:
833178825Sdfr	if (ret2 != GSS_S_COMPLETE)
834178825Sdfr	    ret = ret2;
835178825Sdfr	if (mech_output_token != NULL)
836178825Sdfr	    gss_release_buffer(&minor, mech_output_token);
837178825Sdfr	if (mech_buf.value != NULL)
838178825Sdfr	    free(mech_buf.value);
839178825Sdfr	free_NegotiationToken(&nt);
840178825Sdfr    }
841178825Sdfr
842178825Sdfr    if (ret == GSS_S_COMPLETE) {
843178825Sdfr	if (src_name != NULL && ctx->mech_src_name != NULL) {
844178825Sdfr	    spnego_name name;
845178825Sdfr
846178825Sdfr	    name = calloc(1, sizeof(*name));
847178825Sdfr	    if (name) {
848178825Sdfr		name->mech = ctx->mech_src_name;
849178825Sdfr		ctx->mech_src_name = NULL;
850178825Sdfr		*src_name = (gss_name_t)name;
851178825Sdfr	    }
852178825Sdfr	}
853178825Sdfr    }
854178825Sdfr
855178825Sdfr    if (mech_type != NULL)
856178825Sdfr	*mech_type = ctx->negotiated_mech_type;
857178825Sdfr    if (ret_flags != NULL)
858178825Sdfr	*ret_flags = ctx->mech_flags;
859178825Sdfr    if (time_rec != NULL)
860178825Sdfr	*time_rec = ctx->mech_time_rec;
861178825Sdfr
862178825Sdfr    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
863178825Sdfr	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
864178825Sdfr 	return ret;
865178825Sdfr    }
866178825Sdfr
867178825Sdfr    _gss_spnego_internal_delete_sec_context(&minor, context_handle,
868178825Sdfr				   GSS_C_NO_BUFFER);
869178825Sdfr
870178825Sdfr    return ret;
871178825Sdfr}
872178825Sdfr
873233294SstasOM_uint32 GSSAPI_CALLCONV
874178825Sdfr_gss_spnego_accept_sec_context
875178825Sdfr	   (OM_uint32 * minor_status,
876178825Sdfr	    gss_ctx_id_t * context_handle,
877178825Sdfr	    const gss_cred_id_t acceptor_cred_handle,
878178825Sdfr	    const gss_buffer_t input_token_buffer,
879178825Sdfr	    const gss_channel_bindings_t input_chan_bindings,
880178825Sdfr	    gss_name_t * src_name,
881178825Sdfr	    gss_OID * mech_type,
882178825Sdfr	    gss_buffer_t output_token,
883178825Sdfr	    OM_uint32 * ret_flags,
884178825Sdfr	    OM_uint32 * time_rec,
885178825Sdfr	    gss_cred_id_t *delegated_cred_handle
886178825Sdfr	   )
887178825Sdfr{
888178825Sdfr    _gss_accept_sec_context_t *func;
889178825Sdfr
890178825Sdfr    *minor_status = 0;
891178825Sdfr
892178825Sdfr    output_token->length = 0;
893178825Sdfr    output_token->value  = NULL;
894178825Sdfr
895178825Sdfr    if (src_name != NULL)
896178825Sdfr	*src_name = GSS_C_NO_NAME;
897178825Sdfr    if (mech_type != NULL)
898178825Sdfr	*mech_type = GSS_C_NO_OID;
899178825Sdfr    if (ret_flags != NULL)
900178825Sdfr	*ret_flags = 0;
901178825Sdfr    if (time_rec != NULL)
902178825Sdfr	*time_rec = 0;
903178825Sdfr    if (delegated_cred_handle != NULL)
904178825Sdfr	*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
905178825Sdfr
906178825Sdfr
907233294Sstas    if (*context_handle == GSS_C_NO_CONTEXT)
908178825Sdfr	func = acceptor_start;
909178825Sdfr    else
910178825Sdfr	func = acceptor_continue;
911178825Sdfr
912233294Sstas
913178825Sdfr    return (*func)(minor_status, context_handle, acceptor_cred_handle,
914178825Sdfr		   input_token_buffer, input_chan_bindings,
915178825Sdfr		   src_name, mech_type, output_token, ret_flags,
916178825Sdfr		   time_rec, delegated_cred_handle);
917178825Sdfr}
918