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