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