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