xref: /freebsd/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c (revision b077aed33b7b6aefca7b17ddb250cf521f938613)
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 
36 HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
37 krb5_keytab _gsskrb5_keytab;
38 
39 static krb5_error_code
40 validate_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 
57 OM_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 
104 void
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 
151 static OM_uint32
152 gsskrb5_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 
220 out:
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 
231 static OM_uint32
232 gsskrb5_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 
286 static OM_uint32
287 send_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 
346 static OM_uint32
347 gsskrb5_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             free_Authenticator(ctx->auth_context->authenticator);
429 	    return send_error_token(minor_status, context, kret,
430 				    server, &indata, output_token);
431 	} else if (kret) {
432 	    *minor_status = kret;
433 	    return GSS_S_FAILURE;
434 	}
435 
436 	/*
437 	 * we need to remember some data on the context_handle.
438 	 */
439 	kret = krb5_rd_req_out_get_ap_req_options(context, out,
440 						  &ap_options);
441 	if (kret == 0)
442 	    kret = krb5_rd_req_out_get_ticket(context, out,
443 					      &ctx->ticket);
444 	if (kret == 0)
445 	    kret = krb5_rd_req_out_get_keyblock(context, out,
446 						&ctx->service_keyblock);
447 	ctx->lifetime = ctx->ticket->ticket.endtime;
448 
449 	krb5_rd_req_out_ctx_free(context, out);
450 	if (kret) {
451 	    ret = GSS_S_FAILURE;
452 	    *minor_status = kret;
453 	    return ret;
454 	}
455     }
456 
457 
458     /*
459      * We need to copy the principal names to the context and the
460      * calling layer.
461      */
462     kret = krb5_copy_principal(context,
463 			       ctx->ticket->client,
464 			       &ctx->source);
465     if (kret) {
466 	ret = GSS_S_FAILURE;
467 	*minor_status = kret;
468     }
469 
470     kret = krb5_copy_principal(context,
471 			       ctx->ticket->server,
472 			       &ctx->target);
473     if (kret) {
474 	ret = GSS_S_FAILURE;
475 	*minor_status = kret;
476 	return ret;
477     }
478 
479     /*
480      * We need to setup some compat stuff, this assumes that
481      * context_handle->target is already set.
482      */
483     ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
484     if (ret)
485 	return ret;
486 
487     if (src_name != NULL) {
488 	kret = krb5_copy_principal (context,
489 				    ctx->ticket->client,
490 				    (gsskrb5_name*)src_name);
491 	if (kret) {
492 	    ret = GSS_S_FAILURE;
493 	    *minor_status = kret;
494 	    return ret;
495 	}
496     }
497 
498     /*
499      * We need to get the flags out of the 8003 checksum.
500      */
501 
502     {
503 	krb5_authenticator authenticator;
504 
505 	kret = krb5_auth_con_getauthenticator(context,
506 					      ctx->auth_context,
507 					      &authenticator);
508 	if(kret) {
509 	    ret = GSS_S_FAILURE;
510 	    *minor_status = kret;
511 	    return ret;
512 	}
513 
514 	if (authenticator->cksum == NULL) {
515 	    krb5_free_authenticator(context, &authenticator);
516 	    *minor_status = 0;
517 	    return GSS_S_BAD_BINDINGS;
518 	}
519 
520         if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
521             ret = _gsskrb5_verify_8003_checksum(minor_status,
522 						input_chan_bindings,
523 						authenticator->cksum,
524 						&ctx->flags,
525 						&ctx->fwd_data);
526 
527 	    krb5_free_authenticator(context, &authenticator);
528 	    if (ret) {
529 		return ret;
530 	    }
531         } else {
532 	    krb5_crypto crypto;
533 
534 	    kret = krb5_crypto_init(context,
535 				    ctx->auth_context->keyblock,
536 				    0, &crypto);
537 	    if(kret) {
538 		krb5_free_authenticator(context, &authenticator);
539 
540 		ret = GSS_S_FAILURE;
541 		*minor_status = kret;
542 		return ret;
543 	    }
544 
545 	    /*
546 	     * Windows accepts Samba3's use of a kerberos, rather than
547 	     * GSSAPI checksum here
548 	     */
549 
550 	    kret = krb5_verify_checksum(context,
551 					crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
552 					authenticator->cksum);
553 	    krb5_free_authenticator(context, &authenticator);
554 	    krb5_crypto_destroy(context, crypto);
555 
556 	    if(kret) {
557 		ret = GSS_S_BAD_SIG;
558 		*minor_status = kret;
559 		return ret;
560 	    }
561 
562 	    /*
563 	     * Samba style get some flags (but not DCE-STYLE), use
564 	     * ap_options to guess the mutual flag.
565 	     */
566  	    ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
567 	    if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
568 		ctx->flags |= GSS_C_MUTUAL_FLAG;
569         }
570     }
571 
572     if(ctx->flags & GSS_C_MUTUAL_FLAG) {
573 	krb5_data outbuf;
574 	int use_subkey = 0;
575 
576 	_gsskrb5i_is_cfx(context, ctx, 1);
577 	is_cfx = (ctx->more_flags & IS_CFX);
578 
579 	if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
580 	    use_subkey = 1;
581 	} else {
582 	    krb5_keyblock *rkey;
583 
584 	    /*
585 	     * If there is a initiator subkey, copy that to acceptor
586 	     * subkey to match Windows behavior
587 	     */
588 	    kret = krb5_auth_con_getremotesubkey(context,
589 						 ctx->auth_context,
590 						 &rkey);
591 	    if (kret == 0) {
592 		kret = krb5_auth_con_setlocalsubkey(context,
593 						    ctx->auth_context,
594 						    rkey);
595 		if (kret == 0)
596 		    use_subkey = 1;
597 		krb5_free_keyblock(context, rkey);
598 	    }
599 	}
600 	if (use_subkey) {
601 	    ctx->more_flags |= ACCEPTOR_SUBKEY;
602 	    krb5_auth_con_addflags(context, ctx->auth_context,
603 				   KRB5_AUTH_CONTEXT_USE_SUBKEY,
604 				   NULL);
605 	}
606 
607 	kret = krb5_mk_rep(context,
608 			   ctx->auth_context,
609 			   &outbuf);
610 	if (kret) {
611 	    *minor_status = kret;
612 	    return GSS_S_FAILURE;
613 	}
614 
615 	if (IS_DCE_STYLE(ctx)) {
616 	    output_token->length = outbuf.length;
617 	    output_token->value = outbuf.data;
618 	} else {
619 	    ret = _gsskrb5_encapsulate(minor_status,
620 				       &outbuf,
621 				       output_token,
622 				       "\x02\x00",
623 				       GSS_KRB5_MECHANISM);
624 	    krb5_data_free (&outbuf);
625 	    if (ret)
626 		return ret;
627 	}
628     }
629 
630     ctx->flags |= GSS_C_TRANS_FLAG;
631 
632     /* Remember the flags */
633 
634     ctx->lifetime = ctx->ticket->ticket.endtime;
635     ctx->more_flags |= OPEN;
636 
637     if (mech_type)
638 	*mech_type = GSS_KRB5_MECHANISM;
639 
640     if (time_rec) {
641 	ret = _gsskrb5_lifetime_left(minor_status,
642 				     context,
643 				     ctx->lifetime,
644 				     time_rec);
645 	if (ret) {
646 	    return ret;
647 	}
648     }
649 
650     /*
651      * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
652      * the client.
653      */
654     if (IS_DCE_STYLE(ctx)) {
655 	/*
656 	 * Return flags to caller, but we haven't processed
657 	 * delgations yet
658 	 */
659 	if (ret_flags)
660 	    *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
661 
662 	ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
663 	return GSS_S_CONTINUE_NEEDED;
664     }
665 
666     ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
667 				 delegated_cred_handle);
668 
669     if (ret_flags)
670 	*ret_flags = ctx->flags;
671 
672     return ret;
673 }
674 
675 static OM_uint32
676 acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
677 			   gsskrb5_ctx ctx,
678 			   krb5_context context,
679 			   const gss_cred_id_t acceptor_cred_handle,
680 			   const gss_buffer_t input_token_buffer,
681 			   const gss_channel_bindings_t input_chan_bindings,
682 			   gss_name_t * src_name,
683 			   gss_OID * mech_type,
684 			   gss_buffer_t output_token,
685 			   OM_uint32 * ret_flags,
686 			   OM_uint32 * time_rec,
687 			   gss_cred_id_t * delegated_cred_handle)
688 {
689     OM_uint32 ret;
690     krb5_error_code kret;
691     krb5_data inbuf;
692     int32_t r_seq_number, l_seq_number;
693 
694     /*
695      * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
696      */
697 
698     inbuf.length = input_token_buffer->length;
699     inbuf.data = input_token_buffer->value;
700 
701     /*
702      * We need to remeber the old remote seq_number, then check if the
703      * client has replied with our local seq_number, and then reset
704      * the remote seq_number to the old value
705      */
706     {
707 	kret = krb5_auth_con_getlocalseqnumber(context,
708 					       ctx->auth_context,
709 					       &l_seq_number);
710 	if (kret) {
711 	    *minor_status = kret;
712 	    return GSS_S_FAILURE;
713 	}
714 
715 	kret = krb5_auth_con_getremoteseqnumber(context,
716 						ctx->auth_context,
717 						&r_seq_number);
718 	if (kret) {
719 	    *minor_status = kret;
720 	    return GSS_S_FAILURE;
721 	}
722 
723 	kret = krb5_auth_con_setremoteseqnumber(context,
724 						ctx->auth_context,
725 						l_seq_number);
726 	if (kret) {
727 	    *minor_status = kret;
728 	    return GSS_S_FAILURE;
729 	}
730     }
731 
732     /*
733      * We need to verify the AP_REP, but we need to flag that this is
734      * DCE_STYLE, so don't check the timestamps this time, but put the
735      * flag DO_TIME back afterward.
736     */
737     {
738 	krb5_ap_rep_enc_part *repl;
739 	int32_t auth_flags;
740 
741 	krb5_auth_con_removeflags(context,
742 				  ctx->auth_context,
743 				  KRB5_AUTH_CONTEXT_DO_TIME,
744 				  &auth_flags);
745 
746 	kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
747 	if (kret) {
748 	    *minor_status = kret;
749 	    return GSS_S_FAILURE;
750 	}
751 	krb5_free_ap_rep_enc_part(context, repl);
752 	krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
753     }
754 
755     /* We need to check the liftime */
756     {
757 	OM_uint32 lifetime_rec;
758 
759 	ret = _gsskrb5_lifetime_left(minor_status,
760 				     context,
761 				     ctx->lifetime,
762 				     &lifetime_rec);
763 	if (ret) {
764 	    return ret;
765 	}
766 	if (lifetime_rec == 0) {
767 	    return GSS_S_CONTEXT_EXPIRED;
768 	}
769 
770 	if (time_rec) *time_rec = lifetime_rec;
771     }
772 
773     /* We need to give the caller the flags which are in use */
774     if (ret_flags) *ret_flags = ctx->flags;
775 
776     if (src_name) {
777 	kret = krb5_copy_principal(context,
778 				   ctx->source,
779 				   (gsskrb5_name*)src_name);
780 	if (kret) {
781 	    *minor_status = kret;
782 	    return GSS_S_FAILURE;
783 	}
784     }
785 
786     /*
787      * After the krb5_rd_rep() the remote and local seq_number should
788      * be the same, because the client just replies the seq_number
789      * from our AP-REP in its AP-REP, but then the client uses the
790      * seq_number from its AP-REQ for GSS_wrap()
791      */
792     {
793 	int32_t tmp_r_seq_number, tmp_l_seq_number;
794 
795 	kret = krb5_auth_con_getremoteseqnumber(context,
796 						ctx->auth_context,
797 						&tmp_r_seq_number);
798 	if (kret) {
799 	    *minor_status = kret;
800 	    return GSS_S_FAILURE;
801 	}
802 
803 	kret = krb5_auth_con_getlocalseqnumber(context,
804 					       ctx->auth_context,
805 					       &tmp_l_seq_number);
806 	if (kret) {
807 
808 	    *minor_status = kret;
809 	    return GSS_S_FAILURE;
810 	}
811 
812 	/*
813 	 * Here we check if the client has responsed with our local seq_number,
814 	 */
815 	if (tmp_r_seq_number != tmp_l_seq_number) {
816 	    return GSS_S_UNSEQ_TOKEN;
817 	}
818     }
819 
820     /*
821      * We need to reset the remote seq_number, because the client will use,
822      * the old one for the GSS_wrap() calls
823      */
824     {
825 	kret = krb5_auth_con_setremoteseqnumber(context,
826 						ctx->auth_context,
827 						r_seq_number);
828 	if (kret) {
829 	    *minor_status = kret;
830 	    return GSS_S_FAILURE;
831 	}
832     }
833 
834     return gsskrb5_acceptor_ready(minor_status, ctx, context,
835 				  delegated_cred_handle);
836 }
837 
838 
839 OM_uint32 GSSAPI_CALLCONV
840 _gsskrb5_accept_sec_context(OM_uint32 * minor_status,
841 			    gss_ctx_id_t * context_handle,
842 			    const gss_cred_id_t acceptor_cred_handle,
843 			    const gss_buffer_t input_token_buffer,
844 			    const gss_channel_bindings_t input_chan_bindings,
845 			    gss_name_t * src_name,
846 			    gss_OID * mech_type,
847 			    gss_buffer_t output_token,
848 			    OM_uint32 * ret_flags,
849 			    OM_uint32 * time_rec,
850 			    gss_cred_id_t * delegated_cred_handle)
851 {
852     krb5_context context;
853     OM_uint32 ret;
854     gsskrb5_ctx ctx;
855 
856     GSSAPI_KRB5_INIT(&context);
857 
858     output_token->length = 0;
859     output_token->value = NULL;
860 
861     if (src_name != NULL)
862 	*src_name = NULL;
863     if (mech_type)
864 	*mech_type = GSS_KRB5_MECHANISM;
865 
866     if (*context_handle == GSS_C_NO_CONTEXT) {
867 	ret = _gsskrb5_create_ctx(minor_status,
868 				  context_handle,
869 				  context,
870 				  input_chan_bindings,
871 				  ACCEPTOR_START);
872 	if (ret)
873 	    return ret;
874     }
875 
876     ctx = (gsskrb5_ctx)*context_handle;
877 
878 
879     /*
880      * TODO: check the channel_bindings
881      * (above just sets them to krb5 layer)
882      */
883 
884     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
885 
886     switch (ctx->state) {
887     case ACCEPTOR_START:
888 	ret = gsskrb5_acceptor_start(minor_status,
889 				     ctx,
890 				     context,
891 				     acceptor_cred_handle,
892 				     input_token_buffer,
893 				     input_chan_bindings,
894 				     src_name,
895 				     mech_type,
896 				     output_token,
897 				     ret_flags,
898 				     time_rec,
899 				     delegated_cred_handle);
900 	break;
901     case ACCEPTOR_WAIT_FOR_DCESTYLE:
902 	ret = acceptor_wait_for_dcestyle(minor_status,
903 					 ctx,
904 					 context,
905 					 acceptor_cred_handle,
906 					 input_token_buffer,
907 					 input_chan_bindings,
908 					 src_name,
909 					 mech_type,
910 					 output_token,
911 					 ret_flags,
912 					 time_rec,
913 					 delegated_cred_handle);
914 	break;
915     case ACCEPTOR_READY:
916 	/*
917 	 * If we get there, the caller have called
918 	 * gss_accept_sec_context() one time too many.
919 	 */
920 	ret =  GSS_S_BAD_STATUS;
921 	break;
922     default:
923 	/* TODO: is this correct here? --metze */
924 	ret =  GSS_S_BAD_STATUS;
925 	break;
926     }
927 
928     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
929 
930     if (GSS_ERROR(ret)) {
931 	OM_uint32 min2;
932 	_gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
933     }
934 
935     return ret;
936 }
937