1/*
2 * Copyright (c) 1997 - 2006 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
36HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
37krb5_keytab _gsskrb5_keytab;
38
39static krb5_error_code
40validate_keytab(krb5_context context, const char *name, krb5_keytab *id)
41{
42    krb5_error_code ret;
43
44    ret = krb5_kt_resolve(context, name, id);
45    if (ret)
46	return ret;
47
48    ret = krb5_kt_have_content(context, *id);
49    if (ret) {
50	krb5_kt_close(context, *id);
51	*id = NULL;
52    }
53
54    return ret;
55}
56
57OM_uint32
58_gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
59{
60    krb5_context context;
61    krb5_error_code ret;
62
63    *min_stat = 0;
64
65    ret = _gsskrb5_init(&context);
66    if(ret)
67	return GSS_S_FAILURE;
68
69    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
70
71    if(_gsskrb5_keytab != NULL) {
72	krb5_kt_close(context, _gsskrb5_keytab);
73	_gsskrb5_keytab = NULL;
74    }
75    if (identity == NULL) {
76	ret = krb5_kt_default(context, &_gsskrb5_keytab);
77    } else {
78	/*
79	 * First check if we can the keytab as is and if it has content...
80	 */
81	ret = validate_keytab(context, identity, &_gsskrb5_keytab);
82	/*
83	 * if it doesn't, lets prepend FILE: and try again
84	 */
85	if (ret) {
86	    char *p = NULL;
87	    ret = asprintf(&p, "FILE:%s", identity);
88	    if(ret < 0 || p == NULL) {
89		HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
90		return GSS_S_FAILURE;
91	    }
92	    ret = validate_keytab(context, p, &_gsskrb5_keytab);
93	    free(p);
94	}
95    }
96    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
97    if(ret) {
98	*min_stat = ret;
99	return GSS_S_FAILURE;
100    }
101    return GSS_S_COMPLETE;
102}
103
104void
105_gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
106{
107    krb5_error_code ret;
108    krb5_keyblock *key;
109
110    if (acceptor) {
111	if (ctx->auth_context->local_subkey)
112	    key = ctx->auth_context->local_subkey;
113	else
114	    key = ctx->auth_context->remote_subkey;
115    } else {
116	if (ctx->auth_context->remote_subkey)
117	    key = ctx->auth_context->remote_subkey;
118	else
119	    key = ctx->auth_context->local_subkey;
120    }
121    if (key == NULL)
122	key = ctx->auth_context->keyblock;
123
124    if (key == NULL)
125	return;
126
127    switch (key->keytype) {
128    case ETYPE_DES_CBC_CRC:
129    case ETYPE_DES_CBC_MD4:
130    case ETYPE_DES_CBC_MD5:
131    case ETYPE_DES3_CBC_MD5:
132    case ETYPE_OLD_DES3_CBC_SHA1:
133    case ETYPE_DES3_CBC_SHA1:
134    case ETYPE_ARCFOUR_HMAC_MD5:
135    case ETYPE_ARCFOUR_HMAC_MD5_56:
136	break;
137    default :
138        ctx->more_flags |= IS_CFX;
139
140	if ((acceptor && ctx->auth_context->local_subkey) ||
141	    (!acceptor && ctx->auth_context->remote_subkey))
142	    ctx->more_flags |= ACCEPTOR_SUBKEY;
143	break;
144    }
145    if (ctx->crypto)
146        krb5_crypto_destroy(context, ctx->crypto);
147    ret = krb5_crypto_init(context, key, 0, &ctx->crypto);
148}
149
150
151static OM_uint32
152gsskrb5_accept_delegated_token
153(OM_uint32 * minor_status,
154 gsskrb5_ctx ctx,
155 krb5_context context,
156 gss_cred_id_t * delegated_cred_handle
157    )
158{
159    krb5_ccache ccache = NULL;
160    krb5_error_code kret;
161    int32_t ac_flags, ret = GSS_S_COMPLETE;
162
163    *minor_status = 0;
164
165    /* XXX Create a new delegated_cred_handle? */
166    if (delegated_cred_handle == NULL) {
167	kret = krb5_cc_default (context, &ccache);
168    } else {
169	*delegated_cred_handle = NULL;
170	kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
171				   NULL, &ccache);
172    }
173    if (kret) {
174	ctx->flags &= ~GSS_C_DELEG_FLAG;
175	goto out;
176    }
177
178    kret = krb5_cc_initialize(context, ccache, ctx->source);
179    if (kret) {
180	ctx->flags &= ~GSS_C_DELEG_FLAG;
181	goto out;
182    }
183
184    krb5_auth_con_removeflags(context,
185			      ctx->auth_context,
186			      KRB5_AUTH_CONTEXT_DO_TIME,
187			      &ac_flags);
188    kret = krb5_rd_cred2(context,
189			 ctx->auth_context,
190			 ccache,
191			 &ctx->fwd_data);
192    krb5_auth_con_setflags(context,
193			   ctx->auth_context,
194			   ac_flags);
195    if (kret) {
196	ctx->flags &= ~GSS_C_DELEG_FLAG;
197	ret = GSS_S_FAILURE;
198	*minor_status = kret;
199	goto out;
200    }
201
202    if (delegated_cred_handle) {
203	gsskrb5_cred handle;
204
205	ret = _gsskrb5_krb5_import_cred(minor_status,
206					ccache,
207					NULL,
208					NULL,
209					delegated_cred_handle);
210	if (ret != GSS_S_COMPLETE)
211	    goto out;
212
213	handle = (gsskrb5_cred) *delegated_cred_handle;
214
215	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
216	krb5_cc_close(context, ccache);
217	ccache = NULL;
218    }
219
220out:
221    if (ccache) {
222	/* Don't destroy the default cred cache */
223	if (delegated_cred_handle == NULL)
224	    krb5_cc_close(context, ccache);
225	else
226	    krb5_cc_destroy(context, ccache);
227    }
228    return ret;
229}
230
231static OM_uint32
232gsskrb5_acceptor_ready(OM_uint32 * minor_status,
233		       gsskrb5_ctx ctx,
234		       krb5_context context,
235		       gss_cred_id_t *delegated_cred_handle)
236{
237    OM_uint32 ret;
238    int32_t seq_number;
239    int is_cfx = 0;
240
241    krb5_auth_con_getremoteseqnumber (context,
242				      ctx->auth_context,
243				      &seq_number);
244
245    _gsskrb5i_is_cfx(context, ctx, 1);
246    is_cfx = (ctx->more_flags & IS_CFX);
247
248    ret = _gssapi_msg_order_create(minor_status,
249				   &ctx->order,
250				   _gssapi_msg_order_f(ctx->flags),
251				   seq_number, 0, is_cfx);
252    if (ret)
253	return ret;
254
255    /*
256     * If requested, set local sequence num to remote sequence if this
257     * isn't a mutual authentication context
258     */
259    if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
260	krb5_auth_con_setlocalseqnumber(context,
261					ctx->auth_context,
262					seq_number);
263    }
264
265    /*
266     * We should handle the delegation ticket, in case it's there
267     */
268    if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
269	ret = gsskrb5_accept_delegated_token(minor_status,
270					     ctx,
271					     context,
272					     delegated_cred_handle);
273	if (ret)
274	    return ret;
275    } else {
276	/* Well, looks like it wasn't there after all */
277	ctx->flags &= ~GSS_C_DELEG_FLAG;
278    }
279
280    ctx->state = ACCEPTOR_READY;
281    ctx->more_flags |= OPEN;
282
283    return GSS_S_COMPLETE;
284}
285
286static OM_uint32
287send_error_token(OM_uint32 *minor_status,
288		 krb5_context context,
289		 krb5_error_code kret,
290		 krb5_principal server,
291		 krb5_data *indata,
292		 gss_buffer_t output_token)
293{
294    krb5_principal ap_req_server = NULL;
295    krb5_error_code ret;
296    krb5_data outbuf;
297    /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which
298       tells windows to try again with the corrected timestamp. See
299       [MS-KILE] 2.2.1 KERB-ERROR-DATA */
300    krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") };
301
302    /* build server from request if the acceptor had not selected one */
303    if (server == NULL) {
304	AP_REQ ap_req;
305
306	ret = krb5_decode_ap_req(context, indata, &ap_req);
307	if (ret) {
308	    *minor_status = ret;
309	    return GSS_S_FAILURE;
310	}
311	ret = _krb5_principalname2krb5_principal(context,
312						  &ap_req_server,
313						  ap_req.ticket.sname,
314						  ap_req.ticket.realm);
315	free_AP_REQ(&ap_req);
316	if (ret) {
317	    *minor_status = ret;
318	    return GSS_S_FAILURE;
319	}
320	server = ap_req_server;
321    }
322
323    ret = krb5_mk_error(context, kret, NULL, &e_data, NULL,
324			server, NULL, NULL, &outbuf);
325    if (ap_req_server)
326	krb5_free_principal(context, ap_req_server);
327    if (ret) {
328	*minor_status = ret;
329	return GSS_S_FAILURE;
330    }
331
332    ret = _gsskrb5_encapsulate(minor_status,
333			       &outbuf,
334			       output_token,
335			       "\x03\x00",
336			       GSS_KRB5_MECHANISM);
337    krb5_data_free (&outbuf);
338    if (ret)
339	return ret;
340
341    *minor_status = 0;
342    return GSS_S_CONTINUE_NEEDED;
343}
344
345
346static OM_uint32
347gsskrb5_acceptor_start(OM_uint32 * minor_status,
348		       gsskrb5_ctx ctx,
349		       krb5_context context,
350		       const gss_cred_id_t acceptor_cred_handle,
351		       const gss_buffer_t input_token_buffer,
352		       const gss_channel_bindings_t input_chan_bindings,
353		       gss_name_t * src_name,
354		       gss_OID * mech_type,
355		       gss_buffer_t output_token,
356		       OM_uint32 * ret_flags,
357		       OM_uint32 * time_rec,
358		       gss_cred_id_t * delegated_cred_handle)
359{
360    krb5_error_code kret;
361    OM_uint32 ret = GSS_S_COMPLETE;
362    krb5_data indata;
363    krb5_flags ap_options;
364    krb5_keytab keytab = NULL;
365    int is_cfx = 0;
366    const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
367
368    /*
369     * We may, or may not, have an escapsulation.
370     */
371    ret = _gsskrb5_decapsulate (minor_status,
372				input_token_buffer,
373				&indata,
374				"\x01\x00",
375				GSS_KRB5_MECHANISM);
376
377    if (ret) {
378	/* Assume that there is no OID wrapping. */
379	indata.length	= input_token_buffer->length;
380	indata.data	= input_token_buffer->value;
381    }
382
383    /*
384     * We need to get our keytab
385     */
386    if (acceptor_cred == NULL) {
387	if (_gsskrb5_keytab != NULL)
388	    keytab = _gsskrb5_keytab;
389    } else if (acceptor_cred->keytab != NULL) {
390	keytab = acceptor_cred->keytab;
391    }
392
393    /*
394     * We need to check the ticket and create the AP-REP packet
395     */
396
397    {
398	krb5_rd_req_in_ctx in = NULL;
399	krb5_rd_req_out_ctx out = NULL;
400	krb5_principal server = NULL;
401
402	if (acceptor_cred)
403	    server = acceptor_cred->principal;
404
405	kret = krb5_rd_req_in_ctx_alloc(context, &in);
406	if (kret == 0)
407	    kret = krb5_rd_req_in_set_keytab(context, in, keytab);
408	if (kret) {
409	    if (in)
410		krb5_rd_req_in_ctx_free(context, in);
411	    *minor_status = kret;
412	    return GSS_S_FAILURE;
413	}
414
415	kret = krb5_rd_req_ctx(context,
416			       &ctx->auth_context,
417			       &indata,
418			       server,
419			       in, &out);
420	krb5_rd_req_in_ctx_free(context, in);
421	if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
422	    /*
423	     * No reply in non-MUTUAL mode, but we don't know that its
424	     * non-MUTUAL mode yet, thats inside the 8003 checksum, so
425	     * lets only send the error token on clock skew, that
426	     * limit when send error token for non-MUTUAL.
427	     */
428	    return send_error_token(minor_status, context, kret,
429				    server, &indata, output_token);
430	} else if (kret) {
431	    *minor_status = kret;
432	    return GSS_S_FAILURE;
433	}
434
435	/*
436	 * we need to remember some data on the context_handle.
437	 */
438	kret = krb5_rd_req_out_get_ap_req_options(context, out,
439						  &ap_options);
440	if (kret == 0)
441	    kret = krb5_rd_req_out_get_ticket(context, out,
442					      &ctx->ticket);
443	if (kret == 0)
444	    kret = krb5_rd_req_out_get_keyblock(context, out,
445						&ctx->service_keyblock);
446	ctx->lifetime = ctx->ticket->ticket.endtime;
447
448	krb5_rd_req_out_ctx_free(context, out);
449	if (kret) {
450	    ret = GSS_S_FAILURE;
451	    *minor_status = kret;
452	    return ret;
453	}
454    }
455
456
457    /*
458     * We need to copy the principal names to the context and the
459     * calling layer.
460     */
461    kret = krb5_copy_principal(context,
462			       ctx->ticket->client,
463			       &ctx->source);
464    if (kret) {
465	ret = GSS_S_FAILURE;
466	*minor_status = kret;
467    }
468
469    kret = krb5_copy_principal(context,
470			       ctx->ticket->server,
471			       &ctx->target);
472    if (kret) {
473	ret = GSS_S_FAILURE;
474	*minor_status = kret;
475	return ret;
476    }
477
478    /*
479     * We need to setup some compat stuff, this assumes that
480     * context_handle->target is already set.
481     */
482    ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
483    if (ret)
484	return ret;
485
486    if (src_name != NULL) {
487	kret = krb5_copy_principal (context,
488				    ctx->ticket->client,
489				    (gsskrb5_name*)src_name);
490	if (kret) {
491	    ret = GSS_S_FAILURE;
492	    *minor_status = kret;
493	    return ret;
494	}
495    }
496
497    /*
498     * We need to get the flags out of the 8003 checksum.
499     */
500
501    {
502	krb5_authenticator authenticator;
503
504	kret = krb5_auth_con_getauthenticator(context,
505					      ctx->auth_context,
506					      &authenticator);
507	if(kret) {
508	    ret = GSS_S_FAILURE;
509	    *minor_status = kret;
510	    return ret;
511	}
512
513	if (authenticator->cksum == NULL) {
514	    krb5_free_authenticator(context, &authenticator);
515	    *minor_status = 0;
516	    return GSS_S_BAD_BINDINGS;
517	}
518
519        if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
520            ret = _gsskrb5_verify_8003_checksum(minor_status,
521						input_chan_bindings,
522						authenticator->cksum,
523						&ctx->flags,
524						&ctx->fwd_data);
525
526	    krb5_free_authenticator(context, &authenticator);
527	    if (ret) {
528		return ret;
529	    }
530        } else {
531	    krb5_crypto crypto;
532
533	    kret = krb5_crypto_init(context,
534				    ctx->auth_context->keyblock,
535				    0, &crypto);
536	    if(kret) {
537		krb5_free_authenticator(context, &authenticator);
538
539		ret = GSS_S_FAILURE;
540		*minor_status = kret;
541		return ret;
542	    }
543
544	    /*
545	     * Windows accepts Samba3's use of a kerberos, rather than
546	     * GSSAPI checksum here
547	     */
548
549	    kret = krb5_verify_checksum(context,
550					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
551					authenticator->cksum);
552	    krb5_free_authenticator(context, &authenticator);
553	    krb5_crypto_destroy(context, crypto);
554
555	    if(kret) {
556		ret = GSS_S_BAD_SIG;
557		*minor_status = kret;
558		return ret;
559	    }
560
561	    /*
562	     * Samba style get some flags (but not DCE-STYLE), use
563	     * ap_options to guess the mutual flag.
564	     */
565 	    ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
566	    if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
567		ctx->flags |= GSS_C_MUTUAL_FLAG;
568        }
569    }
570
571    if(ctx->flags & GSS_C_MUTUAL_FLAG) {
572	krb5_data outbuf;
573	int use_subkey = 0;
574
575	_gsskrb5i_is_cfx(context, ctx, 1);
576	is_cfx = (ctx->more_flags & IS_CFX);
577
578	if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
579	    use_subkey = 1;
580	} else {
581	    krb5_keyblock *rkey;
582
583	    /*
584	     * If there is a initiator subkey, copy that to acceptor
585	     * subkey to match Windows behavior
586	     */
587	    kret = krb5_auth_con_getremotesubkey(context,
588						 ctx->auth_context,
589						 &rkey);
590	    if (kret == 0) {
591		kret = krb5_auth_con_setlocalsubkey(context,
592						    ctx->auth_context,
593						    rkey);
594		if (kret == 0)
595		    use_subkey = 1;
596		krb5_free_keyblock(context, rkey);
597	    }
598	}
599	if (use_subkey) {
600	    ctx->more_flags |= ACCEPTOR_SUBKEY;
601	    krb5_auth_con_addflags(context, ctx->auth_context,
602				   KRB5_AUTH_CONTEXT_USE_SUBKEY,
603				   NULL);
604	}
605
606	kret = krb5_mk_rep(context,
607			   ctx->auth_context,
608			   &outbuf);
609	if (kret) {
610	    *minor_status = kret;
611	    return GSS_S_FAILURE;
612	}
613
614	if (IS_DCE_STYLE(ctx)) {
615	    output_token->length = outbuf.length;
616	    output_token->value = outbuf.data;
617	} else {
618	    ret = _gsskrb5_encapsulate(minor_status,
619				       &outbuf,
620				       output_token,
621				       "\x02\x00",
622				       GSS_KRB5_MECHANISM);
623	    krb5_data_free (&outbuf);
624	    if (ret)
625		return ret;
626	}
627    }
628
629    ctx->flags |= GSS_C_TRANS_FLAG;
630
631    /* Remember the flags */
632
633    ctx->lifetime = ctx->ticket->ticket.endtime;
634    ctx->more_flags |= OPEN;
635
636    if (mech_type)
637	*mech_type = GSS_KRB5_MECHANISM;
638
639    if (time_rec) {
640	ret = _gsskrb5_lifetime_left(minor_status,
641				     context,
642				     ctx->lifetime,
643				     time_rec);
644	if (ret) {
645	    return ret;
646	}
647    }
648
649    /*
650     * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
651     * the client.
652     */
653    if (IS_DCE_STYLE(ctx)) {
654	/*
655	 * Return flags to caller, but we haven't processed
656	 * delgations yet
657	 */
658	if (ret_flags)
659	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
660
661	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
662	return GSS_S_CONTINUE_NEEDED;
663    }
664
665    ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
666				 delegated_cred_handle);
667
668    if (ret_flags)
669	*ret_flags = ctx->flags;
670
671    return ret;
672}
673
674static OM_uint32
675acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
676			   gsskrb5_ctx ctx,
677			   krb5_context context,
678			   const gss_cred_id_t acceptor_cred_handle,
679			   const gss_buffer_t input_token_buffer,
680			   const gss_channel_bindings_t input_chan_bindings,
681			   gss_name_t * src_name,
682			   gss_OID * mech_type,
683			   gss_buffer_t output_token,
684			   OM_uint32 * ret_flags,
685			   OM_uint32 * time_rec,
686			   gss_cred_id_t * delegated_cred_handle)
687{
688    OM_uint32 ret;
689    krb5_error_code kret;
690    krb5_data inbuf;
691    int32_t r_seq_number, l_seq_number;
692
693    /*
694     * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
695     */
696
697    inbuf.length = input_token_buffer->length;
698    inbuf.data = input_token_buffer->value;
699
700    /*
701     * We need to remeber the old remote seq_number, then check if the
702     * client has replied with our local seq_number, and then reset
703     * the remote seq_number to the old value
704     */
705    {
706	kret = krb5_auth_con_getlocalseqnumber(context,
707					       ctx->auth_context,
708					       &l_seq_number);
709	if (kret) {
710	    *minor_status = kret;
711	    return GSS_S_FAILURE;
712	}
713
714	kret = krb5_auth_con_getremoteseqnumber(context,
715						ctx->auth_context,
716						&r_seq_number);
717	if (kret) {
718	    *minor_status = kret;
719	    return GSS_S_FAILURE;
720	}
721
722	kret = krb5_auth_con_setremoteseqnumber(context,
723						ctx->auth_context,
724						l_seq_number);
725	if (kret) {
726	    *minor_status = kret;
727	    return GSS_S_FAILURE;
728	}
729    }
730
731    /*
732     * We need to verify the AP_REP, but we need to flag that this is
733     * DCE_STYLE, so don't check the timestamps this time, but put the
734     * flag DO_TIME back afterward.
735    */
736    {
737	krb5_ap_rep_enc_part *repl;
738	int32_t auth_flags;
739
740	krb5_auth_con_removeflags(context,
741				  ctx->auth_context,
742				  KRB5_AUTH_CONTEXT_DO_TIME,
743				  &auth_flags);
744
745	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
746	if (kret) {
747	    *minor_status = kret;
748	    return GSS_S_FAILURE;
749	}
750	krb5_free_ap_rep_enc_part(context, repl);
751	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
752    }
753
754    /* We need to check the liftime */
755    {
756	OM_uint32 lifetime_rec;
757
758	ret = _gsskrb5_lifetime_left(minor_status,
759				     context,
760				     ctx->lifetime,
761				     &lifetime_rec);
762	if (ret) {
763	    return ret;
764	}
765	if (lifetime_rec == 0) {
766	    return GSS_S_CONTEXT_EXPIRED;
767	}
768
769	if (time_rec) *time_rec = lifetime_rec;
770    }
771
772    /* We need to give the caller the flags which are in use */
773    if (ret_flags) *ret_flags = ctx->flags;
774
775    if (src_name) {
776	kret = krb5_copy_principal(context,
777				   ctx->source,
778				   (gsskrb5_name*)src_name);
779	if (kret) {
780	    *minor_status = kret;
781	    return GSS_S_FAILURE;
782	}
783    }
784
785    /*
786     * After the krb5_rd_rep() the remote and local seq_number should
787     * be the same, because the client just replies the seq_number
788     * from our AP-REP in its AP-REP, but then the client uses the
789     * seq_number from its AP-REQ for GSS_wrap()
790     */
791    {
792	int32_t tmp_r_seq_number, tmp_l_seq_number;
793
794	kret = krb5_auth_con_getremoteseqnumber(context,
795						ctx->auth_context,
796						&tmp_r_seq_number);
797	if (kret) {
798	    *minor_status = kret;
799	    return GSS_S_FAILURE;
800	}
801
802	kret = krb5_auth_con_getlocalseqnumber(context,
803					       ctx->auth_context,
804					       &tmp_l_seq_number);
805	if (kret) {
806
807	    *minor_status = kret;
808	    return GSS_S_FAILURE;
809	}
810
811	/*
812	 * Here we check if the client has responsed with our local seq_number,
813	 */
814	if (tmp_r_seq_number != tmp_l_seq_number) {
815	    return GSS_S_UNSEQ_TOKEN;
816	}
817    }
818
819    /*
820     * We need to reset the remote seq_number, because the client will use,
821     * the old one for the GSS_wrap() calls
822     */
823    {
824	kret = krb5_auth_con_setremoteseqnumber(context,
825						ctx->auth_context,
826						r_seq_number);
827	if (kret) {
828	    *minor_status = kret;
829	    return GSS_S_FAILURE;
830	}
831    }
832
833    return gsskrb5_acceptor_ready(minor_status, ctx, context,
834				  delegated_cred_handle);
835}
836
837
838OM_uint32 GSSAPI_CALLCONV
839_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
840			    gss_ctx_id_t * context_handle,
841			    const gss_cred_id_t acceptor_cred_handle,
842			    const gss_buffer_t input_token_buffer,
843			    const gss_channel_bindings_t input_chan_bindings,
844			    gss_name_t * src_name,
845			    gss_OID * mech_type,
846			    gss_buffer_t output_token,
847			    OM_uint32 * ret_flags,
848			    OM_uint32 * time_rec,
849			    gss_cred_id_t * delegated_cred_handle)
850{
851    krb5_context context;
852    OM_uint32 ret;
853    gsskrb5_ctx ctx;
854
855    GSSAPI_KRB5_INIT(&context);
856
857    output_token->length = 0;
858    output_token->value = NULL;
859
860    if (src_name != NULL)
861	*src_name = NULL;
862    if (mech_type)
863	*mech_type = GSS_KRB5_MECHANISM;
864
865    if (*context_handle == GSS_C_NO_CONTEXT) {
866	ret = _gsskrb5_create_ctx(minor_status,
867				  context_handle,
868				  context,
869				  input_chan_bindings,
870				  ACCEPTOR_START);
871	if (ret)
872	    return ret;
873    }
874
875    ctx = (gsskrb5_ctx)*context_handle;
876
877
878    /*
879     * TODO: check the channel_bindings
880     * (above just sets them to krb5 layer)
881     */
882
883    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
884
885    switch (ctx->state) {
886    case ACCEPTOR_START:
887	ret = gsskrb5_acceptor_start(minor_status,
888				     ctx,
889				     context,
890				     acceptor_cred_handle,
891				     input_token_buffer,
892				     input_chan_bindings,
893				     src_name,
894				     mech_type,
895				     output_token,
896				     ret_flags,
897				     time_rec,
898				     delegated_cred_handle);
899	break;
900    case ACCEPTOR_WAIT_FOR_DCESTYLE:
901	ret = acceptor_wait_for_dcestyle(minor_status,
902					 ctx,
903					 context,
904					 acceptor_cred_handle,
905					 input_token_buffer,
906					 input_chan_bindings,
907					 src_name,
908					 mech_type,
909					 output_token,
910					 ret_flags,
911					 time_rec,
912					 delegated_cred_handle);
913	break;
914    case ACCEPTOR_READY:
915	/*
916	 * If we get there, the caller have called
917	 * gss_accept_sec_context() one time too many.
918	 */
919	ret =  GSS_S_BAD_STATUS;
920	break;
921    default:
922	/* TODO: is this correct here? --metze */
923	ret =  GSS_S_BAD_STATUS;
924	break;
925    }
926
927    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
928
929    if (GSS_ERROR(ret)) {
930	OM_uint32 min2;
931	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
932    }
933
934    return ret;
935}
936