xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/gssapi/krb5/accept_sec_context.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
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 
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_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 
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         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 
223 out:
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 
234 static OM_uint32
gsskrb5_acceptor_ready(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_cred_id_t * delegated_cred_handle)235 gsskrb5_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 
289 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)290 send_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 
349 static OM_uint32
gsskrb5_acceptor_start(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_const_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)350 gsskrb5_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 
695 static OM_uint32
acceptor_wait_for_dcestyle(OM_uint32 * minor_status,gsskrb5_ctx ctx,krb5_context context,gss_const_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)696 acceptor_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 
859 OM_uint32 GSSAPI_CALLCONV
_gsskrb5_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_const_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)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