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