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
41static OM_uint32
42set_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
101OM_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
201static OM_uint32
202gsskrb5_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
272static OM_uint32
273gsskrb5_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
311static void
312do_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
375static OM_uint32
376init_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
499failure:
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
508static OM_uint32
509init_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);
693failure:
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
701static krb5_error_code
702handle_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
740static OM_uint32
741repl_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
866OM_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}
1016