Home | History | Annotate | Line # | Download | only in krb5
      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 Hgskolan
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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