s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[metze/samba/wip.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 "gsskrb5_locl.h"
35
36 RCSID("$Id$");
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->kcred                  = NULL;
125     ctx->ccache                 = NULL;
126     ctx->state                  = state;
127     ctx->flags                  = 0;
128     ctx->more_flags             = 0;
129     ctx->service_keyblock       = NULL;
130     ctx->ticket                 = NULL;
131     krb5_data_zero(&ctx->fwd_data);
132     ctx->lifetime               = GSS_C_INDEFINITE;
133     ctx->order                  = NULL;
134     ctx->crypto                 = NULL;
135     HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
136
137     kret = krb5_auth_con_init (context, &ctx->auth_context);
138     if (kret) {
139         *minor_status = kret;
140         HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
141         return GSS_S_FAILURE;
142     }
143
144     kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
145     if (kret) {
146         *minor_status = kret;
147
148         HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
149
150         krb5_auth_con_free(context, ctx->auth_context);
151
152         return GSS_S_BAD_BINDINGS;
153     }
154
155     /*
156      * We need a sequence number
157      */
158
159     krb5_auth_con_addflags(context,
160                            ctx->auth_context,
161                            KRB5_AUTH_CONTEXT_DO_SEQUENCE |
162                            KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
163                            NULL);
164
165     *context_handle = (gss_ctx_id_t)ctx;
166
167     return GSS_S_COMPLETE;
168 }
169
170
171 static OM_uint32
172 gsskrb5_get_creds(
173         OM_uint32 * minor_status,
174         krb5_context context,
175         krb5_ccache ccache,
176         gsskrb5_ctx ctx,
177         const gss_name_t target_name,
178         int use_dns,
179         OM_uint32 time_req,
180         OM_uint32 * time_rec,
181         krb5_creds ** cred)
182 {
183     OM_uint32 ret;
184     krb5_error_code kret;
185     krb5_creds this_cred;
186     OM_uint32 lifetime_rec;
187
188     *cred = NULL;
189
190     if (ctx->target) {
191         krb5_free_principal(context, ctx->target);
192         ctx->target = NULL;
193     }
194
195     ret = _gsskrb5_canon_name(minor_status, context, use_dns,
196                               target_name, &ctx->target);
197     if (ret)
198         return ret;
199
200     memset(&this_cred, 0, sizeof(this_cred));
201     this_cred.client = ctx->source;
202     this_cred.server = ctx->target;
203
204     if (time_req && time_req != GSS_C_INDEFINITE) {
205         krb5_timestamp ts;
206
207         krb5_timeofday (context, &ts);
208         this_cred.times.endtime = ts + time_req;
209     } else {
210         this_cred.times.endtime   = 0;
211     }
212
213     this_cred.session.keytype = KEYTYPE_NULL;
214
215     kret = krb5_get_credentials(context,
216                                 0,
217                                 ccache,
218                                 &this_cred,
219                                 cred);
220     if (kret) {
221         *minor_status = kret;
222         return GSS_S_FAILURE;
223     }
224
225     ctx->lifetime = (*cred)->times.endtime;
226
227     ret = _gsskrb5_lifetime_left(minor_status, context,
228                                  ctx->lifetime, &lifetime_rec);
229     if (ret) return ret;
230
231     if (lifetime_rec == 0) {
232         *minor_status = 0;
233         return GSS_S_CONTEXT_EXPIRED;
234     }
235
236     if (time_rec) *time_rec = lifetime_rec;
237
238     return GSS_S_COMPLETE;
239 }
240
241 static OM_uint32
242 gsskrb5_initiator_ready(
243         OM_uint32 * minor_status,
244         gsskrb5_ctx ctx,
245         krb5_context context)
246 {
247     OM_uint32 ret;
248     int32_t seq_number;
249     int is_cfx = 0;
250     OM_uint32 flags = ctx->flags;
251
252     krb5_free_creds(context, ctx->kcred);
253     ctx->kcred = NULL;
254
255     if (ctx->more_flags & CLOSE_CCACHE)
256         krb5_cc_close(context, ctx->ccache);
257     ctx->ccache = NULL;
258
259     krb5_auth_getremoteseqnumber (context, ctx->auth_context, &seq_number);
260
261     _gsskrb5i_is_cfx(context, ctx, 0);
262     is_cfx = (ctx->more_flags & IS_CFX);
263
264     ret = _gssapi_msg_order_create(minor_status,
265                                    &ctx->order,
266                                    _gssapi_msg_order_f(flags),
267                                    seq_number, 0, is_cfx);
268     if (ret) return ret;
269
270     ctx->state  = INITIATOR_READY;
271     ctx->more_flags     |= OPEN;
272
273     return GSS_S_COMPLETE;
274 }
275
276 /*
277  * handle delegated creds in init-sec-context
278  */
279
280 static void
281 do_delegation (krb5_context context,
282                krb5_auth_context ac,
283                krb5_ccache ccache,
284                krb5_creds *cred,
285                krb5_const_principal name,
286                krb5_data *fwd_data,
287                uint32_t flagmask,
288                uint32_t *flags)
289 {
290     krb5_creds creds;
291     KDCOptions fwd_flags;
292     krb5_error_code kret;
293
294     memset (&creds, 0, sizeof(creds));
295     krb5_data_zero (fwd_data);
296
297     kret = krb5_cc_get_principal(context, ccache, &creds.client);
298     if (kret)
299         goto out;
300
301     kret = krb5_build_principal(context,
302                                 &creds.server,
303                                 strlen(creds.client->realm),
304                                 creds.client->realm,
305                                 KRB5_TGS_NAME,
306                                 creds.client->realm,
307                                 NULL);
308     if (kret)
309         goto out;
310
311     creds.times.endtime = 0;
312
313     memset(&fwd_flags, 0, sizeof(fwd_flags));
314     fwd_flags.forwarded = 1;
315     fwd_flags.forwardable = 1;
316
317     if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
318         name->name.name_string.len < 2)
319         goto out;
320
321     kret = krb5_get_forwarded_creds(context,
322                                     ac,
323                                     ccache,
324                                     KDCOptions2int(fwd_flags),
325                                     name->name.name_string.val[1],
326                                     &creds,
327                                     fwd_data);
328
329  out:
330     if (kret)
331         *flags &= ~flagmask;
332     else
333         *flags |= flagmask;
334
335     if (creds.client)
336         krb5_free_principal(context, creds.client);
337     if (creds.server)
338         krb5_free_principal(context, creds.server);
339 }
340
341 /*
342  * first stage of init-sec-context
343  */
344
345 static OM_uint32
346 init_auth
347 (OM_uint32 * minor_status,
348  gsskrb5_cred cred,
349  gsskrb5_ctx ctx,
350  krb5_context context,
351  gss_name_t name,
352  const gss_OID mech_type,
353  OM_uint32 req_flags,
354  OM_uint32 time_req,
355  const gss_buffer_t input_token,
356  gss_OID * actual_mech_type,
357  gss_buffer_t output_token,
358  OM_uint32 * ret_flags,
359  OM_uint32 * time_rec
360     )
361 {
362     OM_uint32 ret = GSS_S_FAILURE;
363     krb5_error_code kret;
364     krb5_data outbuf;
365     krb5_data fwd_data;
366     OM_uint32 lifetime_rec;
367     int allow_dns = 1;
368
369     krb5_data_zero(&outbuf);
370     krb5_data_zero(&fwd_data);
371
372     *minor_status = 0;
373
374     if (actual_mech_type)
375         *actual_mech_type = GSS_KRB5_MECHANISM;
376
377     if (cred == NULL) {
378         kret = krb5_cc_default (context, &ctx->ccache);
379         if (kret) {
380             *minor_status = kret;
381             ret = GSS_S_FAILURE;
382             goto failure;
383         }
384         ctx->more_flags |= CLOSE_CCACHE;
385     } else
386         ctx->ccache = cred->ccache;
387
388     kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
389     if (kret) {
390         *minor_status = kret;
391         ret = GSS_S_FAILURE;
392         goto failure;
393     }
394
395     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
396     if (ret)
397         goto failure;
398
399
400     /*
401      * This is hideous glue for (NFS) clients that wants to limit the
402      * available enctypes to what it can support (encryption in
403      * kernel). If there is no enctypes selected for this credential,
404      * reset it to the default set of enctypes.
405      */
406     {
407         krb5_enctype *enctypes = NULL;
408
409         if (cred && cred->enctypes)
410             enctypes = cred->enctypes;
411         krb5_set_default_in_tkt_etypes(context, enctypes);
412     }
413
414     /* canon name if needed for client + target realm */
415     kret = krb5_cc_get_config(context, ctx->ccache, NULL,
416                               "realm-config", &outbuf);
417     if (kret == 0) {
418         /* XXX 2 is no server canon */
419         if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
420             allow_dns = 0;
421         krb5_data_free(&outbuf);
422     }
423
424     /*
425      * First we try w/o dns, hope that the KDC have register alias
426      * (and referrals if cross realm) for this principal. If that
427      * fails and if we are allowed to using this realm try again with
428      * DNS canonicalizion.
429      */
430     ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
431                             ctx, name, 0, time_req, 
432                             time_rec, &ctx->kcred);
433     if (ret && allow_dns)
434         ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
435                                 ctx, name, 1, time_req, 
436                                 time_rec, &ctx->kcred);
437     if (ret)
438         goto failure;
439
440     ctx->lifetime = ctx->kcred->times.endtime;
441
442     ret = _gsskrb5_lifetime_left(minor_status,
443                                  context,
444                                  ctx->lifetime,
445                                  &lifetime_rec);
446     if (ret)
447         goto failure;
448
449     if (lifetime_rec == 0) {
450         *minor_status = 0;
451         ret = GSS_S_CONTEXT_EXPIRED;
452         goto failure;
453     }
454
455     krb5_auth_con_setkey(context,
456                          ctx->auth_context,
457                          &ctx->kcred->session);
458
459     kret = krb5_auth_con_generatelocalsubkey(context,
460                                              ctx->auth_context,
461                                              &ctx->kcred->session);
462     if(kret) {
463         *minor_status = kret;
464         ret = GSS_S_FAILURE;
465         goto failure;
466     }
467
468     return GSS_S_COMPLETE;
469
470 failure:
471     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
472         krb5_cc_close(context, ctx->ccache);
473     ctx->ccache = NULL;
474
475     return ret;
476
477 }
478
479 static OM_uint32
480 init_auth_restart
481 (OM_uint32 * minor_status,
482  gsskrb5_cred cred,
483  gsskrb5_ctx ctx,
484  krb5_context context,
485  OM_uint32 req_flags,
486  const gss_channel_bindings_t input_chan_bindings,
487  const gss_buffer_t input_token,
488  gss_OID * actual_mech_type,
489  gss_buffer_t output_token,
490  OM_uint32 * ret_flags,
491  OM_uint32 * time_rec
492     )
493 {
494     OM_uint32 ret = GSS_S_FAILURE;
495     krb5_error_code kret;
496     krb5_flags ap_options;
497     krb5_data outbuf;
498     uint32_t flags;
499     krb5_data authenticator;
500     Checksum cksum;
501     krb5_enctype enctype;
502     krb5_data fwd_data, timedata;
503     int32_t offset = 0, oldoffset;
504     uint32_t flagmask;
505
506     krb5_data_zero(&outbuf);
507     krb5_data_zero(&fwd_data);
508
509     *minor_status = 0;
510
511     /*
512      * If the credential doesn't have ok-as-delegate, check if there
513      * is a realm setting and use that.
514      */
515     if (!ctx->kcred->flags.b.ok_as_delegate) {
516         krb5_data data;
517         
518         ret = krb5_cc_get_config(context, ctx->ccache, NULL,
519                                  "realm-config", &data);
520         if (ret == 0) {
521             /* XXX 1 is use ok-as-delegate */
522             if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
523                 req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
524             krb5_data_free(&data);
525         }
526     }
527
528     flagmask = 0;
529
530     /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
531     if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
532         && ctx->kcred->flags.b.ok_as_delegate)
533         flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
534     /* if there still is a GSS_C_DELEG_FLAG, use that */
535     if (req_flags & GSS_C_DELEG_FLAG)
536         flagmask |= GSS_C_DELEG_FLAG;
537
538
539     flags = 0;
540     ap_options = 0;
541     if (flagmask & GSS_C_DELEG_FLAG) {
542         do_delegation (context,
543                        ctx->auth_context,
544                        ctx->ccache, ctx->kcred, ctx->target,
545                        &fwd_data, flagmask, &flags);
546     }
547
548     if (req_flags & GSS_C_MUTUAL_FLAG) {
549         flags |= GSS_C_MUTUAL_FLAG;
550         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
551     }
552
553     if (req_flags & GSS_C_REPLAY_FLAG)
554         flags |= GSS_C_REPLAY_FLAG;
555     if (req_flags & GSS_C_SEQUENCE_FLAG)
556         flags |= GSS_C_SEQUENCE_FLAG;
557 #if 0
558     if (req_flags & GSS_C_ANON_FLAG)
559         ;                               /* XXX */
560 #endif
561     if (req_flags & GSS_C_DCE_STYLE) {
562         /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
563         flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
564         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
565     }
566     if (req_flags & GSS_C_IDENTIFY_FLAG)
567         flags |= GSS_C_IDENTIFY_FLAG;
568     if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
569         flags |= GSS_C_EXTENDED_ERROR_FLAG;
570
571     if (req_flags & GSS_C_CONF_FLAG) {
572         flags |= GSS_C_CONF_FLAG;
573     }
574     if (req_flags & GSS_C_INTEG_FLAG) {
575         flags |= GSS_C_INTEG_FLAG;
576     }
577     if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
578         flags |= GSS_C_CONF_FLAG;
579         flags |= GSS_C_INTEG_FLAG;
580     }
581     flags |= GSS_C_TRANS_FLAG;
582
583     if (ret_flags)
584         *ret_flags = flags;
585     ctx->flags = flags;
586     ctx->more_flags |= LOCAL;
587
588     ret = _gsskrb5_create_8003_checksum (minor_status,
589                                          input_chan_bindings,
590                                          flags,
591                                          &fwd_data,
592                                          &cksum);
593     krb5_data_free (&fwd_data);
594     if (ret)
595         goto failure;
596
597     enctype = ctx->auth_context->keyblock->keytype;
598
599     ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
600                              "time-offset", &timedata);
601     if (ret == 0) {
602         if (timedata.length == 4) {
603             const u_char *p = timedata.data;
604             offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
605         }
606         krb5_data_free(&timedata);
607     }
608
609     if (offset) {
610         krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
611         krb5_set_kdc_sec_offset (context, offset, -1);
612     }
613
614     kret = krb5_build_authenticator (context,
615                                      ctx->auth_context,
616                                      enctype,
617                                      ctx->kcred,
618                                      &cksum,
619                                      NULL,
620                                      &authenticator,
621                                      KRB5_KU_AP_REQ_AUTH);
622
623     if (kret) {
624         if (offset)
625             krb5_set_kdc_sec_offset (context, oldoffset, -1);
626         *minor_status = kret;
627         ret = GSS_S_FAILURE;
628         goto failure;
629     }
630
631     kret = krb5_build_ap_req (context,
632                               enctype,
633                               ctx->kcred,
634                               ap_options,
635                               authenticator,
636                               &outbuf);
637     if (offset)
638         krb5_set_kdc_sec_offset (context, oldoffset, -1);
639     if (kret) {
640         *minor_status = kret;
641         ret = GSS_S_FAILURE;
642         goto failure;
643     }
644
645     if (flags & GSS_C_DCE_STYLE) {
646         output_token->value = outbuf.data;
647         output_token->length = outbuf.length;
648     } else {
649         ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
650                                     (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
651         krb5_data_free (&outbuf);
652         if (ret)
653             goto failure;
654     }
655
656     free_Checksum(&cksum);
657
658     if (flags & GSS_C_MUTUAL_FLAG) {
659         ctx->state = INITIATOR_WAIT_FOR_MUTAL;
660         return GSS_S_CONTINUE_NEEDED;
661     }
662
663     return gsskrb5_initiator_ready(minor_status, ctx, context);
664 failure:
665     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
666         krb5_cc_close(context, ctx->ccache);
667     ctx->ccache = NULL;
668
669     return ret;
670 }
671
672
673 static OM_uint32
674 repl_mutual
675 (OM_uint32 * minor_status,
676  gsskrb5_ctx ctx,
677  krb5_context context,
678  const gss_OID mech_type,
679  OM_uint32 req_flags,
680  OM_uint32 time_req,
681  const gss_channel_bindings_t input_chan_bindings,
682  const gss_buffer_t input_token,
683  gss_OID * actual_mech_type,
684  gss_buffer_t output_token,
685  OM_uint32 * ret_flags,
686  OM_uint32 * time_rec
687     )
688 {
689     OM_uint32 ret;
690     krb5_error_code kret;
691     krb5_data indata;
692     krb5_ap_rep_enc_part *repl;
693
694     output_token->length = 0;
695     output_token->value = NULL;
696
697     if (actual_mech_type)
698         *actual_mech_type = GSS_KRB5_MECHANISM;
699
700     if (ctx->flags & GSS_C_DCE_STYLE) {
701         /* There is no OID wrapping. */
702         indata.length   = input_token->length;
703         indata.data     = input_token->value;
704     } else {
705         ret = _gsskrb5_decapsulate (minor_status,
706                                     input_token,
707                                     &indata,
708                                     "\x02\x00",
709                                     GSS_KRB5_MECHANISM);
710         if (ret == GSS_S_DEFECTIVE_TOKEN) {
711             /* check if there is an error token sent instead */
712             ret = _gsskrb5_decapsulate (minor_status,
713                                         input_token,
714                                         &indata,
715                                         "\x03\x00",
716                                         GSS_KRB5_MECHANISM);
717             if (ret == GSS_S_COMPLETE) {
718                 KRB_ERROR error;
719                 
720                 kret = krb5_rd_error(context, &indata, &error);
721                 if (kret == 0) {
722                     kret = krb5_error_from_rd_error(context, &error, NULL);
723
724                     /* save the time skrew for this host */
725                     if (kret == KRB5KRB_AP_ERR_SKEW) {
726                         krb5_data timedata;
727                         unsigned char p[4];
728                         int32_t t = error.stime - time(NULL);
729
730                         p[0] = (t >> 24) & 0xFF;
731                         p[1] = (t >> 16) & 0xFF;
732                         p[2] = (t >> 8)  & 0xFF;
733                         p[3] = (t >> 0)  & 0xFF;
734
735                         timedata.data = p;
736                         timedata.length = sizeof(p);
737
738                         krb5_cc_set_config(context, ctx->ccache, ctx->target,
739                                            "time-offset", &timedata);
740
741                         if ((ctx->more_flags & RETRIED) == 0)
742                             ctx->state = INITIATOR_RESTART;
743                         ctx->more_flags |= RETRIED;
744                     }
745                     free_KRB_ERROR (&error);
746                 }
747                 *minor_status = kret;
748                 return GSS_S_FAILURE;
749             }
750             return ret;
751         }
752     }
753
754     kret = krb5_rd_rep (context,
755                         ctx->auth_context,
756                         &indata,
757                         &repl);
758     if (kret) {
759         *minor_status = kret;
760         return GSS_S_FAILURE;
761     }
762     krb5_free_ap_rep_enc_part (context,
763                                repl);
764
765     *minor_status = 0;
766     if (time_rec) {
767         ret = _gsskrb5_lifetime_left(minor_status,
768                                      context,
769                                      ctx->lifetime,
770                                      time_rec);
771     } else {
772         ret = GSS_S_COMPLETE;
773     }
774     if (ret_flags)
775         *ret_flags = ctx->flags;
776
777     if (req_flags & GSS_C_DCE_STYLE) {
778         int32_t local_seq, remote_seq;
779         krb5_data outbuf;
780
781         /*
782          * So DCE_STYLE is strange. The client echos the seq number
783          * that the server used in the server's mk_rep in its own
784          * mk_rep(). After when done, it resets to it's own seq number
785          * for the gss_wrap calls.
786          */
787
788         krb5_auth_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
789         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
790         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
791
792         kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
793         if (kret) {
794             *minor_status = kret;
795             return GSS_S_FAILURE;
796         }
797         
798         /* reset local seq number */
799         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq); 
800
801         output_token->length = outbuf.length;
802         output_token->value  = outbuf.data;
803     }
804
805     return gsskrb5_initiator_ready(minor_status, ctx, context);
806 }
807
808 /*
809  * gss_init_sec_context
810  */
811
812 OM_uint32 _gsskrb5_init_sec_context
813 (OM_uint32 * minor_status,
814  const gss_cred_id_t cred_handle,
815  gss_ctx_id_t * context_handle,
816  const gss_name_t target_name,
817  const gss_OID mech_type,
818  OM_uint32 req_flags,
819  OM_uint32 time_req,
820  const gss_channel_bindings_t input_chan_bindings,
821  const gss_buffer_t input_token,
822  gss_OID * actual_mech_type,
823  gss_buffer_t output_token,
824  OM_uint32 * ret_flags,
825  OM_uint32 * time_rec
826     )
827 {
828     krb5_context context;
829     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
830     gsskrb5_ctx ctx;
831     OM_uint32 ret;
832
833     GSSAPI_KRB5_INIT (&context);
834
835     output_token->length = 0;
836     output_token->value  = NULL;
837
838     if (context_handle == NULL) {
839         *minor_status = 0;
840         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
841     }
842
843     if (ret_flags)
844         *ret_flags = 0;
845     if (time_rec)
846         *time_rec = 0;
847
848     if (target_name == GSS_C_NO_NAME) {
849         if (actual_mech_type)
850             *actual_mech_type = GSS_C_NO_OID;
851         *minor_status = 0;
852         return GSS_S_BAD_NAME;
853     }
854
855     if (mech_type != GSS_C_NO_OID &&
856         !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
857         return GSS_S_BAD_MECH;
858
859     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
860         OM_uint32 ret;
861
862         if (*context_handle != GSS_C_NO_CONTEXT) {
863             *minor_status = 0;
864             return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
865         }
866
867         ret = _gsskrb5_create_ctx(minor_status,
868                                   context_handle,
869                                   context,
870                                   input_chan_bindings,
871                                   INITIATOR_START);
872         if (ret)
873             return ret;
874     }
875
876     if (*context_handle == GSS_C_NO_CONTEXT) {
877         *minor_status = 0;
878         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
879     }
880
881     ctx = (gsskrb5_ctx) *context_handle;
882
883     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
884
885  again:
886     switch (ctx->state) {
887     case INITIATOR_START:
888         ret = init_auth(minor_status,
889                         cred,
890                         ctx,
891                         context,
892                         target_name,
893                         mech_type,
894                         req_flags,
895                         time_req,
896                         input_token,
897                         actual_mech_type,
898                         output_token,
899                         ret_flags,
900                         time_rec);
901         if (ret != GSS_S_COMPLETE)
902             break;      
903         /* FALL THOUGH */
904     case INITIATOR_RESTART:
905         ret = init_auth_restart(minor_status,
906                                 cred,
907                                 ctx,
908                                 context,
909                                 req_flags,
910                                 input_chan_bindings,
911                                 input_token,
912                                 actual_mech_type,
913                                 output_token,
914                                 ret_flags,
915                                 time_rec);
916         break;
917     case INITIATOR_WAIT_FOR_MUTAL:
918         ret = repl_mutual(minor_status,
919                           ctx,
920                           context,
921                           mech_type,
922                           req_flags,
923                           time_req,
924                           input_chan_bindings,
925                           input_token,
926                           actual_mech_type,
927                           output_token,
928                           ret_flags,
929                           time_rec);
930         if (ctx->state == INITIATOR_RESTART)
931             goto again;
932         break;
933     case INITIATOR_READY:
934         /*
935          * If we get there, the caller have called
936          * gss_init_sec_context() one time too many.
937          */
938         _gsskrb5_set_status(EINVAL, "init_sec_context "
939                             "called one time too many");
940         *minor_status = EINVAL;
941         ret = GSS_S_BAD_STATUS;
942         break;
943     default:
944         _gsskrb5_set_status(EINVAL, "init_sec_context "
945                             "invalid state %d for client",
946                             (int)ctx->state);
947         *minor_status = EINVAL;
948         ret = GSS_S_BAD_STATUS;
949         break;
950     }
951     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
952
953     /* destroy context in case of error */
954     if (GSS_ERROR(ret)) {
955         OM_uint32 min2;
956         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
957     }
958
959     return ret;
960
961 }