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