1/*
2 * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * Portions Copyright (c) 2004 PADL Software Pty Ltd.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "spnego/spnego_locl.h"
35
36RCSID("$Id: accept_sec_context.c 21461 2007-07-10 14:01:13Z lha $");
37/* $FreeBSD$ */
38
39static OM_uint32
40send_reject (OM_uint32 *minor_status,
41	     gss_buffer_t output_token)
42{
43    NegotiationToken nt;
44    size_t size;
45
46    nt.element = choice_NegotiationToken_negTokenResp;
47
48    ALLOC(nt.u.negTokenResp.negResult, 1);
49    if (nt.u.negTokenResp.negResult == NULL) {
50	*minor_status = ENOMEM;
51	return GSS_S_FAILURE;
52    }
53    *(nt.u.negTokenResp.negResult)  = reject;
54    nt.u.negTokenResp.supportedMech = NULL;
55    nt.u.negTokenResp.responseToken = NULL;
56    nt.u.negTokenResp.mechListMIC   = NULL;
57
58    ASN1_MALLOC_ENCODE(NegotiationToken,
59		       output_token->value, output_token->length, &nt,
60		       &size, *minor_status);
61    free_NegotiationToken(&nt);
62    if (*minor_status != 0)
63	return GSS_S_FAILURE;
64
65    return GSS_S_BAD_MECH;
66}
67
68static OM_uint32
69acceptor_approved(gss_name_t target_name, gss_OID mech)
70{
71    gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
72    gss_OID_set oidset;
73    OM_uint32 junk, ret;
74
75    if (target_name == GSS_C_NO_NAME)
76	return GSS_S_COMPLETE;
77
78    gss_create_empty_oid_set(&junk, &oidset);
79    gss_add_oid_set_member(&junk, mech, &oidset);
80
81    ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
82			   GSS_C_ACCEPT, &cred, NULL, NULL);
83    gss_release_oid_set(&junk, &oidset);
84    if (ret != GSS_S_COMPLETE)
85	return ret;
86    gss_release_cred(&junk, &cred);
87
88    return GSS_S_COMPLETE;
89}
90
91static OM_uint32
92send_supported_mechs (OM_uint32 *minor_status,
93		      gss_buffer_t output_token)
94{
95    NegotiationTokenWin nt;
96    char hostname[MAXHOSTNAMELEN + 1], *p;
97    gss_buffer_desc name_buf;
98    gss_OID name_type;
99    gss_name_t target_princ;
100    gss_name_t canon_princ;
101    OM_uint32 minor;
102    size_t buf_len;
103    gss_buffer_desc data;
104    OM_uint32 ret;
105
106    memset(&nt, 0, sizeof(nt));
107
108    nt.element = choice_NegotiationTokenWin_negTokenInit;
109    nt.u.negTokenInit.reqFlags = NULL;
110    nt.u.negTokenInit.mechToken = NULL;
111    nt.u.negTokenInit.negHints = NULL;
112
113    ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
114					    acceptor_approved, 1, NULL,
115					    &nt.u.negTokenInit.mechTypes, NULL);
116    if (ret != GSS_S_COMPLETE) {
117	return ret;
118    }
119
120    memset(&target_princ, 0, sizeof(target_princ));
121    if (gethostname(hostname, sizeof(hostname) - 2) != 0) {
122	*minor_status = errno;
123	free_NegotiationTokenWin(&nt);
124	return GSS_S_FAILURE;
125    }
126    hostname[sizeof(hostname) - 1] = '\0';
127
128    /* Send the constructed SAM name for this host */
129    for (p = hostname; *p != '\0' && *p != '.'; p++) {
130	*p = toupper((unsigned char)*p);
131    }
132    *p++ = '$';
133    *p = '\0';
134
135    name_buf.length = strlen(hostname);
136    name_buf.value = hostname;
137
138    ret = gss_import_name(minor_status, &name_buf,
139			  GSS_C_NO_OID,
140			  &target_princ);
141    if (ret != GSS_S_COMPLETE) {
142	free_NegotiationTokenWin(&nt);
143	return ret;
144    }
145
146    name_buf.length = 0;
147    name_buf.value = NULL;
148
149    /* Canonicalize the name using the preferred mechanism */
150    ret = gss_canonicalize_name(minor_status,
151				target_princ,
152				GSS_C_NO_OID,
153				&canon_princ);
154    if (ret != GSS_S_COMPLETE) {
155	free_NegotiationTokenWin(&nt);
156	gss_release_name(&minor, &target_princ);
157	return ret;
158    }
159
160    ret = gss_display_name(minor_status, canon_princ,
161			   &name_buf, &name_type);
162    if (ret != GSS_S_COMPLETE) {
163	free_NegotiationTokenWin(&nt);
164	gss_release_name(&minor, &canon_princ);
165	gss_release_name(&minor, &target_princ);
166	return ret;
167    }
168
169    gss_release_name(&minor, &canon_princ);
170    gss_release_name(&minor, &target_princ);
171
172    ALLOC(nt.u.negTokenInit.negHints, 1);
173    if (nt.u.negTokenInit.negHints == NULL) {
174	*minor_status = ENOMEM;
175	gss_release_buffer(&minor, &name_buf);
176	free_NegotiationTokenWin(&nt);
177	return GSS_S_FAILURE;
178    }
179
180    ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
181    if (nt.u.negTokenInit.negHints->hintName == NULL) {
182	*minor_status = ENOMEM;
183	gss_release_buffer(&minor, &name_buf);
184	free_NegotiationTokenWin(&nt);
185	return GSS_S_FAILURE;
186    }
187
188    *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
189    name_buf.value = NULL;
190    nt.u.negTokenInit.negHints->hintAddress = NULL;
191
192    ASN1_MALLOC_ENCODE(NegotiationTokenWin,
193		       data.value, data.length, &nt, &buf_len, ret);
194    free_NegotiationTokenWin(&nt);
195    if (ret) {
196	return ret;
197    }
198    if (data.length != buf_len)
199	abort();
200
201    ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
202
203    free (data.value);
204
205    if (ret != GSS_S_COMPLETE)
206	return ret;
207
208    *minor_status = 0;
209
210    return GSS_S_CONTINUE_NEEDED;
211}
212
213static OM_uint32
214send_accept (OM_uint32 *minor_status,
215	     gssspnego_ctx context_handle,
216	     gss_buffer_t mech_token,
217	     int initial_response,
218	     gss_buffer_t mech_buf,
219	     gss_buffer_t output_token)
220{
221    NegotiationToken nt;
222    OM_uint32 ret;
223    gss_buffer_desc mech_mic_buf;
224    size_t size;
225
226    memset(&nt, 0, sizeof(nt));
227
228    nt.element = choice_NegotiationToken_negTokenResp;
229
230    ALLOC(nt.u.negTokenResp.negResult, 1);
231    if (nt.u.negTokenResp.negResult == NULL) {
232	*minor_status = ENOMEM;
233	return GSS_S_FAILURE;
234    }
235
236    if (context_handle->open) {
237	if (mech_token != GSS_C_NO_BUFFER
238	    && mech_token->length != 0
239	    && mech_buf != GSS_C_NO_BUFFER)
240	    *(nt.u.negTokenResp.negResult)  = accept_incomplete;
241	else
242	    *(nt.u.negTokenResp.negResult)  = accept_completed;
243    } else {
244	if (initial_response && context_handle->require_mic)
245	    *(nt.u.negTokenResp.negResult)  = request_mic;
246	else
247	    *(nt.u.negTokenResp.negResult)  = accept_incomplete;
248    }
249
250    if (initial_response) {
251	ALLOC(nt.u.negTokenResp.supportedMech, 1);
252	if (nt.u.negTokenResp.supportedMech == NULL) {
253	    free_NegotiationToken(&nt);
254	    *minor_status = ENOMEM;
255	    return GSS_S_FAILURE;
256	}
257
258	ret = der_get_oid(context_handle->preferred_mech_type->elements,
259			  context_handle->preferred_mech_type->length,
260			  nt.u.negTokenResp.supportedMech,
261			  NULL);
262	if (ret) {
263	    free_NegotiationToken(&nt);
264	    *minor_status = ENOMEM;
265	    return GSS_S_FAILURE;
266	}
267    } else {
268	nt.u.negTokenResp.supportedMech = NULL;
269    }
270
271    if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
272	ALLOC(nt.u.negTokenResp.responseToken, 1);
273	if (nt.u.negTokenResp.responseToken == NULL) {
274	    free_NegotiationToken(&nt);
275	    *minor_status = ENOMEM;
276	    return GSS_S_FAILURE;
277	}
278	nt.u.negTokenResp.responseToken->length = mech_token->length;
279	nt.u.negTokenResp.responseToken->data   = mech_token->value;
280	mech_token->length = 0;
281	mech_token->value  = NULL;
282    } else {
283	nt.u.negTokenResp.responseToken = NULL;
284    }
285
286    if (mech_buf != GSS_C_NO_BUFFER) {
287	ret = gss_get_mic(minor_status,
288			  context_handle->negotiated_ctx_id,
289			  0,
290			  mech_buf,
291			  &mech_mic_buf);
292	if (ret == GSS_S_COMPLETE) {
293	    ALLOC(nt.u.negTokenResp.mechListMIC, 1);
294	    if (nt.u.negTokenResp.mechListMIC == NULL) {
295		gss_release_buffer(minor_status, &mech_mic_buf);
296		free_NegotiationToken(&nt);
297		*minor_status = ENOMEM;
298		return GSS_S_FAILURE;
299	    }
300	    nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
301	    nt.u.negTokenResp.mechListMIC->data   = mech_mic_buf.value;
302	} else if (ret == GSS_S_UNAVAILABLE) {
303	    nt.u.negTokenResp.mechListMIC = NULL;
304	} else {
305	    free_NegotiationToken(&nt);
306	    return ret;
307	}
308
309    } else
310	nt.u.negTokenResp.mechListMIC = NULL;
311
312    ASN1_MALLOC_ENCODE(NegotiationToken,
313		       output_token->value, output_token->length,
314		       &nt, &size, ret);
315    if (ret) {
316	free_NegotiationToken(&nt);
317	*minor_status = ret;
318	return GSS_S_FAILURE;
319    }
320
321    /*
322     * The response should not be encapsulated, because
323     * it is a SubsequentContextToken (note though RFC 1964
324     * specifies encapsulation for all _Kerberos_ tokens).
325     */
326
327    if (*(nt.u.negTokenResp.negResult) == accept_completed)
328	ret = GSS_S_COMPLETE;
329    else
330	ret = GSS_S_CONTINUE_NEEDED;
331    free_NegotiationToken(&nt);
332    return ret;
333}
334
335
336static OM_uint32
337verify_mechlist_mic
338	   (OM_uint32 *minor_status,
339	    gssspnego_ctx context_handle,
340	    gss_buffer_t mech_buf,
341	    heim_octet_string *mechListMIC
342	   )
343{
344    OM_uint32 ret;
345    gss_buffer_desc mic_buf;
346
347    if (context_handle->verified_mic) {
348	/* This doesn't make sense, we've already verified it? */
349	*minor_status = 0;
350	return GSS_S_DUPLICATE_TOKEN;
351    }
352
353    if (mechListMIC == NULL) {
354	*minor_status = 0;
355	return GSS_S_DEFECTIVE_TOKEN;
356    }
357
358    mic_buf.length = mechListMIC->length;
359    mic_buf.value  = mechListMIC->data;
360
361    ret = gss_verify_mic(minor_status,
362			 context_handle->negotiated_ctx_id,
363			 mech_buf,
364			 &mic_buf,
365			 NULL);
366
367    if (ret != GSS_S_COMPLETE)
368	ret = GSS_S_DEFECTIVE_TOKEN;
369
370    return ret;
371}
372
373static OM_uint32
374select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
375	    gss_OID *mech_p)
376{
377    char mechbuf[64];
378    size_t mech_len;
379    gss_OID_desc oid;
380    gss_OID oidp;
381    gss_OID_set mechs;
382    int i;
383    OM_uint32 ret, junk;
384
385    ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
386		       sizeof(mechbuf),
387		       mechType,
388		       &mech_len);
389    if (ret) {
390	return GSS_S_DEFECTIVE_TOKEN;
391    }
392
393    oid.length   = mech_len;
394    oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
395
396    if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
397	return GSS_S_BAD_MECH;
398    }
399
400    *minor_status = 0;
401
402    /* Translate broken MS Kebreros OID */
403    if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
404	    oidp = &_gss_spnego_krb5_mechanism_oid_desc;
405    else
406	    oidp = &oid;
407
408
409    ret = gss_indicate_mechs(&junk, &mechs);
410    if (ret)
411	    return (ret);
412
413    for (i = 0; i < mechs->count; i++)
414	    if (gss_oid_equal(&mechs->elements[i], oidp))
415		    break;
416
417    if (i == mechs->count) {
418	    gss_release_oid_set(&junk, &mechs);
419	    return GSS_S_BAD_MECH;
420    }
421    gss_release_oid_set(&junk, &mechs);
422
423    ret = gss_duplicate_oid(minor_status,
424			    &oid, /* possibly this should be oidp */
425			    mech_p);
426
427    if (verify_p) {
428	gss_name_t name = GSS_C_NO_NAME;
429	gss_buffer_desc namebuf;
430	char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
431
432	host = getenv("GSSAPI_SPNEGO_NAME");
433	if (host == NULL || issuid()) {
434	    if (gethostname(hostname, sizeof(hostname)) != 0) {
435		*minor_status = errno;
436		return GSS_S_FAILURE;
437	    }
438	    asprintf(&str, "host@%s", hostname);
439	    host = str;
440	}
441
442	namebuf.length = strlen(host);
443	namebuf.value = host;
444
445	ret = gss_import_name(minor_status, &namebuf,
446			      GSS_C_NT_HOSTBASED_SERVICE, &name);
447	if (str)
448	    free(str);
449	if (ret != GSS_S_COMPLETE)
450	    return ret;
451
452	ret = acceptor_approved(name, *mech_p);
453	gss_release_name(&junk, &name);
454    }
455
456    return ret;
457}
458
459
460static OM_uint32
461acceptor_complete(OM_uint32 * minor_status,
462		  gssspnego_ctx ctx,
463		  int *get_mic,
464		  gss_buffer_t mech_buf,
465		  gss_buffer_t mech_input_token,
466		  gss_buffer_t mech_output_token,
467		  heim_octet_string *mic,
468		  gss_buffer_t output_token)
469{
470    OM_uint32 ret;
471    int require_mic, verify_mic;
472    gss_buffer_desc buf;
473
474    buf.length = 0;
475    buf.value = NULL;
476
477    ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
478    if (ret)
479	return ret;
480
481    ctx->require_mic = require_mic;
482
483    if (mic != NULL)
484	require_mic = 1;
485
486    if (ctx->open && require_mic) {
487	if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
488	    verify_mic = 1;
489	    *get_mic = 0;
490	} else if (mech_output_token != GSS_C_NO_BUFFER &&
491		   mech_output_token->length == 0) { /* Odd */
492	    *get_mic = verify_mic = 1;
493	} else { /* Even/One */
494	    verify_mic = 0;
495	    *get_mic = 1;
496	}
497
498	if (verify_mic || get_mic) {
499	    int eret;
500	    size_t buf_len;
501
502	    ASN1_MALLOC_ENCODE(MechTypeList,
503			       mech_buf->value, mech_buf->length,
504			       &ctx->initiator_mech_types, &buf_len, eret);
505	    if (eret) {
506		*minor_status = eret;
507		return GSS_S_FAILURE;
508	    }
509	    if (buf.length != buf_len)
510		abort();
511	}
512
513	if (verify_mic) {
514	    ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
515	    if (ret) {
516		if (get_mic)
517		    send_reject (minor_status, output_token);
518		if (buf.value)
519		    free(buf.value);
520		return ret;
521	    }
522	    ctx->verified_mic = 1;
523	}
524	if (buf.value)
525	    free(buf.value);
526
527    } else
528	*get_mic = verify_mic = 0;
529
530    return GSS_S_COMPLETE;
531}
532
533
534static OM_uint32
535acceptor_start
536	   (OM_uint32 * minor_status,
537	    gss_ctx_id_t * context_handle,
538	    const gss_cred_id_t acceptor_cred_handle,
539	    const gss_buffer_t input_token_buffer,
540	    const gss_channel_bindings_t input_chan_bindings,
541	    gss_name_t * src_name,
542	    gss_OID * mech_type,
543	    gss_buffer_t output_token,
544	    OM_uint32 * ret_flags,
545	    OM_uint32 * time_rec,
546	    gss_cred_id_t *delegated_cred_handle
547	   )
548{
549    OM_uint32 ret, junk, minor;
550    NegotiationToken nt;
551    size_t nt_len;
552    NegTokenInit *ni;
553    int i;
554    gss_buffer_desc data;
555    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
556    gss_buffer_desc mech_output_token;
557    gss_buffer_desc mech_buf;
558    gss_OID preferred_mech_type = GSS_C_NO_OID;
559    gssspnego_ctx ctx;
560    gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
561    int get_mic = 0;
562    int first_ok = 0;
563
564    mech_output_token.value = NULL;
565    mech_output_token.length = 0;
566    mech_buf.value = NULL;
567
568    if (input_token_buffer->length == 0)
569	return send_supported_mechs (minor_status, output_token);
570
571    ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
572    if (ret != GSS_S_COMPLETE)
573	return ret;
574
575    ctx = (gssspnego_ctx)*context_handle;
576
577    /*
578     * The GSS-API encapsulation is only present on the initial
579     * context token (negTokenInit).
580     */
581    ret = gss_decapsulate_token (input_token_buffer,
582				 GSS_SPNEGO_MECHANISM,
583				 &data);
584    if (ret)
585	return ret;
586
587    ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
588    gss_release_buffer(minor_status, &data);
589    if (ret) {
590	*minor_status = ret;
591	return GSS_S_DEFECTIVE_TOKEN;
592    }
593    if (nt.element != choice_NegotiationToken_negTokenInit) {
594	*minor_status = 0;
595	return GSS_S_DEFECTIVE_TOKEN;
596    }
597    ni = &nt.u.negTokenInit;
598
599    if (ni->mechTypes.len < 1) {
600	free_NegotiationToken(&nt);
601	*minor_status = 0;
602	return GSS_S_DEFECTIVE_TOKEN;
603    }
604
605    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
606
607    ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
608    if (ret) {
609	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
610	free_NegotiationToken(&nt);
611	*minor_status = ret;
612	return GSS_S_FAILURE;
613    }
614
615    /*
616     * First we try the opportunistic token if we have support for it,
617     * don't try to verify we have credential for the token,
618     * gss_accept_sec_context will (hopefully) tell us that.
619     * If that failes,
620     */
621
622    ret = select_mech(minor_status,
623		      &ni->mechTypes.val[0],
624		      0,
625		      &preferred_mech_type);
626
627    if (ret == 0 && ni->mechToken != NULL) {
628	gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
629	gss_cred_id_t mech_cred;
630	gss_buffer_desc ibuf;
631
632	ibuf.length = ni->mechToken->length;
633	ibuf.value = ni->mechToken->data;
634	mech_input_token = &ibuf;
635
636	if (acceptor_cred != NULL)
637	    mech_cred = acceptor_cred->negotiated_cred_id;
638	else
639	    mech_cred = GSS_C_NO_CREDENTIAL;
640
641	if (ctx->mech_src_name != GSS_C_NO_NAME)
642	    gss_release_name(&minor, &ctx->mech_src_name);
643
644	if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
645	    _gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
646
647	ret = gss_accept_sec_context(&minor,
648				     &ctx->negotiated_ctx_id,
649				     mech_cred,
650				     mech_input_token,
651				     input_chan_bindings,
652				     &ctx->mech_src_name,
653				     &ctx->negotiated_mech_type,
654				     &mech_output_token,
655				     &ctx->mech_flags,
656				     &ctx->mech_time_rec,
657				     &mech_delegated_cred);
658	if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
659	    ctx->preferred_mech_type = preferred_mech_type;
660	    ctx->negotiated_mech_type = preferred_mech_type;
661	    if (ret == GSS_S_COMPLETE)
662		ctx->open = 1;
663
664	    if (mech_delegated_cred && delegated_cred_handle)
665		ret = _gss_spnego_alloc_cred(minor_status,
666					     mech_delegated_cred,
667					     delegated_cred_handle);
668	    else
669		gss_release_cred(&junk, &mech_delegated_cred);
670
671	    ret = acceptor_complete(minor_status,
672				    ctx,
673				    &get_mic,
674				    &mech_buf,
675				    mech_input_token,
676				    &mech_output_token,
677				    ni->mechListMIC,
678				    output_token);
679	    if (ret != GSS_S_COMPLETE)
680		goto out;
681
682	    first_ok = 1;
683	}
684    }
685
686    /*
687     * If opportunistic token failed, lets try the other mechs.
688     */
689
690    if (!first_ok) {
691
692	/* Call glue layer to find first mech we support */
693	for (i = 1; i < ni->mechTypes.len; ++i) {
694	    ret = select_mech(minor_status,
695			      &ni->mechTypes.val[i],
696			      1,
697			      &preferred_mech_type);
698	    if (ret == 0)
699		break;
700	}
701	if (preferred_mech_type == GSS_C_NO_OID) {
702	    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
703	    free_NegotiationToken(&nt);
704	    return GSS_S_BAD_MECH;
705	}
706
707	ctx->preferred_mech_type = preferred_mech_type;
708	ctx->negotiated_mech_type = preferred_mech_type;
709    }
710
711    /*
712     * The initial token always have a response
713     */
714
715    ret = send_accept (minor_status,
716		       ctx,
717		       &mech_output_token,
718		       1,
719		       get_mic ? &mech_buf : NULL,
720		       output_token);
721    if (ret)
722	goto out;
723
724out:
725    if (mech_output_token.value != NULL)
726	gss_release_buffer(&minor, &mech_output_token);
727    if (mech_buf.value != NULL) {
728	free(mech_buf.value);
729	mech_buf.value = NULL;
730    }
731    free_NegotiationToken(&nt);
732
733
734    if (ret == GSS_S_COMPLETE) {
735	if (src_name != NULL && ctx->mech_src_name != NULL) {
736	    spnego_name name;
737
738	    name = calloc(1, sizeof(*name));
739	    if (name) {
740		name->mech = ctx->mech_src_name;
741		ctx->mech_src_name = NULL;
742		*src_name = (gss_name_t)name;
743	    }
744	}
745        if (delegated_cred_handle != NULL) {
746	    *delegated_cred_handle = ctx->delegated_cred_id;
747	    ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
748	}
749    }
750
751    if (mech_type != NULL)
752	*mech_type = ctx->negotiated_mech_type;
753    if (ret_flags != NULL)
754	*ret_flags = ctx->mech_flags;
755    if (time_rec != NULL)
756	*time_rec = ctx->mech_time_rec;
757
758    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
759	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
760 	return ret;
761    }
762
763    _gss_spnego_internal_delete_sec_context(&minor, context_handle,
764					    GSS_C_NO_BUFFER);
765
766    return ret;
767}
768
769
770static OM_uint32
771acceptor_continue
772	   (OM_uint32 * minor_status,
773	    gss_ctx_id_t * context_handle,
774	    const gss_cred_id_t acceptor_cred_handle,
775	    const gss_buffer_t input_token_buffer,
776	    const gss_channel_bindings_t input_chan_bindings,
777	    gss_name_t * src_name,
778	    gss_OID * mech_type,
779	    gss_buffer_t output_token,
780	    OM_uint32 * ret_flags,
781	    OM_uint32 * time_rec,
782	    gss_cred_id_t *delegated_cred_handle
783	   )
784{
785    OM_uint32 ret, ret2, minor;
786    NegotiationToken nt;
787    size_t nt_len;
788    NegTokenResp *na;
789    unsigned int negResult = accept_incomplete;
790    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
791    gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
792    gss_buffer_desc mech_buf;
793    gssspnego_ctx ctx;
794    gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
795
796    mech_buf.value = NULL;
797
798    ctx = (gssspnego_ctx)*context_handle;
799
800    /*
801     * The GSS-API encapsulation is only present on the initial
802     * context token (negTokenInit).
803     */
804
805    ret = decode_NegotiationToken(input_token_buffer->value,
806				  input_token_buffer->length,
807				  &nt, &nt_len);
808    if (ret) {
809	*minor_status = ret;
810	return GSS_S_DEFECTIVE_TOKEN;
811    }
812    if (nt.element != choice_NegotiationToken_negTokenResp) {
813	*minor_status = 0;
814	return GSS_S_DEFECTIVE_TOKEN;
815    }
816    na = &nt.u.negTokenResp;
817
818    if (na->negResult != NULL) {
819	negResult = *(na->negResult);
820    }
821
822    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
823
824    {
825	gss_buffer_desc ibuf, obuf;
826	int require_mic, get_mic = 0;
827	int require_response;
828	heim_octet_string *mic;
829
830	if (na->responseToken != NULL) {
831	    ibuf.length = na->responseToken->length;
832	    ibuf.value = na->responseToken->data;
833	    mech_input_token = &ibuf;
834	} else {
835	    ibuf.value = NULL;
836	    ibuf.length = 0;
837	}
838
839	if (mech_input_token != GSS_C_NO_BUFFER) {
840	    gss_cred_id_t mech_cred;
841	    gss_cred_id_t mech_delegated_cred;
842	    gss_cred_id_t *mech_delegated_cred_p;
843
844	    if (acceptor_cred != NULL)
845		mech_cred = acceptor_cred->negotiated_cred_id;
846	    else
847		mech_cred = GSS_C_NO_CREDENTIAL;
848
849	    if (delegated_cred_handle != NULL) {
850		mech_delegated_cred = GSS_C_NO_CREDENTIAL;
851		mech_delegated_cred_p = &mech_delegated_cred;
852	    } else {
853		mech_delegated_cred_p = NULL;
854	    }
855
856	    if (ctx->mech_src_name != GSS_C_NO_NAME)
857		gss_release_name(&minor, &ctx->mech_src_name);
858
859	    if (ctx->delegated_cred_id != GSS_C_NO_CREDENTIAL)
860		_gss_spnego_release_cred(&minor, &ctx->delegated_cred_id);
861
862	    ret = gss_accept_sec_context(&minor,
863					 &ctx->negotiated_ctx_id,
864					 mech_cred,
865					 mech_input_token,
866					 input_chan_bindings,
867					 &ctx->mech_src_name,
868					 &ctx->negotiated_mech_type,
869					 &obuf,
870					 &ctx->mech_flags,
871					 &ctx->mech_time_rec,
872					 mech_delegated_cred_p);
873	    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
874		if (mech_delegated_cred_p != NULL &&
875		    mech_delegated_cred != GSS_C_NO_CREDENTIAL) {
876		    ret2 = _gss_spnego_alloc_cred(minor_status,
877						  mech_delegated_cred,
878						  &ctx->delegated_cred_id);
879		    if (ret2 != GSS_S_COMPLETE)
880			ret = ret2;
881		}
882		mech_output_token = &obuf;
883	    }
884	    if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
885		free_NegotiationToken(&nt);
886		send_reject (minor_status, output_token);
887		HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
888		return ret;
889	    }
890	    if (ret == GSS_S_COMPLETE)
891		ctx->open = 1;
892	} else
893	    ret = GSS_S_COMPLETE;
894
895	ret2 = _gss_spnego_require_mechlist_mic(minor_status,
896						ctx,
897						&require_mic);
898	if (ret2)
899	    goto out;
900
901	ctx->require_mic = require_mic;
902
903	mic = na->mechListMIC;
904	if (mic != NULL)
905	    require_mic = 1;
906
907	if (ret == GSS_S_COMPLETE)
908	    ret = acceptor_complete(minor_status,
909				    ctx,
910				    &get_mic,
911				    &mech_buf,
912				    mech_input_token,
913				    mech_output_token,
914				    na->mechListMIC,
915				    output_token);
916
917	if (ctx->mech_flags & GSS_C_DCE_STYLE)
918	    require_response = (negResult != accept_completed);
919	else
920	    require_response = 0;
921
922	/*
923	 * Check whether we need to send a result: there should be only
924	 * one accept_completed response sent in the entire negotiation
925	 */
926	if ((mech_output_token != GSS_C_NO_BUFFER &&
927	     mech_output_token->length != 0)
928	    || (ctx->open && negResult == accept_incomplete)
929	    || require_response
930	    || get_mic) {
931	    ret2 = send_accept (minor_status,
932				ctx,
933				mech_output_token,
934				0,
935				get_mic ? &mech_buf : NULL,
936				output_token);
937	    if (ret2)
938		goto out;
939	}
940
941     out:
942	if (ret2 != GSS_S_COMPLETE)
943	    ret = ret2;
944	if (mech_output_token != NULL)
945	    gss_release_buffer(&minor, mech_output_token);
946	if (mech_buf.value != NULL)
947	    free(mech_buf.value);
948	free_NegotiationToken(&nt);
949    }
950
951    if (ret == GSS_S_COMPLETE) {
952	if (src_name != NULL && ctx->mech_src_name != NULL) {
953	    spnego_name name;
954
955	    name = calloc(1, sizeof(*name));
956	    if (name) {
957		name->mech = ctx->mech_src_name;
958		ctx->mech_src_name = NULL;
959		*src_name = (gss_name_t)name;
960	    }
961	}
962        if (delegated_cred_handle != NULL) {
963	    *delegated_cred_handle = ctx->delegated_cred_id;
964	    ctx->delegated_cred_id = GSS_C_NO_CREDENTIAL;
965	}
966    }
967
968    if (mech_type != NULL)
969	*mech_type = ctx->negotiated_mech_type;
970    if (ret_flags != NULL)
971	*ret_flags = ctx->mech_flags;
972    if (time_rec != NULL)
973	*time_rec = ctx->mech_time_rec;
974
975    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
976	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
977 	return ret;
978    }
979
980    _gss_spnego_internal_delete_sec_context(&minor, context_handle,
981				   GSS_C_NO_BUFFER);
982
983    return ret;
984}
985
986OM_uint32
987_gss_spnego_accept_sec_context
988	   (OM_uint32 * minor_status,
989	    gss_ctx_id_t * context_handle,
990	    const gss_cred_id_t acceptor_cred_handle,
991	    const gss_buffer_t input_token_buffer,
992	    const gss_channel_bindings_t input_chan_bindings,
993	    gss_name_t * src_name,
994	    gss_OID * mech_type,
995	    gss_buffer_t output_token,
996	    OM_uint32 * ret_flags,
997	    OM_uint32 * time_rec,
998	    gss_cred_id_t *delegated_cred_handle
999	   )
1000{
1001    _gss_accept_sec_context_t *func;
1002
1003    *minor_status = 0;
1004
1005    output_token->length = 0;
1006    output_token->value  = NULL;
1007
1008    if (src_name != NULL)
1009	*src_name = GSS_C_NO_NAME;
1010    if (mech_type != NULL)
1011	*mech_type = GSS_C_NO_OID;
1012    if (ret_flags != NULL)
1013	*ret_flags = 0;
1014    if (time_rec != NULL)
1015	*time_rec = 0;
1016    if (delegated_cred_handle != NULL)
1017	*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1018
1019
1020    if (*context_handle == GSS_C_NO_CONTEXT)
1021	func = acceptor_start;
1022    else
1023	func = acceptor_continue;
1024
1025
1026    return (*func)(minor_status, context_handle, acceptor_cred_handle,
1027		   input_token_buffer, input_chan_bindings,
1028		   src_name, mech_type, output_token, ret_flags,
1029		   time_rec, delegated_cred_handle);
1030}
1031