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