SMB2 signing now works. The spec was wrong (and will be fixed in the
[kai/samba.git] / source / heimdal / lib / gssapi / krb5 / init_sec_context.c
1 /*
2  * Copyright (c) 1997 - 2008 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 22671 2008-03-09 23:57:54Z 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 cred,
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 *kcred = 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 (cred == 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 = cred->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 (cred && cred->enctypes)
404             enctypes = cred->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                             &kcred);
416     if (ret)
417         goto failure;
418
419     ctx->lifetime = kcred->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                          &kcred->session);
438
439     kret = krb5_auth_con_generatelocalsubkey(context, 
440                                              ctx->auth_context,
441                                              &kcred->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 (!kcred->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, kcred, 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     if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
494         flags |= GSS_C_CONF_FLAG;
495         flags |= GSS_C_INTEG_FLAG;
496     }
497     flags |= GSS_C_TRANS_FLAG;
498     
499     if (ret_flags)
500         *ret_flags = flags;
501     ctx->flags = flags;
502     ctx->more_flags |= LOCAL;
503     
504     ret = _gsskrb5_create_8003_checksum (minor_status,
505                                          input_chan_bindings,
506                                          flags,
507                                          &fwd_data,
508                                          &cksum);
509     krb5_data_free (&fwd_data);
510     if (ret)
511         goto failure;
512
513     enctype = ctx->auth_context->keyblock->keytype;
514
515     kret = krb5_build_authenticator (context,
516                                      ctx->auth_context,
517                                      enctype,
518                                      kcred,
519                                      &cksum,
520                                      NULL,
521                                      &authenticator,
522                                      KRB5_KU_AP_REQ_AUTH);
523
524     if (kret) {
525         *minor_status = kret;
526         ret = GSS_S_FAILURE;
527         goto failure;
528     }
529
530     kret = krb5_build_ap_req (context,
531                               enctype,
532                               kcred,
533                               ap_options,
534                               authenticator,
535                               &outbuf);
536
537     if (kret) {
538         *minor_status = kret;
539         ret = GSS_S_FAILURE;
540         goto failure;
541     }
542
543     ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
544                                    (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
545     if (ret)
546         goto failure;
547
548     krb5_data_free (&outbuf);
549     krb5_free_creds(context, kcred);
550     free_Checksum(&cksum);
551     if (cred == NULL)
552         krb5_cc_close(context, ccache);
553
554     if (flags & GSS_C_MUTUAL_FLAG) {
555         ctx->state = INITIATOR_WAIT_FOR_MUTAL;
556         return GSS_S_CONTINUE_NEEDED;
557     }
558
559     return gsskrb5_initiator_ready(minor_status, ctx, context);
560 failure:
561     if(kcred)
562         krb5_free_creds(context, kcred);
563     if (ccache && cred == NULL)
564         krb5_cc_close(context, ccache);
565
566     return ret;
567
568 }
569
570 static OM_uint32
571 repl_mutual
572 (OM_uint32 * minor_status,
573  gsskrb5_ctx ctx,
574  krb5_context context,
575  const gss_OID mech_type,
576  OM_uint32 req_flags,
577  OM_uint32 time_req,
578  const gss_channel_bindings_t input_chan_bindings,
579  const gss_buffer_t input_token,
580  gss_OID * actual_mech_type,
581  gss_buffer_t output_token,
582  OM_uint32 * ret_flags,
583  OM_uint32 * time_rec
584     )
585 {
586     OM_uint32 ret;
587     krb5_error_code kret;
588     krb5_data indata;
589     krb5_ap_rep_enc_part *repl;
590     int is_cfx = 0;
591
592     output_token->length = 0;
593     output_token->value = NULL;
594
595     if (actual_mech_type)
596         *actual_mech_type = GSS_KRB5_MECHANISM;
597
598     if (ctx->flags & GSS_C_DCE_STYLE) {
599         /* There is no OID wrapping. */
600         indata.length   = input_token->length;
601         indata.data     = input_token->value;
602     } else {
603         ret = _gsskrb5_decapsulate (minor_status,
604                                     input_token,
605                                     &indata,
606                                     "\x02\x00",
607                                     GSS_KRB5_MECHANISM);
608         if (ret) {
609             /* XXX - Handle AP_ERROR */
610             return ret;
611         }
612     }
613
614     kret = krb5_rd_rep (context,
615                         ctx->auth_context,
616                         &indata,
617                         &repl);
618     if (kret) {
619         *minor_status = kret;
620         return GSS_S_FAILURE;
621     }
622     krb5_free_ap_rep_enc_part (context,
623                                repl);
624     
625     _gsskrb5i_is_cfx(ctx, &is_cfx);
626     if (is_cfx) {
627         krb5_keyblock *key = NULL;
628
629         kret = krb5_auth_con_getremotesubkey(context,
630                                              ctx->auth_context, 
631                                              &key);
632         if (kret == 0 && key != NULL) {
633             ctx->more_flags |= ACCEPTOR_SUBKEY;
634             krb5_free_keyblock (context, key);
635         }
636     }
637
638
639     *minor_status = 0;
640     if (time_rec) {
641         ret = _gsskrb5_lifetime_left(minor_status,
642                                      context,
643                                      ctx->lifetime,
644                                      time_rec);
645     } else {
646         ret = GSS_S_COMPLETE;
647     }
648     if (ret_flags)
649         *ret_flags = ctx->flags;
650
651     if (req_flags & GSS_C_DCE_STYLE) {
652         int32_t con_flags;
653         krb5_data outbuf;
654
655         /* Do don't do sequence number for the mk-rep */
656         krb5_auth_con_removeflags(context,
657                                   ctx->auth_context,
658                                   KRB5_AUTH_CONTEXT_DO_SEQUENCE,
659                                   &con_flags);
660
661         kret = krb5_mk_rep(context,
662                            ctx->auth_context,
663                            &outbuf);
664         if (kret) {
665             *minor_status = kret;
666             return GSS_S_FAILURE;
667         }
668         
669         output_token->length = outbuf.length;
670         output_token->value  = outbuf.data;
671
672         krb5_auth_con_removeflags(context,
673                                   ctx->auth_context,
674                                   KRB5_AUTH_CONTEXT_DO_SEQUENCE,
675                                   NULL);
676     }
677
678     return gsskrb5_initiator_ready(minor_status, ctx, context);
679 }
680
681 /*
682  * gss_init_sec_context
683  */
684
685 OM_uint32 _gsskrb5_init_sec_context
686 (OM_uint32 * minor_status,
687  const gss_cred_id_t cred_handle,
688  gss_ctx_id_t * context_handle,
689  const gss_name_t target_name,
690  const gss_OID mech_type,
691  OM_uint32 req_flags,
692  OM_uint32 time_req,
693  const gss_channel_bindings_t input_chan_bindings,
694  const gss_buffer_t input_token,
695  gss_OID * actual_mech_type,
696  gss_buffer_t output_token,
697  OM_uint32 * ret_flags,
698  OM_uint32 * time_rec
699     )
700 {
701     krb5_context context;
702     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
703     krb5_const_principal name = (krb5_const_principal)target_name;
704     gsskrb5_ctx ctx;
705     OM_uint32 ret;
706
707     GSSAPI_KRB5_INIT (&context);
708
709     output_token->length = 0;
710     output_token->value  = NULL;
711
712     if (context_handle == NULL) {
713         *minor_status = 0;
714         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
715     }
716
717     if (ret_flags)
718         *ret_flags = 0;
719     if (time_rec)
720         *time_rec = 0;
721
722     if (target_name == GSS_C_NO_NAME) {
723         if (actual_mech_type)
724             *actual_mech_type = GSS_C_NO_OID;
725         *minor_status = 0;
726         return GSS_S_BAD_NAME;
727     }
728
729     if (mech_type != GSS_C_NO_OID && 
730         !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
731         return GSS_S_BAD_MECH;
732
733     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
734         OM_uint32 ret;
735
736         if (*context_handle != GSS_C_NO_CONTEXT) {
737             *minor_status = 0;
738             return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
739         }
740     
741         ret = _gsskrb5_create_ctx(minor_status,
742                                   context_handle,
743                                   context,
744                                   input_chan_bindings,
745                                   INITIATOR_START);
746         if (ret)
747             return ret;
748     }
749
750     if (*context_handle == GSS_C_NO_CONTEXT) {
751         *minor_status = 0;
752         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
753     }
754
755     ctx = (gsskrb5_ctx) *context_handle;
756
757     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
758
759     switch (ctx->state) {
760     case INITIATOR_START:
761         ret = init_auth(minor_status,
762                         cred,
763                         ctx,
764                         context,
765                         name,
766                         mech_type,
767                         req_flags,
768                         time_req,
769                         input_chan_bindings,
770                         input_token,
771                         actual_mech_type,
772                         output_token,
773                         ret_flags,
774                         time_rec);
775         break;
776     case INITIATOR_WAIT_FOR_MUTAL:
777         ret = repl_mutual(minor_status,
778                           ctx,
779                           context,
780                           mech_type,
781                           req_flags,
782                           time_req,
783                           input_chan_bindings,
784                           input_token,
785                           actual_mech_type,
786                           output_token,
787                           ret_flags,
788                           time_rec);
789         break;
790     case INITIATOR_READY:
791         /* 
792          * If we get there, the caller have called
793          * gss_init_sec_context() one time too many.
794          */
795         *minor_status = 0;
796         ret = GSS_S_BAD_STATUS;
797         break;
798     default:
799         *minor_status = 0;
800         ret = GSS_S_BAD_STATUS;
801         break;
802     }
803     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
804
805     /* destroy context in case of error */
806     if (GSS_ERROR(ret)) {
807         OM_uint32 min2;
808         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
809     }
810
811     return ret;
812
813 }