xref: /freebsd/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*
2  * Copyright (c) 1997 - 2006 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
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 "krb5/gsskrb5_locl.h"
35 
36 RCSID("$Id: accept_sec_context.c 20199 2007-02-07 22:36:39Z lha $");
37 
38 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
39 krb5_keytab _gsskrb5_keytab;
40 
41 OM_uint32
42 _gsskrb5_register_acceptor_identity (const char *identity)
43 {
44     krb5_context context;
45     krb5_error_code ret;
46 
47     ret = _gsskrb5_init(&context);
48     if(ret)
49 	return GSS_S_FAILURE;
50 
51     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
52 
53     if(_gsskrb5_keytab != NULL) {
54 	krb5_kt_close(context, _gsskrb5_keytab);
55 	_gsskrb5_keytab = NULL;
56     }
57     if (identity == NULL) {
58 	ret = krb5_kt_default(context, &_gsskrb5_keytab);
59     } else {
60 	char *p;
61 
62 	asprintf(&p, "FILE:%s", identity);
63 	if(p == NULL) {
64 	    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
65 	    return GSS_S_FAILURE;
66 	}
67 	ret = krb5_kt_resolve(context, p, &_gsskrb5_keytab);
68 	free(p);
69     }
70     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
71     if(ret)
72 	return GSS_S_FAILURE;
73     return GSS_S_COMPLETE;
74 }
75 
76 void
77 _gsskrb5i_is_cfx(gsskrb5_ctx ctx, int *is_cfx)
78 {
79     krb5_keyblock *key;
80     int acceptor = (ctx->more_flags & LOCAL) == 0;
81 
82     *is_cfx = 0;
83 
84     if (acceptor) {
85 	if (ctx->auth_context->local_subkey)
86 	    key = ctx->auth_context->local_subkey;
87 	else
88 	    key = ctx->auth_context->remote_subkey;
89     } else {
90 	if (ctx->auth_context->remote_subkey)
91 	    key = ctx->auth_context->remote_subkey;
92 	else
93 	    key = ctx->auth_context->local_subkey;
94     }
95     if (key == NULL)
96 	key = ctx->auth_context->keyblock;
97 
98     if (key == NULL)
99 	return;
100 
101     switch (key->keytype) {
102     case ETYPE_DES_CBC_CRC:
103     case ETYPE_DES_CBC_MD4:
104     case ETYPE_DES_CBC_MD5:
105     case ETYPE_DES3_CBC_MD5:
106     case ETYPE_DES3_CBC_SHA1:
107     case ETYPE_ARCFOUR_HMAC_MD5:
108     case ETYPE_ARCFOUR_HMAC_MD5_56:
109 	break;
110     default :
111 	*is_cfx = 1;
112 	if ((acceptor && ctx->auth_context->local_subkey) ||
113 	    (!acceptor && ctx->auth_context->remote_subkey))
114 	    ctx->more_flags |= ACCEPTOR_SUBKEY;
115 	break;
116     }
117 }
118 
119 
120 static OM_uint32
121 gsskrb5_accept_delegated_token
122 (OM_uint32 * minor_status,
123  gsskrb5_ctx ctx,
124  krb5_context context,
125  gss_cred_id_t * delegated_cred_handle
126     )
127 {
128     krb5_ccache ccache = NULL;
129     krb5_error_code kret;
130     int32_t ac_flags, ret = GSS_S_COMPLETE;
131 
132     *minor_status = 0;
133 
134     /* XXX Create a new delegated_cred_handle? */
135     if (delegated_cred_handle == NULL) {
136 	kret = krb5_cc_default (context, &ccache);
137     } else {
138 	*delegated_cred_handle = NULL;
139 	kret = krb5_cc_gen_new (context, &krb5_mcc_ops, &ccache);
140     }
141     if (kret) {
142 	ctx->flags &= ~GSS_C_DELEG_FLAG;
143 	goto out;
144     }
145 
146     kret = krb5_cc_initialize(context, ccache, ctx->source);
147     if (kret) {
148 	ctx->flags &= ~GSS_C_DELEG_FLAG;
149 	goto out;
150     }
151 
152     krb5_auth_con_removeflags(context,
153 			      ctx->auth_context,
154 			      KRB5_AUTH_CONTEXT_DO_TIME,
155 			      &ac_flags);
156     kret = krb5_rd_cred2(context,
157 			 ctx->auth_context,
158 			 ccache,
159 			 &ctx->fwd_data);
160     krb5_auth_con_setflags(context,
161 			   ctx->auth_context,
162 			   ac_flags);
163     if (kret) {
164 	ctx->flags &= ~GSS_C_DELEG_FLAG;
165 	ret = GSS_S_FAILURE;
166 	*minor_status = kret;
167 	goto out;
168     }
169 
170     if (delegated_cred_handle) {
171 	gsskrb5_cred handle;
172 
173 	ret = _gsskrb5_import_cred(minor_status,
174 				   ccache,
175 				   NULL,
176 				   NULL,
177 				   delegated_cred_handle);
178 	if (ret != GSS_S_COMPLETE)
179 	    goto out;
180 
181 	handle = (gsskrb5_cred) *delegated_cred_handle;
182 
183 	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
184 	krb5_cc_close(context, ccache);
185 	ccache = NULL;
186     }
187 
188 out:
189     if (ccache) {
190 	/* Don't destroy the default cred cache */
191 	if (delegated_cred_handle == NULL)
192 	    krb5_cc_close(context, ccache);
193 	else
194 	    krb5_cc_destroy(context, ccache);
195     }
196     return ret;
197 }
198 
199 static OM_uint32
200 gsskrb5_acceptor_ready(OM_uint32 * minor_status,
201 		       gsskrb5_ctx ctx,
202 		       krb5_context context,
203 		       gss_cred_id_t *delegated_cred_handle)
204 {
205     OM_uint32 ret;
206     int32_t seq_number;
207     int is_cfx = 0;
208 
209     krb5_auth_getremoteseqnumber (context,
210 				  ctx->auth_context,
211 				  &seq_number);
212 
213     _gsskrb5i_is_cfx(ctx, &is_cfx);
214 
215     ret = _gssapi_msg_order_create(minor_status,
216 				   &ctx->order,
217 				   _gssapi_msg_order_f(ctx->flags),
218 				   seq_number, 0, is_cfx);
219     if (ret)
220 	return ret;
221 
222     /*
223      * If requested, set local sequence num to remote sequence if this
224      * isn't a mutual authentication context
225      */
226     if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
227 	krb5_auth_con_setlocalseqnumber(context,
228 					ctx->auth_context,
229 					seq_number);
230     }
231 
232     /*
233      * We should handle the delegation ticket, in case it's there
234      */
235     if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
236 	ret = gsskrb5_accept_delegated_token(minor_status,
237 					     ctx,
238 					     context,
239 					     delegated_cred_handle);
240 	if (ret)
241 	    return ret;
242     } else {
243 	/* Well, looks like it wasn't there after all */
244 	ctx->flags &= ~GSS_C_DELEG_FLAG;
245     }
246 
247     ctx->state = ACCEPTOR_READY;
248     ctx->more_flags |= OPEN;
249 
250     return GSS_S_COMPLETE;
251 }
252 
253 static OM_uint32
254 gsskrb5_acceptor_start(OM_uint32 * minor_status,
255 		       gsskrb5_ctx ctx,
256 		       krb5_context context,
257 		       const gss_cred_id_t acceptor_cred_handle,
258 		       const gss_buffer_t input_token_buffer,
259 		       const gss_channel_bindings_t input_chan_bindings,
260 		       gss_name_t * src_name,
261 		       gss_OID * mech_type,
262 		       gss_buffer_t output_token,
263 		       OM_uint32 * ret_flags,
264 		       OM_uint32 * time_rec,
265 		       gss_cred_id_t * delegated_cred_handle)
266 {
267     krb5_error_code kret;
268     OM_uint32 ret = GSS_S_COMPLETE;
269     krb5_data indata;
270     krb5_flags ap_options;
271     krb5_keytab keytab = NULL;
272     int is_cfx = 0;
273     const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
274 
275     /*
276      * We may, or may not, have an escapsulation.
277      */
278     ret = _gsskrb5_decapsulate (minor_status,
279 				input_token_buffer,
280 				&indata,
281 				"\x01\x00",
282 				GSS_KRB5_MECHANISM);
283 
284     if (ret) {
285 	/* Assume that there is no OID wrapping. */
286 	indata.length	= input_token_buffer->length;
287 	indata.data	= input_token_buffer->value;
288     }
289 
290     /*
291      * We need to get our keytab
292      */
293     if (acceptor_cred == NULL) {
294 	if (_gsskrb5_keytab != NULL)
295 	    keytab = _gsskrb5_keytab;
296     } else if (acceptor_cred->keytab != NULL) {
297 	keytab = acceptor_cred->keytab;
298     }
299 
300     /*
301      * We need to check the ticket and create the AP-REP packet
302      */
303 
304     {
305 	krb5_rd_req_in_ctx in = NULL;
306 	krb5_rd_req_out_ctx out = NULL;
307 
308 	kret = krb5_rd_req_in_ctx_alloc(context, &in);
309 	if (kret == 0)
310 	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
311 	if (kret) {
312 	    if (in)
313 		krb5_rd_req_in_ctx_free(context, in);
314 	    ret = GSS_S_FAILURE;
315 	    *minor_status = kret;
316 	    return ret;
317 	}
318 
319 	kret = krb5_rd_req_ctx(context,
320 			       &ctx->auth_context,
321 			       &indata,
322 			       (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) ? NULL : acceptor_cred->principal,
323 			       in, &out);
324 	krb5_rd_req_in_ctx_free(context, in);
325 	if (kret) {
326 	    ret = GSS_S_FAILURE;
327 	    *minor_status = kret;
328 	    return ret;
329 	}
330 
331 	/*
332 	 * We need to remember some data on the context_handle.
333 	 */
334 	kret = krb5_rd_req_out_get_ap_req_options(context, out,
335 						  &ap_options);
336 	if (kret == 0)
337 	    kret = krb5_rd_req_out_get_ticket(context, out,
338 					      &ctx->ticket);
339 	if (kret == 0)
340 	    kret = krb5_rd_req_out_get_keyblock(context, out,
341 						&ctx->service_keyblock);
342 	ctx->lifetime = ctx->ticket->ticket.endtime;
343 
344 	krb5_rd_req_out_ctx_free(context, out);
345 	if (kret) {
346 	    ret = GSS_S_FAILURE;
347 	    *minor_status = kret;
348 	    return ret;
349 	}
350     }
351 
352 
353     /*
354      * We need to copy the principal names to the context and the
355      * calling layer.
356      */
357     kret = krb5_copy_principal(context,
358 			       ctx->ticket->client,
359 			       &ctx->source);
360     if (kret) {
361 	ret = GSS_S_FAILURE;
362 	*minor_status = kret;
363     }
364 
365     kret = krb5_copy_principal(context,
366 			       ctx->ticket->server,
367 			       &ctx->target);
368     if (kret) {
369 	ret = GSS_S_FAILURE;
370 	*minor_status = kret;
371 	return ret;
372     }
373 
374     /*
375      * We need to setup some compat stuff, this assumes that
376      * context_handle->target is already set.
377      */
378     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
379     if (ret)
380 	return ret;
381 
382     if (src_name != NULL) {
383 	kret = krb5_copy_principal (context,
384 				    ctx->ticket->client,
385 				    (gsskrb5_name*)src_name);
386 	if (kret) {
387 	    ret = GSS_S_FAILURE;
388 	    *minor_status = kret;
389 	    return ret;
390 	}
391     }
392 
393     /*
394      * We need to get the flags out of the 8003 checksum.
395      */
396     {
397 	krb5_authenticator authenticator;
398 
399 	kret = krb5_auth_con_getauthenticator(context,
400 					      ctx->auth_context,
401 					      &authenticator);
402 	if(kret) {
403 	    ret = GSS_S_FAILURE;
404 	    *minor_status = kret;
405 	    return ret;
406 	}
407 
408         if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
409             ret = _gsskrb5_verify_8003_checksum(minor_status,
410 						input_chan_bindings,
411 						authenticator->cksum,
412 						&ctx->flags,
413 						&ctx->fwd_data);
414 
415 	    krb5_free_authenticator(context, &authenticator);
416 	    if (ret) {
417 		return ret;
418 	    }
419         } else {
420 	    krb5_crypto crypto;
421 
422 	    kret = krb5_crypto_init(context,
423 				    ctx->auth_context->keyblock,
424 				    0, &crypto);
425 	    if(kret) {
426 		krb5_free_authenticator(context, &authenticator);
427 
428 		ret = GSS_S_FAILURE;
429 		*minor_status = kret;
430 		return ret;
431 	    }
432 
433 	    /*
434 	     * Windows accepts Samba3's use of a kerberos, rather than
435 	     * GSSAPI checksum here
436 	     */
437 
438 	    kret = krb5_verify_checksum(context,
439 					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
440 					authenticator->cksum);
441 	    krb5_free_authenticator(context, &authenticator);
442 	    krb5_crypto_destroy(context, crypto);
443 
444 	    if(kret) {
445 		ret = GSS_S_BAD_SIG;
446 		*minor_status = kret;
447 		return ret;
448 	    }
449 
450 	    /*
451 	     * Samba style get some flags (but not DCE-STYLE)
452 	     */
453 	    ctx->flags =
454 		GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
455         }
456     }
457 
458     if(ctx->flags & GSS_C_MUTUAL_FLAG) {
459 	krb5_data outbuf;
460 
461 	_gsskrb5i_is_cfx(ctx, &is_cfx);
462 
463 	if (is_cfx != 0
464 	    || (ap_options & AP_OPTS_USE_SUBKEY)) {
465 	    kret = krb5_auth_con_addflags(context,
466 					  ctx->auth_context,
467 					  KRB5_AUTH_CONTEXT_USE_SUBKEY,
468 					  NULL);
469 	    ctx->more_flags |= ACCEPTOR_SUBKEY;
470 	}
471 
472 	kret = krb5_mk_rep(context,
473 			   ctx->auth_context,
474 			   &outbuf);
475 	if (kret) {
476 	    *minor_status = kret;
477 	    return GSS_S_FAILURE;
478 	}
479 
480 	if (IS_DCE_STYLE(ctx)) {
481 	    output_token->length = outbuf.length;
482 	    output_token->value = outbuf.data;
483 	} else {
484 	    ret = _gsskrb5_encapsulate(minor_status,
485 				       &outbuf,
486 				       output_token,
487 				       "\x02\x00",
488 				       GSS_KRB5_MECHANISM);
489 	    krb5_data_free (&outbuf);
490 	    if (ret)
491 		return ret;
492 	}
493     }
494 
495     ctx->flags |= GSS_C_TRANS_FLAG;
496 
497     /* Remember the flags */
498 
499     ctx->lifetime = ctx->ticket->ticket.endtime;
500     ctx->more_flags |= OPEN;
501 
502     if (mech_type)
503 	*mech_type = GSS_KRB5_MECHANISM;
504 
505     if (time_rec) {
506 	ret = _gsskrb5_lifetime_left(minor_status,
507 				     context,
508 				     ctx->lifetime,
509 				     time_rec);
510 	if (ret) {
511 	    return ret;
512 	}
513     }
514 
515     /*
516      * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
517      * the client.
518      */
519     if (IS_DCE_STYLE(ctx)) {
520 	/*
521 	 * Return flags to caller, but we haven't processed
522 	 * delgations yet
523 	 */
524 	if (ret_flags)
525 	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
526 
527 	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
528 	return GSS_S_CONTINUE_NEEDED;
529     }
530 
531     ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
532 				 delegated_cred_handle);
533 
534     if (ret_flags)
535 	*ret_flags = ctx->flags;
536 
537     return ret;
538 }
539 
540 static OM_uint32
541 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
542 			   gsskrb5_ctx ctx,
543 			   krb5_context context,
544 			   const gss_cred_id_t acceptor_cred_handle,
545 			   const gss_buffer_t input_token_buffer,
546 			   const gss_channel_bindings_t input_chan_bindings,
547 			   gss_name_t * src_name,
548 			   gss_OID * mech_type,
549 			   gss_buffer_t output_token,
550 			   OM_uint32 * ret_flags,
551 			   OM_uint32 * time_rec,
552 			   gss_cred_id_t * delegated_cred_handle)
553 {
554     OM_uint32 ret;
555     krb5_error_code kret;
556     krb5_data inbuf;
557     int32_t r_seq_number, l_seq_number;
558 
559     /*
560      * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
561      */
562 
563     inbuf.length = input_token_buffer->length;
564     inbuf.data = input_token_buffer->value;
565 
566     /*
567      * We need to remeber the old remote seq_number, then check if the
568      * client has replied with our local seq_number, and then reset
569      * the remote seq_number to the old value
570      */
571     {
572 	kret = krb5_auth_con_getlocalseqnumber(context,
573 					       ctx->auth_context,
574 					       &l_seq_number);
575 	if (kret) {
576 	    *minor_status = kret;
577 	    return GSS_S_FAILURE;
578 	}
579 
580 	kret = krb5_auth_getremoteseqnumber(context,
581 					    ctx->auth_context,
582 					    &r_seq_number);
583 	if (kret) {
584 	    *minor_status = kret;
585 	    return GSS_S_FAILURE;
586 	}
587 
588 	kret = krb5_auth_con_setremoteseqnumber(context,
589 						ctx->auth_context,
590 						l_seq_number);
591 	if (kret) {
592 	    *minor_status = kret;
593 	    return GSS_S_FAILURE;
594 	}
595     }
596 
597     /*
598      * We need to verify the AP_REP, but we need to flag that this is
599      * DCE_STYLE, so don't check the timestamps this time, but put the
600      * flag DO_TIME back afterward.
601     */
602     {
603 	krb5_ap_rep_enc_part *repl;
604 	int32_t auth_flags;
605 
606 	krb5_auth_con_removeflags(context,
607 				  ctx->auth_context,
608 				  KRB5_AUTH_CONTEXT_DO_TIME,
609 				  &auth_flags);
610 
611 	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
612 	if (kret) {
613 	    *minor_status = kret;
614 	    return GSS_S_FAILURE;
615 	}
616 	krb5_free_ap_rep_enc_part(context, repl);
617 	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
618     }
619 
620     /* We need to check the liftime */
621     {
622 	OM_uint32 lifetime_rec;
623 
624 	ret = _gsskrb5_lifetime_left(minor_status,
625 				     context,
626 				     ctx->lifetime,
627 				     &lifetime_rec);
628 	if (ret) {
629 	    return ret;
630 	}
631 	if (lifetime_rec == 0) {
632 	    return GSS_S_CONTEXT_EXPIRED;
633 	}
634 
635 	if (time_rec) *time_rec = lifetime_rec;
636     }
637 
638     /* We need to give the caller the flags which are in use */
639     if (ret_flags) *ret_flags = ctx->flags;
640 
641     if (src_name) {
642 	kret = krb5_copy_principal(context,
643 				   ctx->source,
644 				   (gsskrb5_name*)src_name);
645 	if (kret) {
646 	    *minor_status = kret;
647 	    return GSS_S_FAILURE;
648 	}
649     }
650 
651     /*
652      * After the krb5_rd_rep() the remote and local seq_number should
653      * be the same, because the client just replies the seq_number
654      * from our AP-REP in its AP-REP, but then the client uses the
655      * seq_number from its AP-REQ for GSS_wrap()
656      */
657     {
658 	int32_t tmp_r_seq_number, tmp_l_seq_number;
659 
660 	kret = krb5_auth_getremoteseqnumber(context,
661 					    ctx->auth_context,
662 					    &tmp_r_seq_number);
663 	if (kret) {
664 	    *minor_status = kret;
665 	    return GSS_S_FAILURE;
666 	}
667 
668 	kret = krb5_auth_con_getlocalseqnumber(context,
669 					       ctx->auth_context,
670 					       &tmp_l_seq_number);
671 	if (kret) {
672 
673 	    *minor_status = kret;
674 	    return GSS_S_FAILURE;
675 	}
676 
677 	/*
678 	 * Here we check if the client has responsed with our local seq_number,
679 	 */
680 	if (tmp_r_seq_number != tmp_l_seq_number) {
681 	    return GSS_S_UNSEQ_TOKEN;
682 	}
683     }
684 
685     /*
686      * We need to reset the remote seq_number, because the client will use,
687      * the old one for the GSS_wrap() calls
688      */
689     {
690 	kret = krb5_auth_con_setremoteseqnumber(context,
691 						ctx->auth_context,
692 						r_seq_number);
693 	if (kret) {
694 	    *minor_status = kret;
695 	    return GSS_S_FAILURE;
696 	}
697     }
698 
699     return gsskrb5_acceptor_ready(minor_status, ctx, context,
700 				  delegated_cred_handle);
701 }
702 
703 
704 OM_uint32
705 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
706 			    gss_ctx_id_t * context_handle,
707 			    const gss_cred_id_t acceptor_cred_handle,
708 			    const gss_buffer_t input_token_buffer,
709 			    const gss_channel_bindings_t input_chan_bindings,
710 			    gss_name_t * src_name,
711 			    gss_OID * mech_type,
712 			    gss_buffer_t output_token,
713 			    OM_uint32 * ret_flags,
714 			    OM_uint32 * time_rec,
715 			    gss_cred_id_t * delegated_cred_handle)
716 {
717     krb5_context context;
718     OM_uint32 ret;
719     gsskrb5_ctx ctx;
720 
721     GSSAPI_KRB5_INIT(&context);
722 
723     output_token->length = 0;
724     output_token->value = NULL;
725 
726     if (src_name != NULL)
727 	*src_name = NULL;
728     if (mech_type)
729 	*mech_type = GSS_KRB5_MECHANISM;
730 
731     if (*context_handle == GSS_C_NO_CONTEXT) {
732 	ret = _gsskrb5_create_ctx(minor_status,
733 				  context_handle,
734 				  context,
735 				  input_chan_bindings,
736 				  ACCEPTOR_START);
737 	if (ret)
738 	    return ret;
739     }
740 
741     ctx = (gsskrb5_ctx)*context_handle;
742 
743 
744     /*
745      * TODO: check the channel_bindings
746      * (above just sets them to krb5 layer)
747      */
748 
749     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
750 
751     switch (ctx->state) {
752     case ACCEPTOR_START:
753 	ret = gsskrb5_acceptor_start(minor_status,
754 				     ctx,
755 				     context,
756 				     acceptor_cred_handle,
757 				     input_token_buffer,
758 				     input_chan_bindings,
759 				     src_name,
760 				     mech_type,
761 				     output_token,
762 				     ret_flags,
763 				     time_rec,
764 				     delegated_cred_handle);
765 	break;
766     case ACCEPTOR_WAIT_FOR_DCESTYLE:
767 	ret = acceptor_wait_for_dcestyle(minor_status,
768 					 ctx,
769 					 context,
770 					 acceptor_cred_handle,
771 					 input_token_buffer,
772 					 input_chan_bindings,
773 					 src_name,
774 					 mech_type,
775 					 output_token,
776 					 ret_flags,
777 					 time_rec,
778 					 delegated_cred_handle);
779 	break;
780     case ACCEPTOR_READY:
781 	/*
782 	 * If we get there, the caller have called
783 	 * gss_accept_sec_context() one time too many.
784 	 */
785 	ret =  GSS_S_BAD_STATUS;
786 	break;
787     default:
788 	/* TODO: is this correct here? --metze */
789 	ret =  GSS_S_BAD_STATUS;
790 	break;
791     }
792 
793     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
794 
795     if (GSS_ERROR(ret)) {
796 	OM_uint32 min2;
797 	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
798     }
799 
800     return ret;
801 }
802