f125573c13722c57699d56556e6855971a8afb51
[samba.git] / source4 / 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 "gsskrb5_locl.h"
35
36 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
37 krb5_keytab _gsskrb5_keytab;
38
39 static krb5_error_code
40 validate_keytab(krb5_context context, const char *name, krb5_keytab *id)
41 {
42     krb5_error_code ret;
43
44     ret = krb5_kt_resolve(context, name, id);
45     if (ret)
46         return ret;
47
48     ret = krb5_kt_have_content(context, *id);
49     if (ret) {
50         krb5_kt_close(context, *id);
51         *id = NULL;
52     }
53
54     return ret;
55 }
56
57 OM_uint32
58 _gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
59 {
60     krb5_context context;
61     krb5_error_code ret;
62
63     *min_stat = 0;
64
65     ret = _gsskrb5_init(&context);
66     if(ret)
67         return GSS_S_FAILURE;
68
69     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
70
71     if(_gsskrb5_keytab != NULL) {
72         krb5_kt_close(context, _gsskrb5_keytab);
73         _gsskrb5_keytab = NULL;
74     }
75     if (identity == NULL) {
76         ret = krb5_kt_default(context, &_gsskrb5_keytab);
77     } else {
78         /*
79          * First check if we can the keytab as is and if it has content...
80          */
81         ret = validate_keytab(context, identity, &_gsskrb5_keytab);
82         /*
83          * if it doesn't, lets prepend FILE: and try again
84          */
85         if (ret) {
86             char *p = NULL;
87             ret = asprintf(&p, "FILE:%s", identity);
88             if(ret < 0 || p == NULL) {
89                 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
90                 return GSS_S_FAILURE;
91             }
92             ret = validate_keytab(context, p, &_gsskrb5_keytab);
93             free(p);
94         }
95     }
96     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
97     if(ret) {
98         *min_stat = ret;
99         return GSS_S_FAILURE;
100     }
101     return GSS_S_COMPLETE;
102 }
103
104 void
105 _gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
106 {
107     krb5_keyblock *key;
108
109     if (acceptor) {
110         if (ctx->auth_context->local_subkey)
111             key = ctx->auth_context->local_subkey;
112         else
113             key = ctx->auth_context->remote_subkey;
114     } else {
115         if (ctx->auth_context->remote_subkey)
116             key = ctx->auth_context->remote_subkey;
117         else
118             key = ctx->auth_context->local_subkey;
119     }
120     if (key == NULL)
121         key = ctx->auth_context->keyblock;
122
123     if (key == NULL)
124         return;
125
126     switch (key->keytype) {
127     case ETYPE_DES_CBC_CRC:
128     case ETYPE_DES_CBC_MD4:
129     case ETYPE_DES_CBC_MD5:
130     case ETYPE_DES3_CBC_MD5:
131     case ETYPE_OLD_DES3_CBC_SHA1:
132     case ETYPE_DES3_CBC_SHA1:
133     case ETYPE_ARCFOUR_HMAC_MD5:
134     case ETYPE_ARCFOUR_HMAC_MD5_56:
135         break;
136     default :
137         ctx->more_flags |= IS_CFX;
138
139         if ((acceptor && ctx->auth_context->local_subkey) ||
140             (!acceptor && ctx->auth_context->remote_subkey))
141             ctx->more_flags |= ACCEPTOR_SUBKEY;
142         break;
143     }
144     if (ctx->crypto)
145         krb5_crypto_destroy(context, ctx->crypto);
146     /* XXX We really shouldn't ignore this; will come back to this */
147     (void) krb5_crypto_init(context, key, 0, &ctx->crypto);
148 }
149
150
151 static OM_uint32
152 gsskrb5_accept_delegated_token(OM_uint32 *minor_status,
153                                gsskrb5_ctx ctx,
154                                krb5_context context,
155                                gss_cred_id_t *delegated_cred_handle)
156 {
157     krb5_ccache ccache = NULL;
158     krb5_error_code kret;
159     int32_t ac_flags, ret = GSS_S_COMPLETE;
160
161     *minor_status = 0;
162
163     /* XXX Create a new delegated_cred_handle? */
164     if (delegated_cred_handle == NULL) {
165         ret = GSS_S_COMPLETE;
166         goto out;
167     }
168
169     *delegated_cred_handle = NULL;
170     kret = krb5_cc_resolve(context, "MEMORY:anonymous", &ccache);
171     if (kret) {
172         ctx->flags &= ~GSS_C_DELEG_FLAG;
173         goto out;
174     }
175
176     kret = krb5_cc_initialize(context, ccache, ctx->source);
177     if (kret) {
178         ctx->flags &= ~GSS_C_DELEG_FLAG;
179         goto out;
180     }
181
182     krb5_auth_con_removeflags(context,
183                               ctx->auth_context,
184                               KRB5_AUTH_CONTEXT_DO_TIME,
185                               &ac_flags);
186     kret = krb5_rd_cred2(context,
187                          ctx->auth_context,
188                          ccache,
189                          &ctx->fwd_data);
190     krb5_auth_con_setflags(context,
191                            ctx->auth_context,
192                            ac_flags);
193     if (kret) {
194         ctx->flags &= ~GSS_C_DELEG_FLAG;
195         ret = GSS_S_FAILURE;
196         *minor_status = kret;
197         goto out;
198     }
199
200     if (delegated_cred_handle) {
201         gsskrb5_cred handle;
202
203         ret = _gsskrb5_krb5_import_cred(minor_status,
204                                         &ccache,
205                                         NULL,
206                                         NULL,
207                                         delegated_cred_handle);
208         if (ret != GSS_S_COMPLETE)
209             goto out;
210
211         handle = (gsskrb5_cred) *delegated_cred_handle;
212         handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
213
214         /*
215          * A root TGT is one of the form krbtgt/REALM@SAME-REALM.
216          *
217          * A destination TGT is a root TGT for the same realm as the acceptor
218          * service's realm.
219          *
220          * Normally clients delegate a root TGT for the client's realm.
221          *
222          * In some deployments clients may want to delegate destination TGTs as
223          * a form of constrained delegation: so that the destination service
224          * cannot use the delegated credential to impersonate the client
225          * principal to services in its home realm (due to KDC lineage/transit
226          * checks).  In those deployments there may not even be a route back to
227          * the KDCs of the client's realm, and attempting to use a
228          * non-destination TGT might even lead to timeouts.
229          *
230          * We could simply pretend not to have obtained a credential, except
231          * that a) we don't (yet) have an app name here for the appdefault we
232          * need to check, b) the application really wants to be able to log a
233          * message about the delegated credential being no good.
234          *
235          * Thus we leave it to _gsskrb5_store_cred_into2() to decide what to do
236          * with non-destination TGTs.  To do that, it needs the realm of the
237          * acceptor service, which we record here.
238          */
239         handle->destination_realm =
240             strdup(krb5_principal_get_realm(context, ctx->target));
241         if (handle->destination_realm == NULL) {
242             _gsskrb5_release_cred(minor_status, delegated_cred_handle);
243             *minor_status = krb5_enomem(context);
244             ret = GSS_S_FAILURE;
245             goto out;
246         }
247     }
248
249 out:
250     if (ccache) {
251         /* Don't destroy the default cred cache */
252         if (delegated_cred_handle == NULL)
253             krb5_cc_close(context, ccache);
254         else
255             krb5_cc_destroy(context, ccache);
256     }
257     return ret;
258 }
259
260 static OM_uint32
261 gsskrb5_acceptor_ready(OM_uint32 * minor_status,
262                        gsskrb5_ctx ctx,
263                        krb5_context context,
264                        gss_cred_id_t *delegated_cred_handle)
265 {
266     OM_uint32 ret;
267     int32_t seq_number;
268     int is_cfx = 0;
269
270     krb5_auth_con_getremoteseqnumber (context,
271                                       ctx->auth_context,
272                                       &seq_number);
273
274     _gsskrb5i_is_cfx(context, ctx, 1);
275     is_cfx = (ctx->more_flags & IS_CFX);
276
277     ret = _gssapi_msg_order_create(minor_status,
278                                    &ctx->order,
279                                    _gssapi_msg_order_f(ctx->flags),
280                                    seq_number, 0, is_cfx);
281     if (ret)
282         return ret;
283
284     /*
285      * If requested, set local sequence num to remote sequence if this
286      * isn't a mutual authentication context
287      */
288     if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
289         krb5_auth_con_setlocalseqnumber(context,
290                                         ctx->auth_context,
291                                         seq_number);
292     }
293
294     /*
295      * We should handle the delegation ticket, in case it's there
296      */
297     if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
298         ret = gsskrb5_accept_delegated_token(minor_status,
299                                              ctx,
300                                              context,
301                                              delegated_cred_handle);
302         if (ret != GSS_S_COMPLETE)
303             return ret;
304     } else {
305         /* Well, looks like it wasn't there after all */
306         ctx->flags &= ~GSS_C_DELEG_FLAG;
307     }
308
309     ctx->state = ACCEPTOR_READY;
310     ctx->more_flags |= OPEN;
311
312     return GSS_S_COMPLETE;
313 }
314
315 static OM_uint32
316 send_error_token(OM_uint32 *minor_status,
317                  krb5_context context,
318                  krb5_error_code kret,
319                  krb5_principal server,
320                  krb5_data *indata,
321                  gss_buffer_t output_token)
322 {
323     krb5_principal ap_req_server = NULL;
324     krb5_error_code ret;
325     krb5_data outbuf;
326     /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
327        tells windows to try again with the corrected timestamp. See
328        [MS-KILE] 2.2.1 KERB-ERROR-DATA */
329     krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
330
331     /* build server from request if the acceptor had not selected one */
332     if (server == NULL) {
333         AP_REQ ap_req;
334
335         ret = krb5_decode_ap_req(context, indata, &ap_req);
336         if (ret) {
337             *minor_status = ret;
338             return GSS_S_FAILURE;
339         }
340         ret = _krb5_principalname2krb5_principal(context,
341                                                   &ap_req_server,
342                                                   ap_req.ticket.sname,
343                                                   ap_req.ticket.realm);
344         free_AP_REQ(&ap_req);
345         if (ret) {
346             *minor_status = ret;
347             return GSS_S_FAILURE;
348         }
349         server = ap_req_server;
350     }
351
352     ret = krb5_mk_error(context, kret, NULL, &e_data, NULL,
353                         server, NULL, NULL, &outbuf);
354     if (ap_req_server)
355         krb5_free_principal(context, ap_req_server);
356     if (ret) {
357         *minor_status = ret;
358         return GSS_S_FAILURE;
359     }
360
361     ret = _gsskrb5_encapsulate(minor_status,
362                                &outbuf,
363                                output_token,
364                                "\x03\x00",
365                                GSS_KRB5_MECHANISM);
366     krb5_data_free (&outbuf);
367     if (ret)
368         return ret;
369
370     *minor_status = 0;
371     return GSS_S_CONTINUE_NEEDED;
372 }
373
374
375 static OM_uint32
376 gsskrb5_acceptor_start(OM_uint32 * minor_status,
377                        gsskrb5_ctx ctx,
378                        krb5_context context,
379                        gss_const_cred_id_t acceptor_cred_handle,
380                        const gss_buffer_t input_token_buffer,
381                        const gss_channel_bindings_t input_chan_bindings,
382                        gss_name_t * src_name,
383                        gss_OID * mech_type,
384                        gss_buffer_t output_token,
385                        OM_uint32 * ret_flags,
386                        OM_uint32 * time_rec,
387                        gss_cred_id_t * delegated_cred_handle)
388 {
389     krb5_error_code kret;
390     OM_uint32 ret = GSS_S_COMPLETE;
391     krb5_data indata;
392     krb5_flags ap_options;
393     krb5_keytab keytab = NULL;
394     int is_cfx = 0;
395     int close_kt = 0;
396     const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
397
398     /*
399      * We may, or may not, have an escapsulation.
400      */
401     ret = _gsskrb5_decapsulate (minor_status,
402                                 input_token_buffer,
403                                 &indata,
404                                 "\x01\x00",
405                                 GSS_KRB5_MECHANISM);
406
407     if (ret) {
408         /* Could be a raw AP-REQ (check for APPLICATION tag) */
409         if (input_token_buffer->length == 0 ||
410             ((const uint8_t *)input_token_buffer->value)[0] != 0x6E) {
411             *minor_status = ASN1_MISPLACED_FIELD;
412             return GSS_S_DEFECTIVE_TOKEN;
413         }
414
415         /* Assume that there is no OID wrapping. */
416         indata.length   = input_token_buffer->length;
417         indata.data     = input_token_buffer->value;
418     }
419
420     /*
421      * We need to get our keytab
422      */
423     if (acceptor_cred == NULL) {
424         HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
425         if (_gsskrb5_keytab != NULL) {
426             char *name = NULL;
427             kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
428             if (kret == 0) {
429                 kret = krb5_kt_resolve(context, name, &keytab);
430                 krb5_xfree(name);
431             }
432             if (kret == 0)
433                 close_kt = 1;
434             else
435                 keytab = NULL;
436         }
437         HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
438     } else if (acceptor_cred->keytab != NULL) {
439         keytab = acceptor_cred->keytab;
440     }
441
442     /*
443      * We need to check the ticket and create the AP-REP packet
444      */
445
446     {
447         krb5_rd_req_in_ctx in = NULL;
448         krb5_rd_req_out_ctx out = NULL;
449         krb5_principal server = NULL;
450
451         if (acceptor_cred)
452             server = acceptor_cred->principal;
453
454         kret = krb5_rd_req_in_ctx_alloc(context, &in);
455         if (kret == 0)
456             kret = krb5_rd_req_in_set_keytab(context, in, keytab);
457         if (kret) {
458             if (in)
459                 krb5_rd_req_in_ctx_free(context, in);
460             if (close_kt)
461                 krb5_kt_close(context, keytab);
462             *minor_status = kret;
463             return GSS_S_FAILURE;
464         }
465
466         kret = krb5_rd_req_ctx(context,
467                                &ctx->auth_context,
468                                &indata,
469                                server,
470                                in, &out);
471         krb5_rd_req_in_ctx_free(context, in);
472         if (close_kt)
473             krb5_kt_close(context, keytab);
474         if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
475             /*
476              * No reply in non-MUTUAL mode, but we don't know that its
477              * non-MUTUAL mode yet, thats inside the 8003 checksum, so
478              * lets only send the error token on clock skew, that
479              * limit when send error token for non-MUTUAL.
480              */
481             return send_error_token(minor_status, context, kret,
482                                     server, &indata, output_token);
483         } else if (kret) {
484             *minor_status = kret;
485             return GSS_S_FAILURE;
486         }
487
488         /*
489          * we need to remember some data on the context_handle.
490          */
491         kret = krb5_rd_req_out_get_ap_req_options(context, out,
492                                                   &ap_options);
493         if (kret == 0)
494             kret = krb5_rd_req_out_get_ticket(context, out,
495                                               &ctx->ticket);
496         if (kret == 0)
497             kret = krb5_rd_req_out_get_keyblock(context, out,
498                                                 &ctx->service_keyblock);
499         ctx->endtime = ctx->ticket->ticket.endtime;
500
501         krb5_rd_req_out_ctx_free(context, out);
502         if (kret) {
503             ret = GSS_S_FAILURE;
504             *minor_status = kret;
505             return ret;
506         }
507     }
508
509
510     /*
511      * We need to copy the principal names to the context and the
512      * calling layer.
513      */
514     kret = krb5_copy_principal(context,
515                                ctx->ticket->client,
516                                &ctx->source);
517     if (kret) {
518         ret = GSS_S_FAILURE;
519         *minor_status = kret;
520         return ret;
521     }
522
523     kret = krb5_copy_principal(context,
524                                ctx->ticket->server,
525                                &ctx->target);
526     if (kret) {
527         ret = GSS_S_FAILURE;
528         *minor_status = kret;
529         return ret;
530     }
531
532     /*
533      * We need to setup some compat stuff, this assumes that
534      * context_handle->target is already set.
535      */
536     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
537     if (ret)
538         return ret;
539
540     if (src_name != NULL) {
541         kret = krb5_copy_principal (context,
542                                     ctx->ticket->client,
543                                     (gsskrb5_name*)src_name);
544         if (kret) {
545             ret = GSS_S_FAILURE;
546             *minor_status = kret;
547             return ret;
548         }
549     }
550
551     /*
552      * We need to get the flags out of the 8003 checksum.
553      */
554
555     {
556         krb5_authenticator authenticator;
557
558         kret = krb5_auth_con_getauthenticator(context,
559                                               ctx->auth_context,
560                                               &authenticator);
561         if(kret) {
562             ret = GSS_S_FAILURE;
563             *minor_status = kret;
564             return ret;
565         }
566
567         if (authenticator->cksum != NULL
568             && authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
569             ret = _gsskrb5_verify_8003_checksum(context,
570                                                 minor_status,
571                                                 input_chan_bindings,
572                                                 authenticator,
573                                                 &ctx->flags,
574                                                 &ctx->fwd_data);
575
576             if (ret) {
577                 krb5_free_authenticator(context, &authenticator);
578                 return ret;
579             }
580         } else {
581             if (authenticator->cksum != NULL) {
582                 krb5_crypto crypto;
583
584                 kret = krb5_crypto_init(context,
585                                         ctx->auth_context->keyblock,
586                                         0, &crypto);
587                 if (kret) {
588                     krb5_free_authenticator(context, &authenticator);
589                     ret = GSS_S_FAILURE;
590                     *minor_status = kret;
591                     return ret;
592                 }
593
594                 /*
595                  * Windows accepts Samba3's use of a kerberos, rather than
596                  * GSSAPI checksum here
597                  */
598
599                 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
600                 kret = krb5_verify_checksum(context,
601                                             crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
602                                             authenticator->cksum);
603                 krb5_crypto_destroy(context, crypto);
604
605                 if (kret) {
606                     krb5_free_authenticator(context, &authenticator);
607                     ret = GSS_S_BAD_SIG;
608                     *minor_status = kret;
609                     return ret;
610                 }
611             }
612
613             /*
614              * If there is no checksum or a kerberos checksum (which Windows
615              * and Samba accept), we use the ap_options to guess the mutual
616              * flag.
617              */
618
619             ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
620             if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
621                 ctx->flags |= GSS_C_MUTUAL_FLAG;
622         }
623         krb5_free_authenticator(context, &authenticator);
624     }
625
626     if(ctx->flags & GSS_C_MUTUAL_FLAG) {
627         krb5_data outbuf;
628         int use_subkey = 0;
629
630         _gsskrb5i_is_cfx(context, ctx, 1);
631         is_cfx = (ctx->more_flags & IS_CFX);
632
633         if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
634             use_subkey = 1;
635         } else {
636             krb5_keyblock *rkey;
637
638             /*
639              * If there is a initiator subkey, copy that to acceptor
640              * subkey to match Windows behavior
641              */
642             kret = krb5_auth_con_getremotesubkey(context,
643                                                  ctx->auth_context,
644                                                  &rkey);
645             if (kret == 0) {
646                 kret = krb5_auth_con_setlocalsubkey(context,
647                                                     ctx->auth_context,
648                                                     rkey);
649                 if (kret == 0)
650                     use_subkey = 1;
651             }
652             krb5_free_keyblock(context, rkey);
653         }
654         if (use_subkey) {
655             ctx->more_flags |= ACCEPTOR_SUBKEY;
656             krb5_auth_con_addflags(context, ctx->auth_context,
657                                    KRB5_AUTH_CONTEXT_USE_SUBKEY,
658                                    NULL);
659         }
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         if (IS_DCE_STYLE(ctx)) {
670             output_token->length = outbuf.length;
671             output_token->value = outbuf.data;
672         } else {
673             ret = _gsskrb5_encapsulate(minor_status,
674                                        &outbuf,
675                                        output_token,
676                                        "\x02\x00",
677                                        GSS_KRB5_MECHANISM);
678             krb5_data_free (&outbuf);
679             if (ret)
680                 return ret;
681         }
682     }
683
684     ctx->flags |= GSS_C_TRANS_FLAG;
685
686     /* Remember the flags */
687
688     ctx->endtime = ctx->ticket->ticket.endtime;
689     ctx->more_flags |= OPEN;
690
691     if (mech_type)
692         *mech_type = GSS_KRB5_MECHANISM;
693
694     if (time_rec) {
695         ret = _gsskrb5_lifetime_left(minor_status,
696                                      context,
697                                      ctx->endtime,
698                                      time_rec);
699         if (ret) {
700             return ret;
701         }
702     }
703
704     /*
705      * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
706      * the client.
707      */
708     if (IS_DCE_STYLE(ctx)) {
709         /*
710          * Return flags to caller, but we haven't processed
711          * delgations yet
712          */
713         if (ret_flags)
714             *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
715
716         ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
717         return GSS_S_CONTINUE_NEEDED;
718     }
719
720     ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
721                                  delegated_cred_handle);
722
723     if (ret_flags)
724         *ret_flags = ctx->flags;
725
726     return ret;
727 }
728
729 static OM_uint32
730 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
731                            gsskrb5_ctx ctx,
732                            krb5_context context,
733                            gss_const_cred_id_t acceptor_cred_handle,
734                            const gss_buffer_t input_token_buffer,
735                            const gss_channel_bindings_t input_chan_bindings,
736                            gss_name_t * src_name,
737                            gss_OID * mech_type,
738                            gss_buffer_t output_token,
739                            OM_uint32 * ret_flags,
740                            OM_uint32 * time_rec,
741                            gss_cred_id_t * delegated_cred_handle)
742 {
743     OM_uint32 ret;
744     krb5_error_code kret;
745     krb5_data inbuf;
746     int32_t r_seq_number, l_seq_number;
747
748     /*
749      * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
750      */
751
752     inbuf.length = input_token_buffer->length;
753     inbuf.data = input_token_buffer->value;
754
755     /*
756      * We need to remeber the old remote seq_number, then check if the
757      * client has replied with our local seq_number, and then reset
758      * the remote seq_number to the old value
759      */
760     {
761         kret = krb5_auth_con_getlocalseqnumber(context,
762                                                ctx->auth_context,
763                                                &l_seq_number);
764         if (kret) {
765             *minor_status = kret;
766             return GSS_S_FAILURE;
767         }
768
769         kret = krb5_auth_con_getremoteseqnumber(context,
770                                                 ctx->auth_context,
771                                                 &r_seq_number);
772         if (kret) {
773             *minor_status = kret;
774             return GSS_S_FAILURE;
775         }
776
777         kret = krb5_auth_con_setremoteseqnumber(context,
778                                                 ctx->auth_context,
779                                                 l_seq_number);
780         if (kret) {
781             *minor_status = kret;
782             return GSS_S_FAILURE;
783         }
784     }
785
786     /*
787      * We need to verify the AP_REP, but we need to flag that this is
788      * DCE_STYLE, so don't check the timestamps this time, but put the
789      * flag DO_TIME back afterward.
790     */
791     {
792         krb5_ap_rep_enc_part *repl;
793         int32_t auth_flags;
794
795         krb5_auth_con_removeflags(context,
796                                   ctx->auth_context,
797                                   KRB5_AUTH_CONTEXT_DO_TIME,
798                                   &auth_flags);
799
800         kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
801         if (kret) {
802             *minor_status = kret;
803             return GSS_S_FAILURE;
804         }
805         krb5_free_ap_rep_enc_part(context, repl);
806         krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
807     }
808
809     /* We need to check the liftime */
810     {
811         OM_uint32 lifetime_rec;
812
813         ret = _gsskrb5_lifetime_left(minor_status,
814                                      context,
815                                      ctx->endtime,
816                                      &lifetime_rec);
817         if (ret) {
818             return ret;
819         }
820         if (lifetime_rec == 0) {
821             return GSS_S_CONTEXT_EXPIRED;
822         }
823
824         if (time_rec) *time_rec = lifetime_rec;
825     }
826
827     /* We need to give the caller the flags which are in use */
828     if (ret_flags) *ret_flags = ctx->flags;
829
830     if (src_name) {
831         kret = krb5_copy_principal(context,
832                                    ctx->source,
833                                    (gsskrb5_name*)src_name);
834         if (kret) {
835             *minor_status = kret;
836             return GSS_S_FAILURE;
837         }
838     }
839
840     /*
841      * After the krb5_rd_rep() the remote and local seq_number should
842      * be the same, because the client just replies the seq_number
843      * from our AP-REP in its AP-REP, but then the client uses the
844      * seq_number from its AP-REQ for GSS_wrap()
845      */
846     {
847         int32_t tmp_r_seq_number, tmp_l_seq_number;
848
849         kret = krb5_auth_con_getremoteseqnumber(context,
850                                                 ctx->auth_context,
851                                                 &tmp_r_seq_number);
852         if (kret) {
853             *minor_status = kret;
854             return GSS_S_FAILURE;
855         }
856
857         kret = krb5_auth_con_getlocalseqnumber(context,
858                                                ctx->auth_context,
859                                                &tmp_l_seq_number);
860         if (kret) {
861
862             *minor_status = kret;
863             return GSS_S_FAILURE;
864         }
865
866         /*
867          * Here we check if the client has responsed with our local seq_number,
868          */
869         if (tmp_r_seq_number != tmp_l_seq_number) {
870             return GSS_S_UNSEQ_TOKEN;
871         }
872     }
873
874     /*
875      * We need to reset the remote seq_number, because the client will use,
876      * the old one for the GSS_wrap() calls
877      */
878     {
879         kret = krb5_auth_con_setremoteseqnumber(context,
880                                                 ctx->auth_context,
881                                                 r_seq_number);
882         if (kret) {
883             *minor_status = kret;
884             return GSS_S_FAILURE;
885         }
886     }
887
888     return gsskrb5_acceptor_ready(minor_status, ctx, context,
889                                   delegated_cred_handle);
890 }
891
892
893 OM_uint32 GSSAPI_CALLCONV
894 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
895                             gss_ctx_id_t * context_handle,
896                             gss_const_cred_id_t acceptor_cred_handle,
897                             const gss_buffer_t input_token_buffer,
898                             const gss_channel_bindings_t input_chan_bindings,
899                             gss_name_t * src_name,
900                             gss_OID * mech_type,
901                             gss_buffer_t output_token,
902                             OM_uint32 * ret_flags,
903                             OM_uint32 * time_rec,
904                             gss_cred_id_t * delegated_cred_handle)
905 {
906     krb5_context context;
907     OM_uint32 ret;
908     gsskrb5_ctx ctx;
909
910     GSSAPI_KRB5_INIT(&context);
911
912     output_token->length = 0;
913     output_token->value = NULL;
914
915     if (src_name != NULL)
916         *src_name = NULL;
917     if (mech_type)
918         *mech_type = GSS_KRB5_MECHANISM;
919
920     if (*context_handle == GSS_C_NO_CONTEXT) {
921         ret = _gsskrb5_create_ctx(minor_status,
922                                   context_handle,
923                                   context,
924                                   input_chan_bindings,
925                                   ACCEPTOR_START);
926         if (ret)
927             return ret;
928     }
929
930     ctx = (gsskrb5_ctx)*context_handle;
931
932
933     /*
934      * TODO: check the channel_bindings
935      * (above just sets them to krb5 layer)
936      */
937
938     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
939
940     switch (ctx->state) {
941     case ACCEPTOR_START:
942         ret = gsskrb5_acceptor_start(minor_status,
943                                      ctx,
944                                      context,
945                                      acceptor_cred_handle,
946                                      input_token_buffer,
947                                      input_chan_bindings,
948                                      src_name,
949                                      mech_type,
950                                      output_token,
951                                      ret_flags,
952                                      time_rec,
953                                      delegated_cred_handle);
954         break;
955     case ACCEPTOR_WAIT_FOR_DCESTYLE:
956         ret = acceptor_wait_for_dcestyle(minor_status,
957                                          ctx,
958                                          context,
959                                          acceptor_cred_handle,
960                                          input_token_buffer,
961                                          input_chan_bindings,
962                                          src_name,
963                                          mech_type,
964                                          output_token,
965                                          ret_flags,
966                                          time_rec,
967                                          delegated_cred_handle);
968         break;
969     case ACCEPTOR_READY:
970         /*
971          * If we get there, the caller have called
972          * gss_accept_sec_context() one time too many.
973          */
974         ret =  GSS_S_BAD_STATUS;
975         break;
976     default:
977         /* TODO: is this correct here? --metze */
978         ret =  GSS_S_BAD_STATUS;
979         break;
980     }
981
982     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
983
984     if (GSS_ERROR(ret)) {
985         OM_uint32 min2;
986         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
987     }
988
989     return ret;
990 }