Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-test
[tprouty/samba.git] / source4 / 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     if (flags & GSS_C_DCE_STYLE) {
544         output_token->value = outbuf.data;
545         output_token->length = outbuf.length;
546     } else {
547         ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
548                                     (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
549         if (ret)
550             goto failure;
551
552         krb5_data_free (&outbuf);
553     }
554
555     krb5_free_creds(context, kcred);
556     free_Checksum(&cksum);
557     if (cred == NULL)
558         krb5_cc_close(context, ccache);
559
560     if (flags & GSS_C_MUTUAL_FLAG) {
561         ctx->state = INITIATOR_WAIT_FOR_MUTAL;
562         return GSS_S_CONTINUE_NEEDED;
563     }
564
565     return gsskrb5_initiator_ready(minor_status, ctx, context);
566 failure:
567     if(kcred)
568         krb5_free_creds(context, kcred);
569     if (ccache && cred == NULL)
570         krb5_cc_close(context, ccache);
571
572     return ret;
573
574 }
575
576 static OM_uint32
577 repl_mutual
578 (OM_uint32 * minor_status,
579  gsskrb5_ctx ctx,
580  krb5_context context,
581  const gss_OID mech_type,
582  OM_uint32 req_flags,
583  OM_uint32 time_req,
584  const gss_channel_bindings_t input_chan_bindings,
585  const gss_buffer_t input_token,
586  gss_OID * actual_mech_type,
587  gss_buffer_t output_token,
588  OM_uint32 * ret_flags,
589  OM_uint32 * time_rec
590     )
591 {
592     OM_uint32 ret;
593     krb5_error_code kret;
594     krb5_data indata;
595     krb5_ap_rep_enc_part *repl;
596     int is_cfx = 0;
597
598     output_token->length = 0;
599     output_token->value = NULL;
600
601     if (actual_mech_type)
602         *actual_mech_type = GSS_KRB5_MECHANISM;
603
604     if (ctx->flags & GSS_C_DCE_STYLE) {
605         /* There is no OID wrapping. */
606         indata.length   = input_token->length;
607         indata.data     = input_token->value;
608     } else {
609         ret = _gsskrb5_decapsulate (minor_status,
610                                     input_token,
611                                     &indata,
612                                     "\x02\x00",
613                                     GSS_KRB5_MECHANISM);
614         if (ret) {
615             /* XXX - Handle AP_ERROR */
616             return ret;
617         }
618     }
619
620     kret = krb5_rd_rep (context,
621                         ctx->auth_context,
622                         &indata,
623                         &repl);
624     if (kret) {
625         *minor_status = kret;
626         return GSS_S_FAILURE;
627     }
628     krb5_free_ap_rep_enc_part (context,
629                                repl);
630     
631     _gsskrb5i_is_cfx(ctx, &is_cfx);
632     if (is_cfx) {
633         krb5_keyblock *key = NULL;
634
635         kret = krb5_auth_con_getremotesubkey(context,
636                                              ctx->auth_context, 
637                                              &key);
638         if (kret == 0 && key != NULL) {
639             ctx->more_flags |= ACCEPTOR_SUBKEY;
640             krb5_free_keyblock (context, key);
641         }
642     }
643
644
645     *minor_status = 0;
646     if (time_rec) {
647         ret = _gsskrb5_lifetime_left(minor_status,
648                                      context,
649                                      ctx->lifetime,
650                                      time_rec);
651     } else {
652         ret = GSS_S_COMPLETE;
653     }
654     if (ret_flags)
655         *ret_flags = ctx->flags;
656
657     if (req_flags & GSS_C_DCE_STYLE) {
658         int32_t con_flags;
659         krb5_data outbuf;
660
661         /* Do don't do sequence number for the mk-rep */
662         krb5_auth_con_removeflags(context,
663                                   ctx->auth_context,
664                                   KRB5_AUTH_CONTEXT_DO_SEQUENCE,
665                                   &con_flags);
666
667         kret = krb5_mk_rep(context,
668                            ctx->auth_context,
669                            &outbuf);
670         if (kret) {
671             *minor_status = kret;
672             return GSS_S_FAILURE;
673         }
674         
675         output_token->length = outbuf.length;
676         output_token->value  = outbuf.data;
677
678         krb5_auth_con_removeflags(context,
679                                   ctx->auth_context,
680                                   KRB5_AUTH_CONTEXT_DO_SEQUENCE,
681                                   NULL);
682     }
683
684     return gsskrb5_initiator_ready(minor_status, ctx, context);
685 }
686
687 /*
688  * gss_init_sec_context
689  */
690
691 OM_uint32 _gsskrb5_init_sec_context
692 (OM_uint32 * minor_status,
693  const gss_cred_id_t cred_handle,
694  gss_ctx_id_t * context_handle,
695  const gss_name_t target_name,
696  const gss_OID mech_type,
697  OM_uint32 req_flags,
698  OM_uint32 time_req,
699  const gss_channel_bindings_t input_chan_bindings,
700  const gss_buffer_t input_token,
701  gss_OID * actual_mech_type,
702  gss_buffer_t output_token,
703  OM_uint32 * ret_flags,
704  OM_uint32 * time_rec
705     )
706 {
707     krb5_context context;
708     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
709     krb5_const_principal name = (krb5_const_principal)target_name;
710     gsskrb5_ctx ctx;
711     OM_uint32 ret;
712
713     GSSAPI_KRB5_INIT (&context);
714
715     output_token->length = 0;
716     output_token->value  = NULL;
717
718     if (context_handle == NULL) {
719         *minor_status = 0;
720         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
721     }
722
723     if (ret_flags)
724         *ret_flags = 0;
725     if (time_rec)
726         *time_rec = 0;
727
728     if (target_name == GSS_C_NO_NAME) {
729         if (actual_mech_type)
730             *actual_mech_type = GSS_C_NO_OID;
731         *minor_status = 0;
732         return GSS_S_BAD_NAME;
733     }
734
735     if (mech_type != GSS_C_NO_OID && 
736         !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
737         return GSS_S_BAD_MECH;
738
739     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
740         OM_uint32 ret;
741
742         if (*context_handle != GSS_C_NO_CONTEXT) {
743             *minor_status = 0;
744             return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
745         }
746     
747         ret = _gsskrb5_create_ctx(minor_status,
748                                   context_handle,
749                                   context,
750                                   input_chan_bindings,
751                                   INITIATOR_START);
752         if (ret)
753             return ret;
754     }
755
756     if (*context_handle == GSS_C_NO_CONTEXT) {
757         *minor_status = 0;
758         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
759     }
760
761     ctx = (gsskrb5_ctx) *context_handle;
762
763     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
764
765     switch (ctx->state) {
766     case INITIATOR_START:
767         ret = init_auth(minor_status,
768                         cred,
769                         ctx,
770                         context,
771                         name,
772                         mech_type,
773                         req_flags,
774                         time_req,
775                         input_chan_bindings,
776                         input_token,
777                         actual_mech_type,
778                         output_token,
779                         ret_flags,
780                         time_rec);
781         break;
782     case INITIATOR_WAIT_FOR_MUTAL:
783         ret = repl_mutual(minor_status,
784                           ctx,
785                           context,
786                           mech_type,
787                           req_flags,
788                           time_req,
789                           input_chan_bindings,
790                           input_token,
791                           actual_mech_type,
792                           output_token,
793                           ret_flags,
794                           time_rec);
795         break;
796     case INITIATOR_READY:
797         /* 
798          * If we get there, the caller have called
799          * gss_init_sec_context() one time too many.
800          */
801         *minor_status = 0;
802         ret = GSS_S_BAD_STATUS;
803         break;
804     default:
805         *minor_status = 0;
806         ret = GSS_S_BAD_STATUS;
807         break;
808     }
809     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
810
811     /* destroy context in case of error */
812     if (GSS_ERROR(ret)) {
813         OM_uint32 min2;
814         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
815     }
816
817     return ret;
818
819 }