heimdal: update to lorikeet-heimdal rev 801
[kai/samba-autobuild/.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: init_sec_context.c 23422 2008-07-26 18:38:29Z lha $");
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 *flags)
275 {
276     krb5_creds creds;
277     KDCOptions fwd_flags;
278     krb5_error_code kret;
279        
280     memset (&creds, 0, sizeof(creds));
281     krb5_data_zero (fwd_data);
282        
283     kret = krb5_cc_get_principal(context, ccache, &creds.client);
284     if (kret) 
285         goto out;
286        
287     kret = krb5_build_principal(context,
288                                 &creds.server,
289                                 strlen(creds.client->realm),
290                                 creds.client->realm,
291                                 KRB5_TGS_NAME,
292                                 creds.client->realm,
293                                 NULL);
294     if (kret)
295         goto out; 
296        
297     creds.times.endtime = 0;
298        
299     memset(&fwd_flags, 0, sizeof(fwd_flags));
300     fwd_flags.forwarded = 1;
301     fwd_flags.forwardable = 1;
302        
303     if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
304         name->name.name_string.len < 2) 
305         goto out;
306        
307     kret = krb5_get_forwarded_creds(context,
308                                     ac,
309                                     ccache,
310                                     KDCOptions2int(fwd_flags),
311                                     name->name.name_string.val[1],
312                                     &creds,
313                                     fwd_data);
314        
315  out:
316     if (kret)
317         *flags &= ~GSS_C_DELEG_FLAG;
318     else
319         *flags |= GSS_C_DELEG_FLAG;
320        
321     if (creds.client)
322         krb5_free_principal(context, creds.client);
323     if (creds.server)
324         krb5_free_principal(context, creds.server);
325 }
326
327 /*
328  * first stage of init-sec-context
329  */
330
331 static OM_uint32
332 init_auth
333 (OM_uint32 * minor_status,
334  gsskrb5_cred cred,
335  gsskrb5_ctx ctx,
336  krb5_context context,
337  krb5_const_principal name,
338  const gss_OID mech_type,
339  OM_uint32 req_flags,
340  OM_uint32 time_req,
341  const gss_buffer_t input_token,
342  gss_OID * actual_mech_type,
343  gss_buffer_t output_token,
344  OM_uint32 * ret_flags,
345  OM_uint32 * time_rec
346     )
347 {
348     OM_uint32 ret = GSS_S_FAILURE;
349     krb5_error_code kret;
350     krb5_data outbuf;
351     krb5_data fwd_data;
352     OM_uint32 lifetime_rec;
353
354     krb5_data_zero(&outbuf);
355     krb5_data_zero(&fwd_data);
356
357     *minor_status = 0;
358
359     if (actual_mech_type)
360         *actual_mech_type = GSS_KRB5_MECHANISM;
361
362     if (cred == NULL) {
363         kret = krb5_cc_default (context, &ctx->ccache);
364         if (kret) {
365             *minor_status = kret;
366             ret = GSS_S_FAILURE;
367             goto failure;
368         }
369         ctx->more_flags |= CLOSE_CCACHE;
370     } else
371         ctx->ccache = cred->ccache;
372
373     kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
374     if (kret) {
375         *minor_status = kret;
376         ret = GSS_S_FAILURE;
377         goto failure;
378     }
379
380     kret = krb5_copy_principal (context, name, &ctx->target);
381     if (kret) {
382         *minor_status = kret;
383         ret = GSS_S_FAILURE;
384         goto failure;
385     }
386
387     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
388     if (ret)
389         goto failure;
390
391
392     /*
393      * This is hideous glue for (NFS) clients that wants to limit the
394      * available enctypes to what it can support (encryption in
395      * kernel). If there is no enctypes selected for this credential,
396      * reset it to the default set of enctypes.
397      */
398     {
399         krb5_enctype *enctypes = NULL;
400
401         if (cred && cred->enctypes)
402             enctypes = cred->enctypes;
403         krb5_set_default_in_tkt_etypes(context, enctypes);
404     }
405
406     ret = gsskrb5_get_creds(minor_status,
407                             context,
408                             ctx->ccache,
409                             ctx,
410                             ctx->target,
411                             time_req,
412                             time_rec,
413                             &ctx->kcred);
414     if (ret)
415         goto failure;
416
417     ctx->lifetime = ctx->kcred->times.endtime;
418
419     ret = _gsskrb5_lifetime_left(minor_status,
420                                  context,
421                                  ctx->lifetime,
422                                  &lifetime_rec);
423     if (ret) {
424         goto failure;
425     }
426
427     if (lifetime_rec == 0) {
428         *minor_status = 0;
429         ret = GSS_S_CONTEXT_EXPIRED;
430         goto failure;
431     }
432
433     krb5_auth_con_setkey(context, 
434                          ctx->auth_context, 
435                          &ctx->kcred->session);
436
437     kret = krb5_auth_con_generatelocalsubkey(context, 
438                                              ctx->auth_context,
439                                              &ctx->kcred->session);
440     if(kret) {
441         *minor_status = kret;
442         ret = GSS_S_FAILURE;
443         goto failure;
444     }
445
446     return GSS_S_COMPLETE;
447
448 failure:
449     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
450         krb5_cc_close(context, ctx->ccache);
451     ctx->ccache = NULL;
452
453     return ret;
454
455 }
456
457 static OM_uint32
458 init_auth_restart
459 (OM_uint32 * minor_status,
460  gsskrb5_cred cred,
461  gsskrb5_ctx ctx,
462  krb5_context context,
463  OM_uint32 req_flags,
464  const gss_channel_bindings_t input_chan_bindings,
465  const gss_buffer_t input_token,
466  gss_OID * actual_mech_type,
467  gss_buffer_t output_token,
468  OM_uint32 * ret_flags,
469  OM_uint32 * time_rec
470     )
471 {
472     OM_uint32 ret = GSS_S_FAILURE;
473     krb5_error_code kret;
474     krb5_flags ap_options;
475     krb5_data outbuf;
476     uint32_t flags;
477     krb5_data authenticator;
478     Checksum cksum;
479     krb5_enctype enctype;
480     krb5_data fwd_data, timedata;
481     int32_t offset = 0, oldoffset;
482
483     krb5_data_zero(&outbuf);
484     krb5_data_zero(&fwd_data);
485
486     *minor_status = 0;
487
488     /* 
489      * If the credential doesn't have ok-as-delegate, check what local
490      * policy say about ok-as-delegate, default is FALSE that makes
491      * code ignore the KDC setting and follow what the application
492      * requested. If it is TRUE, strip of the GSS_C_DELEG_FLAG if the
493      * KDC doesn't set ok-as-delegate.
494      */
495     if (!ctx->kcred->flags.b.ok_as_delegate) {
496         krb5_boolean delegate, realm_setting;
497         krb5_data data;
498     
499         realm_setting = FALSE;
500
501         ret = krb5_cc_get_config(context, ctx->ccache, NULL,
502                                  "realm-config", &data);
503         if (ret == 0) {
504             /* XXX 1 is use ok-as-delegate */
505             if (data.length > 0 && (((unsigned char *)data.data)[0]) & 1)
506                 realm_setting = TRUE;
507             krb5_data_free(&data);
508         }
509
510         krb5_appdefault_boolean(context, "gssapi", ctx->target->realm,
511                                 "ok-as-delegate", realm_setting,
512                                 &delegate);
513         if (delegate)
514             req_flags &= ~GSS_C_DELEG_FLAG;
515     }
516
517     flags = 0;
518     ap_options = 0;
519     if (req_flags & GSS_C_DELEG_FLAG)
520         do_delegation (context,
521                        ctx->auth_context,
522                        ctx->ccache, ctx->kcred, ctx->target,
523                        &fwd_data, &flags);
524     
525     if (req_flags & GSS_C_MUTUAL_FLAG) {
526         flags |= GSS_C_MUTUAL_FLAG;
527         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
528     }
529     
530     if (req_flags & GSS_C_REPLAY_FLAG)
531         flags |= GSS_C_REPLAY_FLAG;
532     if (req_flags & GSS_C_SEQUENCE_FLAG)
533         flags |= GSS_C_SEQUENCE_FLAG;
534     if (req_flags & GSS_C_ANON_FLAG)
535         ;                               /* XXX */
536     if (req_flags & GSS_C_DCE_STYLE) {
537         /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
538         flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
539         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
540     }
541     if (req_flags & GSS_C_IDENTIFY_FLAG)
542         flags |= GSS_C_IDENTIFY_FLAG;
543     if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
544         flags |= GSS_C_EXTENDED_ERROR_FLAG;
545
546     if (req_flags & GSS_C_CONF_FLAG) {
547         flags |= GSS_C_CONF_FLAG;
548     }
549     if (req_flags & GSS_C_INTEG_FLAG) {
550         flags |= GSS_C_INTEG_FLAG;
551     }
552     if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
553         flags |= GSS_C_CONF_FLAG;
554         flags |= GSS_C_INTEG_FLAG;
555     }
556     flags |= GSS_C_TRANS_FLAG;
557     
558     if (ret_flags)
559         *ret_flags = flags;
560     ctx->flags = flags;
561     ctx->more_flags |= LOCAL;
562     
563     ret = _gsskrb5_create_8003_checksum (minor_status,
564                                          input_chan_bindings,
565                                          flags,
566                                          &fwd_data,
567                                          &cksum);
568     krb5_data_free (&fwd_data);
569     if (ret)
570         goto failure;
571
572     enctype = ctx->auth_context->keyblock->keytype;
573
574     ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
575                              "time-offset", &timedata);
576     if (ret == 0) {
577         if (timedata.length == 4) {
578             const u_char *p = timedata.data;
579             offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
580         }
581         krb5_data_free(&timedata);
582     }
583
584     if (offset) {
585         krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
586         krb5_set_kdc_sec_offset (context, offset, -1);
587     }
588
589     kret = krb5_build_authenticator (context,
590                                      ctx->auth_context,
591                                      enctype,
592                                      ctx->kcred,
593                                      &cksum,
594                                      NULL,
595                                      &authenticator,
596                                      KRB5_KU_AP_REQ_AUTH);
597
598     if (kret) {
599         if (offset)
600             krb5_set_kdc_sec_offset (context, oldoffset, -1);
601         *minor_status = kret;
602         ret = GSS_S_FAILURE;
603         goto failure;
604     }
605
606     kret = krb5_build_ap_req (context,
607                               enctype,
608                               ctx->kcred,
609                               ap_options,
610                               authenticator,
611                               &outbuf);
612     if (offset)
613         krb5_set_kdc_sec_offset (context, oldoffset, -1);
614     if (kret) {
615         *minor_status = kret;
616         ret = GSS_S_FAILURE;
617         goto failure;
618     }
619
620     if (flags & GSS_C_DCE_STYLE) {
621         output_token->value = outbuf.data;
622         output_token->length = outbuf.length;
623     } else {
624         ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
625                                     (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
626         krb5_data_free (&outbuf);
627         if (ret)
628             goto failure;
629     }
630
631     free_Checksum(&cksum);
632
633     if (flags & GSS_C_MUTUAL_FLAG) {
634         ctx->state = INITIATOR_WAIT_FOR_MUTAL;
635         return GSS_S_CONTINUE_NEEDED;
636     }
637
638     return gsskrb5_initiator_ready(minor_status, ctx, context);
639 failure:
640     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
641         krb5_cc_close(context, ctx->ccache);
642     ctx->ccache = NULL;
643
644     return ret;
645 }
646
647
648 static OM_uint32
649 repl_mutual
650 (OM_uint32 * minor_status,
651  gsskrb5_ctx ctx,
652  krb5_context context,
653  const gss_OID mech_type,
654  OM_uint32 req_flags,
655  OM_uint32 time_req,
656  const gss_channel_bindings_t input_chan_bindings,
657  const gss_buffer_t input_token,
658  gss_OID * actual_mech_type,
659  gss_buffer_t output_token,
660  OM_uint32 * ret_flags,
661  OM_uint32 * time_rec
662     )
663 {
664     OM_uint32 ret;
665     krb5_error_code kret;
666     krb5_data indata;
667     krb5_ap_rep_enc_part *repl;
668     int is_cfx = 0;
669
670     output_token->length = 0;
671     output_token->value = NULL;
672
673     if (actual_mech_type)
674         *actual_mech_type = GSS_KRB5_MECHANISM;
675
676     if (ctx->flags & GSS_C_DCE_STYLE) {
677         /* There is no OID wrapping. */
678         indata.length   = input_token->length;
679         indata.data     = input_token->value;
680     } else {
681         ret = _gsskrb5_decapsulate (minor_status,
682                                     input_token,
683                                     &indata,
684                                     "\x02\x00",
685                                     GSS_KRB5_MECHANISM);
686         if (ret == GSS_S_DEFECTIVE_TOKEN) {
687             /* check if there is an error token sent instead */
688             ret = _gsskrb5_decapsulate (minor_status,
689                                         input_token,
690                                         &indata,
691                                         "\x03\x00",
692                                         GSS_KRB5_MECHANISM);
693             if (ret == GSS_S_COMPLETE) {
694                 KRB_ERROR error;
695                 
696                 kret = krb5_rd_error(context, &indata, &error);
697                 if (kret == 0) {
698                     kret = krb5_error_from_rd_error(context, &error, NULL);
699
700                     /* save the time skrew for this host */
701                     if (kret == KRB5KRB_AP_ERR_SKEW) {
702                         krb5_data timedata;
703                         unsigned char p[4];
704                         int32_t t = error.stime - time(NULL);
705
706                         p[0] = (t >> 24) & 0xFF;
707                         p[1] = (t >> 16) & 0xFF;
708                         p[2] = (t >> 8)  & 0xFF;
709                         p[3] = (t >> 0)  & 0xFF;
710
711                         timedata.data = p;
712                         timedata.length = sizeof(p);
713
714                         krb5_cc_set_config(context, ctx->ccache, ctx->target,
715                                            "time-offset", &timedata);
716
717                         if ((ctx->more_flags & RETRIED) == 0)
718                             ctx->state = INITIATOR_RESTART;
719                         ctx->more_flags |= RETRIED;
720                     }
721                     free_KRB_ERROR (&error);
722                 }
723                 *minor_status = kret;
724                 return GSS_S_FAILURE;
725             }
726             return ret;
727         }
728     }
729
730     kret = krb5_rd_rep (context,
731                         ctx->auth_context,
732                         &indata,
733                         &repl);
734     if (kret) {
735         *minor_status = kret;
736         return GSS_S_FAILURE;
737     }
738     krb5_free_ap_rep_enc_part (context,
739                                repl);
740     
741     _gsskrb5i_is_cfx(ctx, &is_cfx);
742     if (is_cfx) {
743         krb5_keyblock *key = NULL;
744
745         kret = krb5_auth_con_getremotesubkey(context,
746                                              ctx->auth_context, 
747                                              &key);
748         if (kret == 0 && key != NULL) {
749             ctx->more_flags |= ACCEPTOR_SUBKEY;
750             krb5_free_keyblock (context, key);
751         }
752     }
753
754
755     *minor_status = 0;
756     if (time_rec) {
757         ret = _gsskrb5_lifetime_left(minor_status,
758                                      context,
759                                      ctx->lifetime,
760                                      time_rec);
761     } else {
762         ret = GSS_S_COMPLETE;
763     }
764     if (ret_flags)
765         *ret_flags = ctx->flags;
766
767     if (req_flags & GSS_C_DCE_STYLE) {
768         int32_t local_seq, remote_seq;
769         krb5_data outbuf;
770
771         /*
772          * So DCE_STYLE is strange. The client echos the seq number
773          * that the server used in the server's mk_rep in its own
774          * mk_rep(). After when done, it resets to it's own seq number
775          * for the gss_wrap calls.
776          */
777
778         krb5_auth_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
779         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
780         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
781
782         kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
783         if (kret) {
784             *minor_status = kret;
785             return GSS_S_FAILURE;
786         }
787         
788         /* reset local seq number */
789         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq); 
790
791         output_token->length = outbuf.length;
792         output_token->value  = outbuf.data;
793     }
794
795     return gsskrb5_initiator_ready(minor_status, ctx, context);
796 }
797
798 /*
799  * gss_init_sec_context
800  */
801
802 OM_uint32 _gsskrb5_init_sec_context
803 (OM_uint32 * minor_status,
804  const gss_cred_id_t cred_handle,
805  gss_ctx_id_t * context_handle,
806  const gss_name_t target_name,
807  const gss_OID mech_type,
808  OM_uint32 req_flags,
809  OM_uint32 time_req,
810  const gss_channel_bindings_t input_chan_bindings,
811  const gss_buffer_t input_token,
812  gss_OID * actual_mech_type,
813  gss_buffer_t output_token,
814  OM_uint32 * ret_flags,
815  OM_uint32 * time_rec
816     )
817 {
818     krb5_context context;
819     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
820     krb5_const_principal name = (krb5_const_principal)target_name;
821     gsskrb5_ctx ctx;
822     OM_uint32 ret;
823
824     GSSAPI_KRB5_INIT (&context);
825
826     output_token->length = 0;
827     output_token->value  = NULL;
828
829     if (context_handle == NULL) {
830         *minor_status = 0;
831         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
832     }
833
834     if (ret_flags)
835         *ret_flags = 0;
836     if (time_rec)
837         *time_rec = 0;
838
839     if (target_name == GSS_C_NO_NAME) {
840         if (actual_mech_type)
841             *actual_mech_type = GSS_C_NO_OID;
842         *minor_status = 0;
843         return GSS_S_BAD_NAME;
844     }
845
846     if (mech_type != GSS_C_NO_OID && 
847         !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
848         return GSS_S_BAD_MECH;
849
850     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
851         OM_uint32 ret;
852
853         if (*context_handle != GSS_C_NO_CONTEXT) {
854             *minor_status = 0;
855             return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
856         }
857     
858         ret = _gsskrb5_create_ctx(minor_status,
859                                   context_handle,
860                                   context,
861                                   input_chan_bindings,
862                                   INITIATOR_START);
863         if (ret)
864             return ret;
865     }
866
867     if (*context_handle == GSS_C_NO_CONTEXT) {
868         *minor_status = 0;
869         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
870     }
871
872     ctx = (gsskrb5_ctx) *context_handle;
873
874     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
875
876  again:
877     switch (ctx->state) {
878     case INITIATOR_START:
879         ret = init_auth(minor_status,
880                         cred,
881                         ctx,
882                         context,
883                         name,
884                         mech_type,
885                         req_flags,
886                         time_req,
887                         input_token,
888                         actual_mech_type,
889                         output_token,
890                         ret_flags,
891                         time_rec);
892         if (ret != GSS_S_COMPLETE)
893             break;          
894         /* FALL THOUGH */
895     case INITIATOR_RESTART:
896         ret = init_auth_restart(minor_status,
897                                 cred,
898                                 ctx,
899                                 context,
900                                 req_flags,
901                                 input_chan_bindings,
902                                 input_token,
903                                 actual_mech_type,
904                                 output_token,
905                                 ret_flags,
906                                 time_rec);
907         break;
908     case INITIATOR_WAIT_FOR_MUTAL:
909         ret = repl_mutual(minor_status,
910                           ctx,
911                           context,
912                           mech_type,
913                           req_flags,
914                           time_req,
915                           input_chan_bindings,
916                           input_token,
917                           actual_mech_type,
918                           output_token,
919                           ret_flags,
920                           time_rec);
921         if (ctx->state == INITIATOR_RESTART)
922             goto again;
923         break;
924     case INITIATOR_READY:
925         /* 
926          * If we get there, the caller have called
927          * gss_init_sec_context() one time too many.
928          */
929         *minor_status = 0;
930         ret = GSS_S_BAD_STATUS;
931         break;
932     default:
933         *minor_status = 0;
934         ret = GSS_S_BAD_STATUS;
935         break;
936     }
937     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
938
939     /* destroy context in case of error */
940     if (GSS_ERROR(ret)) {
941         OM_uint32 min2;
942         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
943     }
944
945     return ret;
946
947 }