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