mirror of
https://github.com/samba-team/samba.git
synced 2025-02-03 13:47:25 +03:00
ddee603b5e
Some non-GSSAPI implementations that instead try to create compatible packets by wrapping krb5_mk_req() can trigger a NULL authenticator here. Assume this to be equvilent to specifying an all-zero channel bindings and some reasonable (fixed) flags. This was seen in the wild, with a Huawei Unified Storage System S5500 V3 against the AD DC Original patch by Andrew Bartlett, restructured by Douglas Bagnall Cherry-picked from upstream GIT 0a5de96d72cdea9e465412d7dba1e5d13e53dc09 which is the merge of https://github.com/heimdal/heimdal/pull/134 BUG: https://bugzilla.samba.org/show_bug.cgi?id=11425 Signed-off-by: Andrew Bartlett <abartlet@samba.org> Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Stefan Metzmacher <metze@samba.org>
935 lines
22 KiB
C
935 lines
22 KiB
C
/*
|
|
* 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_error_code ret;
|
|
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);
|
|
ret = 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;
|
|
|
|
*minor_status = 0;
|
|
|
|
/* XXX Create a new delegated_cred_handle? */
|
|
if (delegated_cred_handle == NULL) {
|
|
kret = krb5_cc_default (context, &ccache);
|
|
} else {
|
|
*delegated_cred_handle = NULL;
|
|
kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
|
|
NULL, &ccache);
|
|
}
|
|
if (kret) {
|
|
ctx->flags &= ~GSS_C_DELEG_FLAG;
|
|
goto out;
|
|
}
|
|
|
|
kret = krb5_cc_initialize(context, ccache, ctx->source);
|
|
if (kret) {
|
|
ctx->flags &= ~GSS_C_DELEG_FLAG;
|
|
goto out;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
|
|
if (delegated_cred_handle) {
|
|
gsskrb5_cred handle;
|
|
|
|
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;
|
|
krb5_cc_close(context, ccache);
|
|
ccache = NULL;
|
|
}
|
|
|
|
out:
|
|
if (ccache) {
|
|
/* Don't destroy the default cred cache */
|
|
if (delegated_cred_handle == NULL)
|
|
krb5_cc_close(context, ccache);
|
|
else
|
|
krb5_cc_destroy(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)
|
|
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,
|
|
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)
|
|
{
|
|
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;
|
|
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) {
|
|
/* 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) {
|
|
if (_gsskrb5_keytab != NULL)
|
|
keytab = _gsskrb5_keytab;
|
|
} 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);
|
|
*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 (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.
|
|
*/
|
|
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->lifetime = 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;
|
|
}
|
|
|
|
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(minor_status,
|
|
input_chan_bindings,
|
|
authenticator->cksum,
|
|
&ctx->flags,
|
|
&ctx->fwd_data);
|
|
|
|
krb5_free_authenticator(context, &authenticator);
|
|
if (ret) {
|
|
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
|
|
*/
|
|
|
|
kret = krb5_verify_checksum(context,
|
|
crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
|
|
authenticator->cksum);
|
|
krb5_free_authenticator(context, &authenticator);
|
|
krb5_crypto_destroy(context, crypto);
|
|
|
|
if(kret) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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->lifetime = 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->lifetime,
|
|
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,
|
|
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)
|
|
{
|
|
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->lifetime,
|
|
&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,
|
|
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)
|
|
{
|
|
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;
|
|
}
|