r23456: Update Samba4 to current lorikeet-heimdal.
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / gssapi / krb5 / init_sec_context.c
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 20326 2007-04-12 16:49:57Z 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 its 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 }