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