xref: /freebsd/crypto/heimdal/lib/gssapi/krb5/init_sec_context.c (revision 7aa383846770374466b1dcb2cefd71bde9acf463)
1 /*
2  * Copyright (c) 1997 - 2007 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: init_sec_context.c 22071 2007-11-14 20:04:50Z lha $");
37 
38 /*
39  * copy the addresses from `input_chan_bindings' (if any) to
40  * the auth context `ac'
41  */
42 
43 static OM_uint32
44 set_addresses (krb5_context context,
45 	       krb5_auth_context ac,
46 	       const gss_channel_bindings_t input_chan_bindings)
47 {
48     /* Port numbers are expected to be in application_data.value,
49      * initator's port first */
50 
51     krb5_address initiator_addr, acceptor_addr;
52     krb5_error_code kret;
53 
54     if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
55 	|| input_chan_bindings->application_data.length !=
56 	2 * sizeof(ac->local_port))
57 	return 0;
58 
59     memset(&initiator_addr, 0, sizeof(initiator_addr));
60     memset(&acceptor_addr, 0, sizeof(acceptor_addr));
61 
62     ac->local_port =
63 	*(int16_t *) input_chan_bindings->application_data.value;
64 
65     ac->remote_port =
66 	*((int16_t *) input_chan_bindings->application_data.value + 1);
67 
68     kret = _gsskrb5i_address_to_krb5addr(context,
69 					 input_chan_bindings->acceptor_addrtype,
70 					 &input_chan_bindings->acceptor_address,
71 					 ac->remote_port,
72 					 &acceptor_addr);
73     if (kret)
74 	return kret;
75 
76     kret = _gsskrb5i_address_to_krb5addr(context,
77 					 input_chan_bindings->initiator_addrtype,
78 					 &input_chan_bindings->initiator_address,
79 					 ac->local_port,
80 					 &initiator_addr);
81     if (kret) {
82 	krb5_free_address (context, &acceptor_addr);
83 	return kret;
84     }
85 
86     kret = krb5_auth_con_setaddrs(context,
87 				  ac,
88 				  &initiator_addr,  /* local address */
89 				  &acceptor_addr);  /* remote address */
90 
91     krb5_free_address (context, &initiator_addr);
92     krb5_free_address (context, &acceptor_addr);
93 
94 #if 0
95     free(input_chan_bindings->application_data.value);
96     input_chan_bindings->application_data.value = NULL;
97     input_chan_bindings->application_data.length = 0;
98 #endif
99 
100     return kret;
101 }
102 
103 OM_uint32
104 _gsskrb5_create_ctx(
105         OM_uint32 * minor_status,
106 	gss_ctx_id_t * context_handle,
107 	krb5_context context,
108  	const gss_channel_bindings_t input_chan_bindings,
109  	enum gss_ctx_id_t_state state)
110 {
111     krb5_error_code kret;
112     gsskrb5_ctx ctx;
113 
114     *context_handle = NULL;
115 
116     ctx = malloc(sizeof(*ctx));
117     if (ctx == NULL) {
118 	*minor_status = ENOMEM;
119 	return GSS_S_FAILURE;
120     }
121     ctx->auth_context		= NULL;
122     ctx->source			= NULL;
123     ctx->target			= NULL;
124     ctx->state			= state;
125     ctx->flags			= 0;
126     ctx->more_flags		= 0;
127     ctx->service_keyblock	= NULL;
128     ctx->ticket			= NULL;
129     krb5_data_zero(&ctx->fwd_data);
130     ctx->lifetime		= GSS_C_INDEFINITE;
131     ctx->order			= NULL;
132     HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
133 
134     kret = krb5_auth_con_init (context, &ctx->auth_context);
135     if (kret) {
136 	*minor_status = kret;
137 
138 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
139 
140 	return GSS_S_FAILURE;
141     }
142 
143     kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
144     if (kret) {
145 	*minor_status = kret;
146 
147 	HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
148 
149 	krb5_auth_con_free(context, ctx->auth_context);
150 
151 	return GSS_S_BAD_BINDINGS;
152     }
153 
154     /*
155      * We need a sequence number
156      */
157 
158     krb5_auth_con_addflags(context,
159 			   ctx->auth_context,
160 			   KRB5_AUTH_CONTEXT_DO_SEQUENCE |
161 			   KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
162 			   NULL);
163 
164     *context_handle = (gss_ctx_id_t)ctx;
165 
166     return GSS_S_COMPLETE;
167 }
168 
169 
170 static OM_uint32
171 gsskrb5_get_creds(
172         OM_uint32 * minor_status,
173 	krb5_context context,
174 	krb5_ccache ccache,
175 	gsskrb5_ctx ctx,
176 	krb5_const_principal target_name,
177 	OM_uint32 time_req,
178 	OM_uint32 * time_rec,
179 	krb5_creds ** cred)
180 {
181     OM_uint32 ret;
182     krb5_error_code kret;
183     krb5_creds this_cred;
184     OM_uint32 lifetime_rec;
185 
186     *cred = NULL;
187 
188     memset(&this_cred, 0, sizeof(this_cred));
189     this_cred.client = ctx->source;
190     this_cred.server = ctx->target;
191 
192     if (time_req && time_req != GSS_C_INDEFINITE) {
193 	krb5_timestamp ts;
194 
195 	krb5_timeofday (context, &ts);
196 	this_cred.times.endtime = ts + time_req;
197     } else {
198 	this_cred.times.endtime   = 0;
199     }
200 
201     this_cred.session.keytype = KEYTYPE_NULL;
202 
203     kret = krb5_get_credentials(context,
204 				0,
205 				ccache,
206 				&this_cred,
207 				cred);
208     if (kret) {
209 	*minor_status = kret;
210 	return GSS_S_FAILURE;
211     }
212 
213     ctx->lifetime = (*cred)->times.endtime;
214 
215     ret = _gsskrb5_lifetime_left(minor_status, context,
216 				 ctx->lifetime, &lifetime_rec);
217     if (ret) return ret;
218 
219     if (lifetime_rec == 0) {
220 	*minor_status = 0;
221 	return GSS_S_CONTEXT_EXPIRED;
222     }
223 
224     if (time_rec) *time_rec = lifetime_rec;
225 
226     return GSS_S_COMPLETE;
227 }
228 
229 static OM_uint32
230 gsskrb5_initiator_ready(
231 	OM_uint32 * minor_status,
232 	gsskrb5_ctx ctx,
233 	krb5_context context)
234 {
235 	OM_uint32 ret;
236 	int32_t seq_number;
237 	int is_cfx = 0;
238 	OM_uint32 flags = ctx->flags;
239 
240 	krb5_auth_getremoteseqnumber (context,
241 				      ctx->auth_context,
242 				      &seq_number);
243 
244 	_gsskrb5i_is_cfx(ctx, &is_cfx);
245 
246 	ret = _gssapi_msg_order_create(minor_status,
247 				       &ctx->order,
248 				       _gssapi_msg_order_f(flags),
249 				       seq_number, 0, is_cfx);
250 	if (ret) return ret;
251 
252 	ctx->state	= INITIATOR_READY;
253 	ctx->more_flags	|= OPEN;
254 
255 	return GSS_S_COMPLETE;
256 }
257 
258 /*
259  * handle delegated creds in init-sec-context
260  */
261 
262 static void
263 do_delegation (krb5_context context,
264 	       krb5_auth_context ac,
265 	       krb5_ccache ccache,
266 	       krb5_creds *cred,
267 	       krb5_const_principal name,
268 	       krb5_data *fwd_data,
269 	       uint32_t *flags)
270 {
271     krb5_creds creds;
272     KDCOptions fwd_flags;
273     krb5_error_code kret;
274 
275     memset (&creds, 0, sizeof(creds));
276     krb5_data_zero (fwd_data);
277 
278     kret = krb5_cc_get_principal(context, ccache, &creds.client);
279     if (kret)
280 	goto out;
281 
282     kret = krb5_build_principal(context,
283 				&creds.server,
284 				strlen(creds.client->realm),
285 				creds.client->realm,
286 				KRB5_TGS_NAME,
287 				creds.client->realm,
288 				NULL);
289     if (kret)
290 	goto out;
291 
292     creds.times.endtime = 0;
293 
294     memset(&fwd_flags, 0, sizeof(fwd_flags));
295     fwd_flags.forwarded = 1;
296     fwd_flags.forwardable = 1;
297 
298     if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
299 	name->name.name_string.len < 2)
300 	goto out;
301 
302     kret = krb5_get_forwarded_creds(context,
303 				    ac,
304 				    ccache,
305 				    KDCOptions2int(fwd_flags),
306 				    name->name.name_string.val[1],
307 				    &creds,
308 				    fwd_data);
309 
310  out:
311     if (kret)
312 	*flags &= ~GSS_C_DELEG_FLAG;
313     else
314 	*flags |= GSS_C_DELEG_FLAG;
315 
316     if (creds.client)
317 	krb5_free_principal(context, creds.client);
318     if (creds.server)
319 	krb5_free_principal(context, creds.server);
320 }
321 
322 /*
323  * first stage of init-sec-context
324  */
325 
326 static OM_uint32
327 init_auth
328 (OM_uint32 * minor_status,
329  gsskrb5_cred initiator_cred_handle,
330  gsskrb5_ctx ctx,
331  krb5_context context,
332  krb5_const_principal name,
333  const gss_OID mech_type,
334  OM_uint32 req_flags,
335  OM_uint32 time_req,
336  const gss_channel_bindings_t input_chan_bindings,
337  const gss_buffer_t input_token,
338  gss_OID * actual_mech_type,
339  gss_buffer_t output_token,
340  OM_uint32 * ret_flags,
341  OM_uint32 * time_rec
342     )
343 {
344     OM_uint32 ret = GSS_S_FAILURE;
345     krb5_error_code kret;
346     krb5_flags ap_options;
347     krb5_creds *cred = NULL;
348     krb5_data outbuf;
349     krb5_ccache ccache = NULL;
350     uint32_t flags;
351     krb5_data authenticator;
352     Checksum cksum;
353     krb5_enctype enctype;
354     krb5_data fwd_data;
355     OM_uint32 lifetime_rec;
356 
357     krb5_data_zero(&outbuf);
358     krb5_data_zero(&fwd_data);
359 
360     *minor_status = 0;
361 
362     if (actual_mech_type)
363 	*actual_mech_type = GSS_KRB5_MECHANISM;
364 
365     if (initiator_cred_handle == NULL) {
366 	kret = krb5_cc_default (context, &ccache);
367 	if (kret) {
368 	    *minor_status = kret;
369 	    ret = GSS_S_FAILURE;
370 	    goto failure;
371 	}
372     } else
373 	ccache = initiator_cred_handle->ccache;
374 
375     kret = krb5_cc_get_principal (context, ccache, &ctx->source);
376     if (kret) {
377 	*minor_status = kret;
378 	ret = GSS_S_FAILURE;
379 	goto failure;
380     }
381 
382     kret = krb5_copy_principal (context, name, &ctx->target);
383     if (kret) {
384 	*minor_status = kret;
385 	ret = GSS_S_FAILURE;
386 	goto failure;
387     }
388 
389     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
390     if (ret)
391 	goto failure;
392 
393 
394     /*
395      * This is hideous glue for (NFS) clients that wants to limit the
396      * available enctypes to what it can support (encryption in
397      * kernel). If there is no enctypes selected for this credential,
398      * reset it to the default set of enctypes.
399      */
400     {
401 	krb5_enctype *enctypes = NULL;
402 
403 	if (initiator_cred_handle && initiator_cred_handle->enctypes)
404 	    enctypes = initiator_cred_handle->enctypes;
405 	krb5_set_default_in_tkt_etypes(context, enctypes);
406     }
407 
408     ret = gsskrb5_get_creds(minor_status,
409 			    context,
410 			    ccache,
411 			    ctx,
412 			    ctx->target,
413 			    time_req,
414 			    time_rec,
415 			    &cred);
416     if (ret)
417 	goto failure;
418 
419     ctx->lifetime = cred->times.endtime;
420 
421     ret = _gsskrb5_lifetime_left(minor_status,
422 				 context,
423 				 ctx->lifetime,
424 				 &lifetime_rec);
425     if (ret) {
426 	goto failure;
427     }
428 
429     if (lifetime_rec == 0) {
430 	*minor_status = 0;
431 	ret = GSS_S_CONTEXT_EXPIRED;
432 	goto failure;
433     }
434 
435     krb5_auth_con_setkey(context,
436 			 ctx->auth_context,
437 			 &cred->session);
438 
439     kret = krb5_auth_con_generatelocalsubkey(context,
440 					     ctx->auth_context,
441 					     &cred->session);
442     if(kret) {
443 	*minor_status = kret;
444 	ret = GSS_S_FAILURE;
445 	goto failure;
446     }
447 
448     /*
449      * If the credential doesn't have ok-as-delegate, check what local
450      * policy say about ok-as-delegate, default is FALSE that makes
451      * code ignore the KDC setting and follow what the application
452      * requested. If it is TRUE, strip of the GSS_C_DELEG_FLAG if the
453      * KDC doesn't set ok-as-delegate.
454      */
455     if (!cred->flags.b.ok_as_delegate) {
456 	krb5_boolean delegate;
457 
458 	krb5_appdefault_boolean(context,
459 				"gssapi", name->realm,
460 				"ok-as-delegate", FALSE, &delegate);
461 	if (delegate)
462 	    req_flags &= ~GSS_C_DELEG_FLAG;
463     }
464 
465     flags = 0;
466     ap_options = 0;
467     if (req_flags & GSS_C_DELEG_FLAG)
468 	do_delegation (context,
469 		       ctx->auth_context,
470 		       ccache, cred, name, &fwd_data, &flags);
471 
472     if (req_flags & GSS_C_MUTUAL_FLAG) {
473 	flags |= GSS_C_MUTUAL_FLAG;
474 	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
475     }
476 
477     if (req_flags & GSS_C_REPLAY_FLAG)
478 	flags |= GSS_C_REPLAY_FLAG;
479     if (req_flags & GSS_C_SEQUENCE_FLAG)
480 	flags |= GSS_C_SEQUENCE_FLAG;
481     if (req_flags & GSS_C_ANON_FLAG)
482 	;                               /* XXX */
483     if (req_flags & GSS_C_DCE_STYLE) {
484 	/* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
485 	flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
486 	ap_options |= AP_OPTS_MUTUAL_REQUIRED;
487     }
488     if (req_flags & GSS_C_IDENTIFY_FLAG)
489 	flags |= GSS_C_IDENTIFY_FLAG;
490     if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
491 	flags |= GSS_C_EXTENDED_ERROR_FLAG;
492 
493     flags |= GSS_C_CONF_FLAG;
494     flags |= GSS_C_INTEG_FLAG;
495     flags |= GSS_C_TRANS_FLAG;
496 
497     if (ret_flags)
498 	*ret_flags = flags;
499     ctx->flags = flags;
500     ctx->more_flags |= LOCAL;
501 
502     ret = _gsskrb5_create_8003_checksum (minor_status,
503 					 input_chan_bindings,
504 					 flags,
505 					 &fwd_data,
506 					 &cksum);
507     krb5_data_free (&fwd_data);
508     if (ret)
509 	goto failure;
510 
511     enctype = ctx->auth_context->keyblock->keytype;
512 
513     kret = krb5_build_authenticator (context,
514 				     ctx->auth_context,
515 				     enctype,
516 				     cred,
517 				     &cksum,
518 				     NULL,
519 				     &authenticator,
520 				     KRB5_KU_AP_REQ_AUTH);
521 
522     if (kret) {
523 	*minor_status = kret;
524 	ret = GSS_S_FAILURE;
525 	goto failure;
526     }
527 
528     kret = krb5_build_ap_req (context,
529 			      enctype,
530 			      cred,
531 			      ap_options,
532 			      authenticator,
533 			      &outbuf);
534 
535     if (kret) {
536 	*minor_status = kret;
537 	ret = GSS_S_FAILURE;
538 	goto failure;
539     }
540 
541     ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
542 				   (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
543     if (ret)
544 	goto failure;
545 
546     krb5_data_free (&outbuf);
547     krb5_free_creds(context, cred);
548     free_Checksum(&cksum);
549     if (initiator_cred_handle == NULL)
550 	krb5_cc_close(context, ccache);
551 
552     if (flags & GSS_C_MUTUAL_FLAG) {
553 	ctx->state = INITIATOR_WAIT_FOR_MUTAL;
554 	return GSS_S_CONTINUE_NEEDED;
555     }
556 
557     return gsskrb5_initiator_ready(minor_status, ctx, context);
558 failure:
559     if(cred)
560 	krb5_free_creds(context, cred);
561     if (ccache && initiator_cred_handle == NULL)
562 	krb5_cc_close(context, ccache);
563 
564     return ret;
565 
566 }
567 
568 static OM_uint32
569 repl_mutual
570 (OM_uint32 * minor_status,
571  gsskrb5_ctx ctx,
572  krb5_context context,
573  const gss_OID mech_type,
574  OM_uint32 req_flags,
575  OM_uint32 time_req,
576  const gss_channel_bindings_t input_chan_bindings,
577  const gss_buffer_t input_token,
578  gss_OID * actual_mech_type,
579  gss_buffer_t output_token,
580  OM_uint32 * ret_flags,
581  OM_uint32 * time_rec
582     )
583 {
584     OM_uint32 ret;
585     krb5_error_code kret;
586     krb5_data indata;
587     krb5_ap_rep_enc_part *repl;
588     int is_cfx = 0;
589 
590     output_token->length = 0;
591     output_token->value = NULL;
592 
593     if (actual_mech_type)
594 	*actual_mech_type = GSS_KRB5_MECHANISM;
595 
596     if (ctx->flags & GSS_C_DCE_STYLE) {
597 	/* There is no OID wrapping. */
598 	indata.length	= input_token->length;
599 	indata.data	= input_token->value;
600     } else {
601 	ret = _gsskrb5_decapsulate (minor_status,
602 				    input_token,
603 				    &indata,
604 				    "\x02\x00",
605 				    GSS_KRB5_MECHANISM);
606 	if (ret) {
607 	    /* XXX - Handle AP_ERROR */
608 	    return ret;
609 	}
610     }
611 
612     kret = krb5_rd_rep (context,
613 			ctx->auth_context,
614 			&indata,
615 			&repl);
616     if (kret) {
617 	*minor_status = kret;
618 	return GSS_S_FAILURE;
619     }
620     krb5_free_ap_rep_enc_part (context,
621 			       repl);
622 
623     _gsskrb5i_is_cfx(ctx, &is_cfx);
624     if (is_cfx) {
625 	krb5_keyblock *key = NULL;
626 
627 	kret = krb5_auth_con_getremotesubkey(context,
628 					     ctx->auth_context,
629 					     &key);
630 	if (kret == 0 && key != NULL) {
631     	    ctx->more_flags |= ACCEPTOR_SUBKEY;
632 	    krb5_free_keyblock (context, key);
633 	}
634     }
635 
636 
637     *minor_status = 0;
638     if (time_rec) {
639 	ret = _gsskrb5_lifetime_left(minor_status,
640 				     context,
641 				     ctx->lifetime,
642 				     time_rec);
643     } else {
644 	ret = GSS_S_COMPLETE;
645     }
646     if (ret_flags)
647 	*ret_flags = ctx->flags;
648 
649     if (req_flags & GSS_C_DCE_STYLE) {
650 	int32_t con_flags;
651 	krb5_data outbuf;
652 
653 	/* Do don't do sequence number for the mk-rep */
654 	krb5_auth_con_removeflags(context,
655 				  ctx->auth_context,
656 				  KRB5_AUTH_CONTEXT_DO_SEQUENCE,
657 				  &con_flags);
658 
659 	kret = krb5_mk_rep(context,
660 			   ctx->auth_context,
661 			   &outbuf);
662 	if (kret) {
663 	    *minor_status = kret;
664 	    return GSS_S_FAILURE;
665 	}
666 
667 	output_token->length = outbuf.length;
668 	output_token->value  = outbuf.data;
669 
670 	krb5_auth_con_removeflags(context,
671 				  ctx->auth_context,
672 				  KRB5_AUTH_CONTEXT_DO_SEQUENCE,
673 				  NULL);
674     }
675 
676     return gsskrb5_initiator_ready(minor_status, ctx, context);
677 }
678 
679 /*
680  * gss_init_sec_context
681  */
682 
683 OM_uint32 _gsskrb5_init_sec_context
684 (OM_uint32 * minor_status,
685  const gss_cred_id_t initiator_cred_handle,
686  gss_ctx_id_t * context_handle,
687  const gss_name_t target_name,
688  const gss_OID mech_type,
689  OM_uint32 req_flags,
690  OM_uint32 time_req,
691  const gss_channel_bindings_t input_chan_bindings,
692  const gss_buffer_t input_token,
693  gss_OID * actual_mech_type,
694  gss_buffer_t output_token,
695  OM_uint32 * ret_flags,
696  OM_uint32 * time_rec
697     )
698 {
699     krb5_context context;
700     gsskrb5_cred cred = (gsskrb5_cred)initiator_cred_handle;
701     krb5_const_principal name = (krb5_const_principal)target_name;
702     gsskrb5_ctx ctx;
703     OM_uint32 ret;
704 
705     GSSAPI_KRB5_INIT (&context);
706 
707     output_token->length = 0;
708     output_token->value  = NULL;
709 
710     if (context_handle == NULL) {
711 	*minor_status = 0;
712 	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
713     }
714 
715     if (ret_flags)
716 	*ret_flags = 0;
717     if (time_rec)
718 	*time_rec = 0;
719 
720     if (target_name == GSS_C_NO_NAME) {
721 	if (actual_mech_type)
722 	    *actual_mech_type = GSS_C_NO_OID;
723 	*minor_status = 0;
724 	return GSS_S_BAD_NAME;
725     }
726 
727     if (mech_type != GSS_C_NO_OID &&
728 	!gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
729 	return GSS_S_BAD_MECH;
730 
731     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
732 	OM_uint32 ret;
733 
734 	if (*context_handle != GSS_C_NO_CONTEXT) {
735 	    *minor_status = 0;
736 	    return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
737 	}
738 
739 	ret = _gsskrb5_create_ctx(minor_status,
740 				  context_handle,
741 				  context,
742 				  input_chan_bindings,
743 				  INITIATOR_START);
744 	if (ret)
745 	    return ret;
746     }
747 
748     if (*context_handle == GSS_C_NO_CONTEXT) {
749 	*minor_status = 0;
750 	return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
751     }
752 
753     ctx = (gsskrb5_ctx) *context_handle;
754 
755     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
756 
757     switch (ctx->state) {
758     case INITIATOR_START:
759 	ret = init_auth(minor_status,
760 			cred,
761 			ctx,
762 			context,
763 			name,
764 			mech_type,
765 			req_flags,
766 			time_req,
767 			input_chan_bindings,
768 			input_token,
769 			actual_mech_type,
770 			output_token,
771 			ret_flags,
772 			time_rec);
773 	break;
774     case INITIATOR_WAIT_FOR_MUTAL:
775 	ret = repl_mutual(minor_status,
776 			  ctx,
777 			  context,
778 			  mech_type,
779 			  req_flags,
780 			  time_req,
781 			  input_chan_bindings,
782 			  input_token,
783 			  actual_mech_type,
784 			  output_token,
785 			  ret_flags,
786 			  time_rec);
787 	break;
788     case INITIATOR_READY:
789 	/*
790 	 * If we get there, the caller have called
791 	 * gss_init_sec_context() one time too many.
792 	 */
793 	*minor_status = 0;
794 	ret = GSS_S_BAD_STATUS;
795 	break;
796     default:
797 	*minor_status = 0;
798 	ret = GSS_S_BAD_STATUS;
799 	break;
800     }
801     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
802 
803     /* destroy context in case of error */
804     if (GSS_ERROR(ret)) {
805 	OM_uint32 min2;
806 	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
807     }
808 
809     return ret;
810 
811 }
812