diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c')
-rw-r--r-- | third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c | 978 |
1 files changed, 978 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c b/third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c new file mode 100644 index 0000000..3f8e274 --- /dev/null +++ b/third_party/heimdal/lib/gssapi/krb5/accept_sec_context.c @@ -0,0 +1,978 @@ +/* + * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gsskrb5_locl.h" + +HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER; +krb5_keytab _gsskrb5_keytab; + +static krb5_error_code +validate_keytab(krb5_context context, const char *name, krb5_keytab *id) +{ + krb5_error_code ret; + + ret = krb5_kt_resolve(context, name, id); + if (ret) + return ret; + + ret = krb5_kt_have_content(context, *id); + if (ret) { + krb5_kt_close(context, *id); + *id = NULL; + } + + return ret; +} + +OM_uint32 +_gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity) +{ + krb5_context context; + krb5_error_code ret; + + *min_stat = 0; + + ret = _gsskrb5_init(&context); + if(ret) + return GSS_S_FAILURE; + + HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); + + if(_gsskrb5_keytab != NULL) { + krb5_kt_close(context, _gsskrb5_keytab); + _gsskrb5_keytab = NULL; + } + if (identity == NULL) { + ret = krb5_kt_default(context, &_gsskrb5_keytab); + } else { + /* + * First check if we can the keytab as is and if it has content... + */ + ret = validate_keytab(context, identity, &_gsskrb5_keytab); + /* + * if it doesn't, lets prepend FILE: and try again + */ + if (ret) { + char *p = NULL; + ret = asprintf(&p, "FILE:%s", identity); + if(ret < 0 || p == NULL) { + HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); + return GSS_S_FAILURE; + } + ret = validate_keytab(context, p, &_gsskrb5_keytab); + free(p); + } + } + HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); + if(ret) { + *min_stat = ret; + return GSS_S_FAILURE; + } + return GSS_S_COMPLETE; +} + +void +_gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor) +{ + krb5_keyblock *key; + + if (acceptor) { + if (ctx->auth_context->local_subkey) + key = ctx->auth_context->local_subkey; + else + key = ctx->auth_context->remote_subkey; + } else { + if (ctx->auth_context->remote_subkey) + key = ctx->auth_context->remote_subkey; + else + key = ctx->auth_context->local_subkey; + } + if (key == NULL) + key = ctx->auth_context->keyblock; + + if (key == NULL) + return; + + switch (key->keytype) { + case ETYPE_DES_CBC_CRC: + case ETYPE_DES_CBC_MD4: + case ETYPE_DES_CBC_MD5: + case ETYPE_DES3_CBC_MD5: + case ETYPE_OLD_DES3_CBC_SHA1: + case ETYPE_DES3_CBC_SHA1: + case ETYPE_ARCFOUR_HMAC_MD5: + case ETYPE_ARCFOUR_HMAC_MD5_56: + break; + default : + ctx->more_flags |= IS_CFX; + + if ((acceptor && ctx->auth_context->local_subkey) || + (!acceptor && ctx->auth_context->remote_subkey)) + ctx->more_flags |= ACCEPTOR_SUBKEY; + break; + } + if (ctx->crypto) + krb5_crypto_destroy(context, ctx->crypto); + /* XXX We really shouldn't ignore this; will come back to this */ + (void) krb5_crypto_init(context, key, 0, &ctx->crypto); +} + + +static OM_uint32 +gsskrb5_accept_delegated_token(OM_uint32 *minor_status, + gsskrb5_ctx ctx, + krb5_context context, + gss_cred_id_t *delegated_cred_handle) +{ + krb5_ccache ccache = NULL; + krb5_error_code kret; + int32_t ac_flags, ret = GSS_S_COMPLETE; + gsskrb5_cred handle; + + *minor_status = 0; + + /* XXX Create a new delegated_cred_handle? */ + if (delegated_cred_handle == NULL) + return GSS_S_COMPLETE; + + *delegated_cred_handle = NULL; + kret = krb5_cc_resolve(context, "MEMORY:anonymous", &ccache); + if (kret == 0) + kret = krb5_cc_initialize(context, ccache, ctx->source); + if (kret == 0) { + (void) krb5_auth_con_removeflags(context, + ctx->auth_context, + KRB5_AUTH_CONTEXT_DO_TIME, + &ac_flags); + kret = krb5_rd_cred2(context, + ctx->auth_context, + ccache, + &ctx->fwd_data); + (void) krb5_auth_con_setflags(context, + ctx->auth_context, + ac_flags); + } + if (kret) { + ctx->flags &= ~GSS_C_DELEG_FLAG; + ret = GSS_S_FAILURE; + *minor_status = kret; + goto out; + } + + ret = _gsskrb5_krb5_import_cred(minor_status, + &ccache, + NULL, + NULL, + delegated_cred_handle); + if (ret != GSS_S_COMPLETE) + goto out; + + handle = (gsskrb5_cred) *delegated_cred_handle; + handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; + + /* + * A root TGT is one of the form krbtgt/REALM@SAME-REALM. + * + * A destination TGT is a root TGT for the same realm as the acceptor + * service's realm. + * + * Normally clients delegate a root TGT for the client's realm. + * + * In some deployments clients may want to delegate destination TGTs as + * a form of constrained delegation: so that the destination service + * cannot use the delegated credential to impersonate the client + * principal to services in its home realm (due to KDC lineage/transit + * checks). In those deployments there may not even be a route back to + * the KDCs of the client's realm, and attempting to use a + * non-destination TGT might even lead to timeouts. + * + * We could simply pretend not to have obtained a credential, except + * that a) we don't (yet) have an app name here for the appdefault we + * need to check, b) the application really wants to be able to log a + * message about the delegated credential being no good. + * + * Thus we leave it to _gsskrb5_store_cred_into2() to decide what to do + * with non-destination TGTs. To do that, it needs the realm of the + * acceptor service, which we record here. + */ + handle->destination_realm = + strdup(krb5_principal_get_realm(context, ctx->target)); + if (handle->destination_realm == NULL) { + _gsskrb5_release_cred(minor_status, delegated_cred_handle); + *minor_status = krb5_enomem(context); + ret = GSS_S_FAILURE; + goto out; + } + +out: + if (ccache) { + krb5_cc_close(context, ccache); + } + return ret; +} + +static OM_uint32 +gsskrb5_acceptor_ready(OM_uint32 * minor_status, + gsskrb5_ctx ctx, + krb5_context context, + gss_cred_id_t *delegated_cred_handle) +{ + OM_uint32 ret; + int32_t seq_number; + int is_cfx = 0; + + krb5_auth_con_getremoteseqnumber (context, + ctx->auth_context, + &seq_number); + + _gsskrb5i_is_cfx(context, ctx, 1); + is_cfx = (ctx->more_flags & IS_CFX); + + ret = _gssapi_msg_order_create(minor_status, + &ctx->order, + _gssapi_msg_order_f(ctx->flags), + seq_number, 0, is_cfx); + if (ret) + return ret; + + /* + * If requested, set local sequence num to remote sequence if this + * isn't a mutual authentication context + */ + if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) { + krb5_auth_con_setlocalseqnumber(context, + ctx->auth_context, + seq_number); + } + + /* + * We should handle the delegation ticket, in case it's there + */ + if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) { + ret = gsskrb5_accept_delegated_token(minor_status, + ctx, + context, + delegated_cred_handle); + if (ret != GSS_S_COMPLETE) + return ret; + } else { + /* Well, looks like it wasn't there after all */ + ctx->flags &= ~GSS_C_DELEG_FLAG; + } + + ctx->state = ACCEPTOR_READY; + ctx->more_flags |= OPEN; + + return GSS_S_COMPLETE; +} + +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) +{ + krb5_principal ap_req_server = NULL; + krb5_error_code ret; + krb5_data outbuf; + /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which + tells windows to try again with the corrected timestamp. See + [MS-KILE] 2.2.1 KERB-ERROR-DATA */ + krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") }; + + /* build server from request if the acceptor had not selected one */ + if (server == NULL) { + AP_REQ ap_req; + + ret = krb5_decode_ap_req(context, indata, &ap_req); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + ret = _krb5_principalname2krb5_principal(context, + &ap_req_server, + ap_req.ticket.sname, + ap_req.ticket.realm); + free_AP_REQ(&ap_req); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + server = ap_req_server; + } + + ret = krb5_mk_error(context, kret, NULL, &e_data, NULL, + server, NULL, NULL, &outbuf); + if (ap_req_server) + krb5_free_principal(context, ap_req_server); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = _gsskrb5_encapsulate(minor_status, + &outbuf, + output_token, + "\x03\x00", + GSS_KRB5_MECHANISM); + krb5_data_free (&outbuf); + if (ret) + return ret; + + *minor_status = 0; + return GSS_S_CONTINUE_NEEDED; +} + + +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) +{ + krb5_error_code kret; + OM_uint32 ret = GSS_S_COMPLETE; + krb5_data indata; + krb5_flags ap_options; + krb5_keytab keytab = NULL; + int is_cfx = 0; + int close_kt = 0; + const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; + + /* + * We may, or may not, have an escapsulation. + */ + ret = _gsskrb5_decapsulate (minor_status, + input_token_buffer, + &indata, + "\x01\x00", + GSS_KRB5_MECHANISM); + + if (ret) { + /* Could be a raw AP-REQ (check for APPLICATION tag) */ + if (input_token_buffer->length == 0 || + ((const uint8_t *)input_token_buffer->value)[0] != 0x6E) { + *minor_status = ASN1_MISPLACED_FIELD; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* Assume that there is no OID wrapping. */ + indata.length = input_token_buffer->length; + indata.data = input_token_buffer->value; + } + + /* + * We need to get our keytab + */ + if (acceptor_cred == NULL) { + HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); + if (_gsskrb5_keytab != NULL) { + char *name = NULL; + kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name); + if (kret == 0) { + kret = krb5_kt_resolve(context, name, &keytab); + krb5_xfree(name); + } + if (kret == 0) + close_kt = 1; + else + keytab = NULL; + } + HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); + } else if (acceptor_cred->keytab != NULL) { + keytab = acceptor_cred->keytab; + } + + /* + * We need to check the ticket and create the AP-REP packet + */ + + { + krb5_rd_req_in_ctx in = NULL; + krb5_rd_req_out_ctx out = NULL; + krb5_principal server = NULL; + + if (acceptor_cred) + server = acceptor_cred->principal; + + kret = krb5_rd_req_in_ctx_alloc(context, &in); + if (kret == 0) + kret = krb5_rd_req_in_set_keytab(context, in, keytab); + if (kret) { + if (in) + krb5_rd_req_in_ctx_free(context, in); + if (close_kt) + krb5_kt_close(context, keytab); + *minor_status = kret; + return GSS_S_FAILURE; + } + + kret = krb5_rd_req_ctx(context, + &ctx->auth_context, + &indata, + server, + in, &out); + krb5_rd_req_in_ctx_free(context, in); + if (close_kt) + krb5_kt_close(context, keytab); + if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) { + /* + * No reply in non-MUTUAL mode, but we don't know that its + * non-MUTUAL mode yet, thats inside the 8003 checksum, so + * lets only send the error token on clock skew, that + * limit when send error token for non-MUTUAL. + */ + krb5_auth_con_free(context, ctx->auth_context); + krb5_auth_con_free(context, ctx->deleg_auth_context); + ctx->deleg_auth_context = NULL; + ctx->auth_context = NULL; + return send_error_token(minor_status, context, kret, + server, &indata, output_token); + } else if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + + /* + * we need to remember some data on the context_handle. + */ + kret = krb5_rd_req_out_get_ap_req_options(context, out, + &ap_options); + if (kret == 0) + kret = krb5_rd_req_out_get_ticket(context, out, + &ctx->ticket); + if (kret == 0) + kret = krb5_rd_req_out_get_keyblock(context, out, + &ctx->service_keyblock); + ctx->endtime = ctx->ticket->ticket.endtime; + + krb5_rd_req_out_ctx_free(context, out); + if (kret) { + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + } + + + /* + * We need to copy the principal names to the context and the + * calling layer. + */ + kret = krb5_copy_principal(context, + ctx->ticket->client, + &ctx->source); + if (kret) { + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + + kret = krb5_copy_principal(context, + ctx->ticket->server, + &ctx->target); + if (kret) { + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + + /* + * We need to setup some compat stuff, this assumes that + * context_handle->target is already set. + */ + ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); + if (ret) + return ret; + + if (src_name != NULL) { + kret = krb5_copy_principal (context, + ctx->ticket->client, + (gsskrb5_name*)src_name); + if (kret) { + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + } + + /* + * We need to get the flags out of the 8003 checksum. + */ + + { + krb5_authenticator authenticator; + + kret = krb5_auth_con_getauthenticator(context, + ctx->auth_context, + &authenticator); + if(kret) { + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + + if (authenticator->cksum != NULL + && authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { + ret = _gsskrb5_verify_8003_checksum(context, + minor_status, + input_chan_bindings, + authenticator, + &ctx->flags, + &ctx->fwd_data); + + if (ret) { + krb5_free_authenticator(context, &authenticator); + return ret; + } + } else { + if (authenticator->cksum != NULL) { + krb5_crypto crypto; + + kret = krb5_crypto_init(context, + ctx->auth_context->keyblock, + 0, &crypto); + if (kret) { + krb5_free_authenticator(context, &authenticator); + ret = GSS_S_FAILURE; + *minor_status = kret; + return ret; + } + + /* + * Windows accepts Samba3's use of a kerberos, rather than + * GSSAPI checksum here + */ + + _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM); + kret = krb5_verify_checksum(context, + crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, + authenticator->cksum); + krb5_crypto_destroy(context, crypto); + + if (kret) { + krb5_free_authenticator(context, &authenticator); + ret = GSS_S_BAD_SIG; + *minor_status = kret; + return ret; + } + } + + /* + * If there is no checksum or a kerberos checksum (which Windows + * and Samba accept), we use the ap_options to guess the mutual + * flag. + */ + + ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; + if (ap_options & AP_OPTS_MUTUAL_REQUIRED) + ctx->flags |= GSS_C_MUTUAL_FLAG; + } + krb5_free_authenticator(context, &authenticator); + } + + if(ctx->flags & GSS_C_MUTUAL_FLAG) { + krb5_data outbuf; + int use_subkey = 0; + + _gsskrb5i_is_cfx(context, ctx, 1); + is_cfx = (ctx->more_flags & IS_CFX); + + if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { + use_subkey = 1; + } else { + krb5_keyblock *rkey; + + /* + * If there is a initiator subkey, copy that to acceptor + * subkey to match Windows behavior + */ + kret = krb5_auth_con_getremotesubkey(context, + ctx->auth_context, + &rkey); + if (kret == 0) { + kret = krb5_auth_con_setlocalsubkey(context, + ctx->auth_context, + rkey); + if (kret == 0) + use_subkey = 1; + } + krb5_free_keyblock(context, rkey); + } + if (use_subkey) { + ctx->more_flags |= ACCEPTOR_SUBKEY; + krb5_auth_con_addflags(context, ctx->auth_context, + KRB5_AUTH_CONTEXT_USE_SUBKEY, + NULL); + } + + kret = krb5_mk_rep(context, + ctx->auth_context, + &outbuf); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + + if (IS_DCE_STYLE(ctx)) { + output_token->length = outbuf.length; + output_token->value = outbuf.data; + } else { + ret = _gsskrb5_encapsulate(minor_status, + &outbuf, + output_token, + "\x02\x00", + GSS_KRB5_MECHANISM); + krb5_data_free (&outbuf); + if (ret) + return ret; + } + } + + ctx->flags |= GSS_C_TRANS_FLAG; + + /* Remember the flags */ + + ctx->endtime = ctx->ticket->ticket.endtime; + ctx->more_flags |= OPEN; + + if (mech_type) + *mech_type = GSS_KRB5_MECHANISM; + + if (time_rec) { + ret = _gsskrb5_lifetime_left(minor_status, + context, + ctx->endtime, + time_rec); + if (ret) { + return ret; + } + } + + /* + * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from + * the client. + */ + if (IS_DCE_STYLE(ctx)) { + /* + * Return flags to caller, but we haven't processed + * delgations yet + */ + if (ret_flags) + *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); + + ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE; + return GSS_S_CONTINUE_NEEDED; + } + + ret = gsskrb5_acceptor_ready(minor_status, ctx, context, + delegated_cred_handle); + + if (ret_flags) + *ret_flags = ctx->flags; + + return ret; +} + +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) +{ + OM_uint32 ret; + krb5_error_code kret; + krb5_data inbuf; + int32_t r_seq_number, l_seq_number; + + /* + * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP + */ + + inbuf.length = input_token_buffer->length; + inbuf.data = input_token_buffer->value; + + /* + * We need to remeber the old remote seq_number, then check if the + * client has replied with our local seq_number, and then reset + * the remote seq_number to the old value + */ + { + kret = krb5_auth_con_getlocalseqnumber(context, + ctx->auth_context, + &l_seq_number); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + + kret = krb5_auth_con_getremoteseqnumber(context, + ctx->auth_context, + &r_seq_number); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + + kret = krb5_auth_con_setremoteseqnumber(context, + ctx->auth_context, + l_seq_number); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + } + + /* + * We need to verify the AP_REP, but we need to flag that this is + * DCE_STYLE, so don't check the timestamps this time, but put the + * flag DO_TIME back afterward. + */ + { + krb5_ap_rep_enc_part *repl; + int32_t auth_flags; + + krb5_auth_con_removeflags(context, + ctx->auth_context, + KRB5_AUTH_CONTEXT_DO_TIME, + &auth_flags); + + kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + krb5_free_ap_rep_enc_part(context, repl); + krb5_auth_con_setflags(context, ctx->auth_context, auth_flags); + } + + /* We need to check the liftime */ + { + OM_uint32 lifetime_rec; + + ret = _gsskrb5_lifetime_left(minor_status, + context, + ctx->endtime, + &lifetime_rec); + if (ret) { + return ret; + } + if (lifetime_rec == 0) { + return GSS_S_CONTEXT_EXPIRED; + } + + if (time_rec) *time_rec = lifetime_rec; + } + + /* We need to give the caller the flags which are in use */ + if (ret_flags) *ret_flags = ctx->flags; + + if (src_name) { + kret = krb5_copy_principal(context, + ctx->source, + (gsskrb5_name*)src_name); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + } + + /* + * After the krb5_rd_rep() the remote and local seq_number should + * be the same, because the client just replies the seq_number + * from our AP-REP in its AP-REP, but then the client uses the + * seq_number from its AP-REQ for GSS_wrap() + */ + { + int32_t tmp_r_seq_number, tmp_l_seq_number; + + kret = krb5_auth_con_getremoteseqnumber(context, + ctx->auth_context, + &tmp_r_seq_number); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + + kret = krb5_auth_con_getlocalseqnumber(context, + ctx->auth_context, + &tmp_l_seq_number); + if (kret) { + + *minor_status = kret; + return GSS_S_FAILURE; + } + + /* + * Here we check if the client has responsed with our local seq_number, + */ + if (tmp_r_seq_number != tmp_l_seq_number) { + return GSS_S_UNSEQ_TOKEN; + } + } + + /* + * We need to reset the remote seq_number, because the client will use, + * the old one for the GSS_wrap() calls + */ + { + kret = krb5_auth_con_setremoteseqnumber(context, + ctx->auth_context, + r_seq_number); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + } + + return gsskrb5_acceptor_ready(minor_status, ctx, context, + delegated_cred_handle); +} + + +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) +{ + krb5_context context; + OM_uint32 ret; + gsskrb5_ctx ctx; + + GSSAPI_KRB5_INIT(&context); + + output_token->length = 0; + output_token->value = NULL; + + if (src_name != NULL) + *src_name = NULL; + if (mech_type) + *mech_type = GSS_KRB5_MECHANISM; + + if (*context_handle == GSS_C_NO_CONTEXT) { + ret = _gsskrb5_create_ctx(minor_status, + context_handle, + context, + input_chan_bindings, + ACCEPTOR_START); + if (ret) + return ret; + } + + ctx = (gsskrb5_ctx)*context_handle; + + + /* + * TODO: check the channel_bindings + * (above just sets them to krb5 layer) + */ + + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); + + switch (ctx->state) { + case ACCEPTOR_START: + ret = gsskrb5_acceptor_start(minor_status, + ctx, + context, + acceptor_cred_handle, + input_token_buffer, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle); + break; + case ACCEPTOR_WAIT_FOR_DCESTYLE: + ret = acceptor_wait_for_dcestyle(minor_status, + ctx, + context, + acceptor_cred_handle, + input_token_buffer, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle); + break; + case ACCEPTOR_READY: + /* + * If we get there, the caller have called + * gss_accept_sec_context() one time too many. + */ + ret = GSS_S_BAD_STATUS; + break; + default: + /* TODO: is this correct here? --metze */ + ret = GSS_S_BAD_STATUS; + break; + } + + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + + if (GSS_ERROR(ret)) { + OM_uint32 min2; + _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); + } + + return ret; +} |