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