]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/gssapi/krb5/init_sec_context.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / 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         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     /*
426      * This is hideous glue for (NFS) clients that wants to limit the
427      * available enctypes to what it can support (encryption in
428      * kernel). If there is no enctypes selected for this credential,
429      * reset it to the default set of enctypes.
430      */
431     {
432         krb5_enctype *enctypes = NULL;
433
434         if (cred && cred->enctypes)
435             enctypes = cred->enctypes;
436         krb5_set_default_in_tkt_etypes(context, enctypes);
437     }
438
439     /* canon name if needed for client + target realm */
440     kret = krb5_cc_get_config(context, ctx->ccache, NULL,
441                               "realm-config", &outbuf);
442     if (kret == 0) {
443         /* XXX 2 is no server canon */
444         if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
445             allow_dns = 0;
446         krb5_data_free(&outbuf);
447     }
448
449     /*
450      * First we try w/o dns, hope that the KDC have register alias
451      * (and referrals if cross realm) for this principal. If that
452      * fails and if we are allowed to using this realm try again with
453      * DNS canonicalizion.
454      */
455     ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
456                             ctx, name, 0, time_req,
457                             time_rec);
458     if (ret && allow_dns)
459         ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
460                                 ctx, name, 1, time_req,
461                                 time_rec);
462     if (ret)
463         goto failure;
464
465     ctx->lifetime = ctx->kcred->times.endtime;
466
467     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
468     if (ret)
469         goto failure;
470
471     ret = _gsskrb5_lifetime_left(minor_status,
472                                  context,
473                                  ctx->lifetime,
474                                  &lifetime_rec);
475     if (ret)
476         goto failure;
477
478     if (lifetime_rec == 0) {
479         *minor_status = 0;
480         ret = GSS_S_CONTEXT_EXPIRED;
481         goto failure;
482     }
483
484     krb5_auth_con_setkey(context,
485                          ctx->auth_context,
486                          &ctx->kcred->session);
487
488     kret = krb5_auth_con_generatelocalsubkey(context,
489                                              ctx->auth_context,
490                                              &ctx->kcred->session);
491     if(kret) {
492         *minor_status = kret;
493         ret = GSS_S_FAILURE;
494         goto failure;
495     }
496
497     return GSS_S_COMPLETE;
498
499 failure:
500     if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
501         krb5_cc_close(context, ctx->ccache);
502     ctx->ccache = NULL;
503
504     return ret;
505
506 }
507
508 static OM_uint32
509 init_auth_restart
510 (OM_uint32 * minor_status,
511  gsskrb5_cred cred,
512  gsskrb5_ctx ctx,
513  krb5_context context,
514  OM_uint32 req_flags,
515  const gss_channel_bindings_t input_chan_bindings,
516  const gss_buffer_t input_token,
517  gss_OID * actual_mech_type,
518  gss_buffer_t output_token,
519  OM_uint32 * ret_flags,
520  OM_uint32 * time_rec
521     )
522 {
523     OM_uint32 ret = GSS_S_FAILURE;
524     krb5_error_code kret;
525     krb5_flags ap_options;
526     krb5_data outbuf;
527     uint32_t flags;
528     krb5_data authenticator;
529     Checksum cksum;
530     krb5_enctype enctype;
531     krb5_data fwd_data, timedata;
532     int32_t offset = 0, oldoffset = 0;
533     uint32_t flagmask;
534
535     krb5_data_zero(&outbuf);
536     krb5_data_zero(&fwd_data);
537
538     *minor_status = 0;
539
540     /*
541      * If the credential doesn't have ok-as-delegate, check if there
542      * is a realm setting and use that.
543      */
544     if (!ctx->kcred->flags.b.ok_as_delegate) {
545         krb5_data data;
546
547         ret = krb5_cc_get_config(context, ctx->ccache, NULL,
548                                  "realm-config", &data);
549         if (ret == 0) {
550             /* XXX 1 is use ok-as-delegate */
551             if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
552                 req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
553             krb5_data_free(&data);
554         }
555     }
556
557     flagmask = 0;
558
559     /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
560     if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
561         && ctx->kcred->flags.b.ok_as_delegate)
562         flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
563     /* if there still is a GSS_C_DELEG_FLAG, use that */
564     if (req_flags & GSS_C_DELEG_FLAG)
565         flagmask |= GSS_C_DELEG_FLAG;
566
567
568     flags = 0;
569     ap_options = 0;
570     if (flagmask & GSS_C_DELEG_FLAG) {
571         do_delegation (context,
572                        ctx->deleg_auth_context,
573                        ctx->ccache, ctx->kcred, ctx->target,
574                        &fwd_data, flagmask, &flags);
575     }
576
577     if (req_flags & GSS_C_MUTUAL_FLAG) {
578         flags |= GSS_C_MUTUAL_FLAG;
579         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
580     }
581
582     if (req_flags & GSS_C_REPLAY_FLAG)
583         flags |= GSS_C_REPLAY_FLAG;
584     if (req_flags & GSS_C_SEQUENCE_FLAG)
585         flags |= GSS_C_SEQUENCE_FLAG;
586 #if 0
587     if (req_flags & GSS_C_ANON_FLAG)
588         ;                               /* XXX */
589 #endif
590     if (req_flags & GSS_C_DCE_STYLE) {
591         /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
592         flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
593         ap_options |= AP_OPTS_MUTUAL_REQUIRED;
594     }
595     if (req_flags & GSS_C_IDENTIFY_FLAG)
596         flags |= GSS_C_IDENTIFY_FLAG;
597     if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
598         flags |= GSS_C_EXTENDED_ERROR_FLAG;
599
600     if (req_flags & GSS_C_CONF_FLAG) {
601         flags |= GSS_C_CONF_FLAG;
602     }
603     if (req_flags & GSS_C_INTEG_FLAG) {
604         flags |= GSS_C_INTEG_FLAG;
605     }
606     if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
607         flags |= GSS_C_CONF_FLAG;
608         flags |= GSS_C_INTEG_FLAG;
609     }
610     flags |= GSS_C_TRANS_FLAG;
611
612     if (ret_flags)
613         *ret_flags = flags;
614     ctx->flags = flags;
615     ctx->more_flags |= LOCAL;
616
617     ret = _gsskrb5_create_8003_checksum (minor_status,
618                                          input_chan_bindings,
619                                          flags,
620                                          &fwd_data,
621                                          &cksum);
622     krb5_data_free (&fwd_data);
623     if (ret)
624         goto failure;
625
626     enctype = ctx->auth_context->keyblock->keytype;
627
628     ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
629                              "time-offset", &timedata);
630     if (ret == 0) {
631         if (timedata.length == 4) {
632             const u_char *p = timedata.data;
633             offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
634         }
635         krb5_data_free(&timedata);
636     }
637
638     if (offset) {
639         krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
640         krb5_set_kdc_sec_offset (context, offset, -1);
641     }
642
643     kret = _krb5_build_authenticator(context,
644                                      ctx->auth_context,
645                                      enctype,
646                                      ctx->kcred,
647                                      &cksum,
648                                      &authenticator,
649                                      KRB5_KU_AP_REQ_AUTH);
650
651     if (kret) {
652         if (offset)
653             krb5_set_kdc_sec_offset (context, oldoffset, -1);
654         *minor_status = kret;
655         ret = GSS_S_FAILURE;
656         goto failure;
657     }
658
659     kret = krb5_build_ap_req (context,
660                               enctype,
661                               ctx->kcred,
662                               ap_options,
663                               authenticator,
664                               &outbuf);
665     if (offset)
666         krb5_set_kdc_sec_offset (context, oldoffset, -1);
667     if (kret) {
668         *minor_status = kret;
669         ret = GSS_S_FAILURE;
670         goto failure;
671     }
672
673     if (flags & GSS_C_DCE_STYLE) {
674         output_token->value = outbuf.data;
675         output_token->length = outbuf.length;
676     } else {
677         ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
678                                     (u_char *)(intptr_t)"\x01\x00",
679                                     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 static krb5_error_code
702 handle_error_packet(krb5_context context,
703                     gsskrb5_ctx ctx,
704                     krb5_data indata)
705 {
706     krb5_error_code kret;
707     KRB_ERROR error;
708
709     kret = krb5_rd_error(context, &indata, &error);
710     if (kret == 0) {
711         kret = krb5_error_from_rd_error(context, &error, NULL);
712
713         /* save the time skrew for this host */
714         if (kret == KRB5KRB_AP_ERR_SKEW) {
715             krb5_data timedata;
716             unsigned char p[4];
717             int32_t t = error.stime - time(NULL);
718
719             p[0] = (t >> 24) & 0xFF;
720             p[1] = (t >> 16) & 0xFF;
721             p[2] = (t >> 8)  & 0xFF;
722             p[3] = (t >> 0)  & 0xFF;
723
724             timedata.data = p;
725             timedata.length = sizeof(p);
726
727             krb5_cc_set_config(context, ctx->ccache, ctx->target,
728                                "time-offset", &timedata);
729
730             if ((ctx->more_flags & RETRIED) == 0)
731                  ctx->state = INITIATOR_RESTART;
732             ctx->more_flags |= RETRIED;
733         }
734         free_KRB_ERROR (&error);
735     }
736     return kret;
737 }
738
739
740 static OM_uint32
741 repl_mutual
742 (OM_uint32 * minor_status,
743  gsskrb5_ctx ctx,
744  krb5_context context,
745  const gss_OID mech_type,
746  OM_uint32 req_flags,
747  OM_uint32 time_req,
748  const gss_channel_bindings_t input_chan_bindings,
749  const gss_buffer_t input_token,
750  gss_OID * actual_mech_type,
751  gss_buffer_t output_token,
752  OM_uint32 * ret_flags,
753  OM_uint32 * time_rec
754     )
755 {
756     OM_uint32 ret;
757     krb5_error_code kret;
758     krb5_data indata;
759     krb5_ap_rep_enc_part *repl;
760
761     output_token->length = 0;
762     output_token->value = NULL;
763
764     if (actual_mech_type)
765         *actual_mech_type = GSS_KRB5_MECHANISM;
766
767     if (IS_DCE_STYLE(ctx)) {
768         /* There is no OID wrapping. */
769         indata.length   = input_token->length;
770         indata.data     = input_token->value;
771         kret = krb5_rd_rep(context,
772                            ctx->auth_context,
773                            &indata,
774                            &repl);
775         if (kret) {
776             ret = _gsskrb5_decapsulate(minor_status,
777                                        input_token,
778                                        &indata,
779                                        "\x03\x00",
780                                        GSS_KRB5_MECHANISM);
781             if (ret == GSS_S_COMPLETE) {
782                 *minor_status = handle_error_packet(context, ctx, indata);
783             } else {
784                 *minor_status = kret;
785             }
786             return GSS_S_FAILURE;
787         }
788     } else {
789         ret = _gsskrb5_decapsulate (minor_status,
790                                     input_token,
791                                     &indata,
792                                     "\x02\x00",
793                                     GSS_KRB5_MECHANISM);
794         if (ret == GSS_S_DEFECTIVE_TOKEN) {
795             /* check if there is an error token sent instead */
796             ret = _gsskrb5_decapsulate (minor_status,
797                                         input_token,
798                                         &indata,
799                                         "\x03\x00",
800                                         GSS_KRB5_MECHANISM);
801             if (ret == GSS_S_COMPLETE) {
802                 *minor_status = handle_error_packet(context, ctx, indata);
803                 return GSS_S_FAILURE;
804             }
805         }
806         kret = krb5_rd_rep (context,
807                             ctx->auth_context,
808                             &indata,
809                             &repl);
810         if (kret) {
811             *minor_status = kret;
812             return GSS_S_FAILURE;
813         }
814     }
815
816     krb5_free_ap_rep_enc_part (context,
817                                repl);
818
819     *minor_status = 0;
820     if (time_rec) {
821         ret = _gsskrb5_lifetime_left(minor_status,
822                                      context,
823                                      ctx->lifetime,
824                                      time_rec);
825     } else {
826         ret = GSS_S_COMPLETE;
827     }
828     if (ret_flags)
829         *ret_flags = ctx->flags;
830
831     if (req_flags & GSS_C_DCE_STYLE) {
832         int32_t local_seq, remote_seq;
833         krb5_data outbuf;
834
835         /*
836          * So DCE_STYLE is strange. The client echos the seq number
837          * that the server used in the server's mk_rep in its own
838          * mk_rep(). After when done, it resets to it's own seq number
839          * for the gss_wrap calls.
840          */
841
842         krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
843         krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
844         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
845
846         kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
847         if (kret) {
848             *minor_status = kret;
849             return GSS_S_FAILURE;
850         }
851
852         /* reset local seq number */
853         krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq);
854
855         output_token->length = outbuf.length;
856         output_token->value  = outbuf.data;
857     }
858
859     return gsskrb5_initiator_ready(minor_status, ctx, context);
860 }
861
862 /*
863  * gss_init_sec_context
864  */
865
866 OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
867 (OM_uint32 * minor_status,
868  const gss_cred_id_t cred_handle,
869  gss_ctx_id_t * context_handle,
870  const gss_name_t target_name,
871  const gss_OID mech_type,
872  OM_uint32 req_flags,
873  OM_uint32 time_req,
874  const gss_channel_bindings_t input_chan_bindings,
875  const gss_buffer_t input_token,
876  gss_OID * actual_mech_type,
877  gss_buffer_t output_token,
878  OM_uint32 * ret_flags,
879  OM_uint32 * time_rec
880     )
881 {
882     krb5_context context;
883     gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
884     gsskrb5_ctx ctx;
885     OM_uint32 ret;
886
887     GSSAPI_KRB5_INIT (&context);
888
889     output_token->length = 0;
890     output_token->value  = NULL;
891
892     if (context_handle == NULL) {
893         *minor_status = 0;
894         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
895     }
896
897     if (ret_flags)
898         *ret_flags = 0;
899     if (time_rec)
900         *time_rec = 0;
901
902     if (target_name == GSS_C_NO_NAME) {
903         if (actual_mech_type)
904             *actual_mech_type = GSS_C_NO_OID;
905         *minor_status = 0;
906         return GSS_S_BAD_NAME;
907     }
908
909     if (mech_type != GSS_C_NO_OID &&
910         !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
911         return GSS_S_BAD_MECH;
912
913     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
914         OM_uint32 ret1;
915
916         if (*context_handle != GSS_C_NO_CONTEXT) {
917             *minor_status = 0;
918             return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
919         }
920
921         ret1 = _gsskrb5_create_ctx(minor_status,
922                                   context_handle,
923                                   context,
924                                   input_chan_bindings,
925                                   INITIATOR_START);
926         if (ret1)
927             return ret1;
928     }
929
930     if (*context_handle == GSS_C_NO_CONTEXT) {
931         *minor_status = 0;
932         return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
933     }
934
935     ctx = (gsskrb5_ctx) *context_handle;
936
937     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
938
939  again:
940     switch (ctx->state) {
941     case INITIATOR_START:
942         ret = init_auth(minor_status,
943                         cred,
944                         ctx,
945                         context,
946                         target_name,
947                         mech_type,
948                         req_flags,
949                         time_req,
950                         input_token,
951                         actual_mech_type,
952                         output_token,
953                         ret_flags,
954                         time_rec);
955         if (ret != GSS_S_COMPLETE)
956             break;
957         /* FALL THOUGH */
958     case INITIATOR_RESTART:
959         ret = init_auth_restart(minor_status,
960                                 cred,
961                                 ctx,
962                                 context,
963                                 req_flags,
964                                 input_chan_bindings,
965                                 input_token,
966                                 actual_mech_type,
967                                 output_token,
968                                 ret_flags,
969                                 time_rec);
970         break;
971     case INITIATOR_WAIT_FOR_MUTAL:
972         ret = repl_mutual(minor_status,
973                           ctx,
974                           context,
975                           mech_type,
976                           req_flags,
977                           time_req,
978                           input_chan_bindings,
979                           input_token,
980                           actual_mech_type,
981                           output_token,
982                           ret_flags,
983                           time_rec);
984         if (ctx->state == INITIATOR_RESTART)
985             goto again;
986         break;
987     case INITIATOR_READY:
988         /*
989          * If we get there, the caller have called
990          * gss_init_sec_context() one time too many.
991          */
992         _gsskrb5_set_status(EINVAL, "init_sec_context "
993                             "called one time too many");
994         *minor_status = EINVAL;
995         ret = GSS_S_BAD_STATUS;
996         break;
997     default:
998         _gsskrb5_set_status(EINVAL, "init_sec_context "
999                             "invalid state %d for client",
1000                             (int)ctx->state);
1001         *minor_status = EINVAL;
1002         ret = GSS_S_BAD_STATUS;
1003         break;
1004     }
1005     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1006
1007     /* destroy context in case of error */
1008     if (GSS_ERROR(ret)) {
1009         OM_uint32 min2;
1010         _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
1011     }
1012
1013     return ret;
1014
1015 }