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