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