#include "k5-int.h"
#include "k5-input.h"
#include "gssapiP_krb5.h"
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <assert.h>
#ifdef CFX_EXERCISE
#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
#else
#define CFX_ACCEPTOR_SUBKEY 1
#endif
#ifndef LEAN_CLIENT
static OM_uint32
create_constrained_deleg_creds(OM_uint32 *minor_status,
krb5_gss_cred_id_t verifier_cred_handle,
krb5_ticket *ticket,
krb5_gss_cred_id_t *out_cred,
krb5_context context)
{
OM_uint32 major_status;
krb5_creds krb_creds;
krb5_data *data;
krb5_error_code code;
assert(out_cred != NULL);
assert(verifier_cred_handle->usage == GSS_C_BOTH);
memset(&krb_creds, 0, sizeof(krb_creds));
krb_creds.client = ticket->enc_part2->client;
krb_creds.server = ticket->server;
krb_creds.keyblock = *(ticket->enc_part2->session);
krb_creds.ticket_flags = ticket->enc_part2->flags;
krb_creds.times = ticket->enc_part2->times;
krb_creds.magic = KV5M_CREDS;
krb_creds.authdata = NULL;
code = encode_krb5_ticket(ticket, &data);
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
krb_creds.ticket = *data;
major_status = kg_compose_deleg_cred(minor_status,
verifier_cred_handle,
&krb_creds,
GSS_C_INDEFINITE,
out_cred,
NULL,
context);
krb5_free_data(context, data);
return major_status;
}
static krb5_error_code
rd_and_store_for_creds(krb5_context context, krb5_auth_context auth_context,
krb5_data *inbuf, krb5_gss_cred_id_t *out_cred)
{
krb5_creds ** creds = NULL;
krb5_error_code retval;
krb5_ccache ccache = NULL;
krb5_gss_cred_id_t cred = NULL;
krb5_auth_context new_auth_ctx = NULL;
krb5_int32 flags_org;
if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
return retval;
krb5_auth_con_setflags(context, auth_context,
0);
if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) {
if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
goto cleanup;
krb5_auth_con_setflags(context, new_auth_ctx, 0);
if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
&creds, NULL)))
goto cleanup;
}
if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
ccache = NULL;
goto cleanup;
}
if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
goto cleanup;
if ((retval = k5_cc_store_primary_cred(context, ccache, creds[0])))
goto cleanup;
if (out_cred) {
if (!(cred =
(krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
retval = ENOMEM;
goto cleanup;
}
memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
retval = k5_mutex_init(&cred->lock);
if (retval) {
xfree(cred);
cred = NULL;
goto cleanup;
}
if ((retval =
kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0,
&cred->name))) {
k5_mutex_destroy(&cred->lock);
retval = ENOMEM;
xfree(cred);
cred = NULL;
goto cleanup;
}
cred->usage = GSS_C_INITIATE;
cred->keytab = NULL;
cred->expire = creds[0]->times.endtime;
cred->ccache = ccache;
cred->destroy_ccache = 1;
ccache = NULL;
}
cleanup:
if (creds)
krb5_free_tgt_creds(context, creds);
if (ccache)
(void)krb5_cc_destroy(context, ccache);
if (out_cred)
*out_cred = cred;
if (new_auth_ctx)
krb5_auth_con_free(context, new_auth_ctx);
krb5_auth_con_setflags(context, auth_context, flags_org);
return retval;
}
static OM_uint32
kg_accept_dce(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle, gss_buffer_t input_token,
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 code;
krb5_gss_ctx_id_rec *ctx = 0;
krb5_timestamp now;
krb5_gss_name_t name = NULL;
krb5_ui_4 nonce = 0;
krb5_data ap_rep;
OM_uint32 major_status = GSS_S_FAILURE;
output_token->length = 0;
output_token->value = NULL;
if (mech_type)
*mech_type = GSS_C_NULL_OID;
if (delegated_cred_handle)
*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
ctx = (krb5_gss_ctx_id_rec *)*context_handle;
code = krb5_timeofday(ctx->k5_context, &now);
if (code != 0) {
major_status = GSS_S_FAILURE;
goto fail;
}
ap_rep.data = input_token->value;
ap_rep.length = input_token->length;
code = krb5_rd_rep_dce(ctx->k5_context,
ctx->auth_context,
&ap_rep,
&nonce);
if (code != 0) {
major_status = GSS_S_FAILURE;
goto fail;
}
ctx->established = 1;
if (src_name) {
code = kg_duplicate_name(ctx->k5_context, ctx->there, &name);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
*src_name = (gss_name_t) name;
}
if (mech_type)
*mech_type = ctx->mech_used;
if (time_rec) {
*time_rec = ts_interval(ts_incr(now, -ctx->k5_context->clockskew),
ctx->krb_times.endtime);
}
if (ret_flags)
*ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG);
*minor_status = 0;
return GSS_S_COMPLETE;
fail:
(void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx,
NULL);
*context_handle = GSS_C_NO_CONTEXT;
*minor_status = code;
return major_status;
}
static krb5_error_code
kg_process_extension(krb5_context context,
krb5_auth_context auth_context,
int ext_type,
krb5_data *ext_data,
krb5_gss_ctx_ext_t exts)
{
krb5_error_code code = 0;
assert(exts != NULL);
switch (ext_type) {
case GSS_EXTS_FINISHED:
if (exts->iakerb.conv == NULL) {
code = KRB5KRB_AP_ERR_MSG_TYPE;
} else {
krb5_key key;
code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
if (code != 0)
break;
code = iakerb_verify_finished(context, key, exts->iakerb.conv,
ext_data);
if (code == 0)
exts->iakerb.verified = 1;
krb5_k_free_key(context, key);
}
break;
default:
break;
}
return code;
}
#define CB_MD5_LEN 16
#define MIN_8003_LEN (4 + CB_MD5_LEN + 4)
#define INITIATOR_FLAGS (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | \
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \
GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE | \
GSS_C_IDENTIFY_FLAG | GSS_C_EXTENDED_ERROR_FLAG)
static const uint8_t null_cb[CB_MD5_LEN];
static krb5_error_code
check_cbt(krb5_context context, krb5_authdata *const *authdata,
krb5_boolean *cbt_out)
{
krb5_error_code code;
krb5_authdata **ad;
uint32_t ad_ap_options;
const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
*cbt_out = FALSE;
code = krb5_find_authdata(context, NULL, authdata,
KRB5_AUTHDATA_AP_OPTIONS, &ad);
if (code || ad == NULL)
return code;
if (ad[1] != NULL || ad[0]->length != 4) {
code = KRB5KRB_AP_ERR_MSG_TYPE;
} else {
ad_ap_options = load_32_le(ad[0]->contents);
if (ad_ap_options & KERB_AP_OPTIONS_CBT)
*cbt_out = TRUE;
}
krb5_free_authdata(context, ad);
return code;
}
static OM_uint32
process_checksum(OM_uint32 *minor_status, krb5_context context,
gss_channel_bindings_t acceptor_cb,
krb5_auth_context auth_context, krb5_flags ap_req_options,
krb5_authenticator *authenticator, krb5_gss_ctx_ext_t exts,
krb5_gss_cred_id_t *deleg_out, krb5_ui_4 *flags_out,
krb5_error_code *code_out)
{
krb5_error_code code = 0;
OM_uint32 status, option_id, token_flags;
size_t cb_len, option_len;
krb5_boolean valid, client_cbt, token_cb_present = FALSE, cb_match = FALSE;
krb5_key subkey;
krb5_data option, empty = empty_data();
krb5_checksum cb_cksum;
const uint8_t *token_cb, *option_bytes;
struct k5input in;
const krb5_checksum *cksum = authenticator->checksum;
cb_cksum.contents = NULL;
if (cksum == NULL) {
*flags_out = 0;
} else if (cksum->checksum_type != CKSUMTYPE_KG_CB) {
code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
if (code) {
status = GSS_S_FAILURE;
goto fail;
}
code = krb5_k_verify_checksum(context, subkey,
KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, &empty,
cksum, &valid);
krb5_k_free_key(context, subkey);
if (code || !valid) {
status = GSS_S_BAD_SIG;
goto fail;
}
*flags_out = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
*flags_out |= GSS_C_MUTUAL_FLAG;
} else {
if (cksum->length < MIN_8003_LEN) {
status = GSS_S_BAD_BINDINGS;
goto fail;
}
k5_input_init(&in, cksum->contents, cksum->length);
cb_len = k5_input_get_uint32_le(&in);
if (cb_len != CB_MD5_LEN) {
code = KG_BAD_LENGTH;
status = GSS_S_FAILURE;
goto fail;
}
token_cb = k5_input_get_bytes(&in, cb_len);
if (acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS) {
code = kg_checksum_channel_bindings(context, acceptor_cb,
&cb_cksum);
if (code) {
status = GSS_S_BAD_BINDINGS;
goto fail;
}
assert(cb_cksum.length == cb_len);
token_cb_present = (k5_bcmp(token_cb, null_cb, cb_len) != 0);
cb_match = (k5_bcmp(token_cb, cb_cksum.contents, cb_len) == 0);
if (token_cb_present && !cb_match) {
status = GSS_S_BAD_BINDINGS;
goto fail;
}
}
token_flags = k5_input_get_uint32_le(&in);
*flags_out = token_flags & INITIATOR_FLAGS;
if (cb_match)
*flags_out |= GSS_C_CHANNEL_BOUND_FLAG;
if (in.len >= 4 && (token_flags & GSS_C_DELEG_FLAG)) {
option_id = k5_input_get_uint16_le(&in);
option_len = k5_input_get_uint16_le(&in);
option_bytes = k5_input_get_bytes(&in, option_len);
option = make_data((uint8_t *)option_bytes, option_len);
if (in.status) {
code = KG_BAD_LENGTH;
status = GSS_S_FAILURE;
goto fail;
}
if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
status = GSS_S_FAILURE;
goto fail;
}
code = rd_and_store_for_creds(context, auth_context, &option,
deleg_out);
if (code) {
status = GSS_S_FAILURE;
goto fail;
}
*flags_out |= GSS_C_DELEG_FLAG;
}
while (in.len > 0) {
option_id = k5_input_get_uint32_be(&in);
option_len = k5_input_get_uint32_be(&in);
option_bytes = k5_input_get_bytes(&in, option_len);
option = make_data((uint8_t *)option_bytes, option_len);
if (in.status) {
code = KG_BAD_LENGTH;
status = GSS_S_FAILURE;
goto fail;
}
code = kg_process_extension(context, auth_context, option_id,
&option, exts);
if (code) {
status = GSS_S_FAILURE;
goto fail;
}
}
}
code = check_cbt(context, authenticator->authorization_data, &client_cbt);
if (code) {
status = GSS_S_FAILURE;
goto fail;
}
if (client_cbt && acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS && !cb_match) {
status = GSS_S_BAD_BINDINGS;
goto fail;
}
status = GSS_S_COMPLETE;
fail:
free(cb_cksum.contents);
*code_out = code;
return status;
}
static OM_uint32
parse_init_token(gss_buffer_t input_token, gss_const_OID *mech_used_out,
krb5_data *ap_req_out)
{
struct k5input in;
gss_OID_desc mech;
size_t tlen;
k5_input_init(&in, input_token->value, input_token->length);
if (!g_get_token_header(&in, &mech, &tlen) || tlen != input_token->length)
return G_BAD_TOK_HEADER;
if (k5_input_get_uint16_be(&in) != KG_TOK_CTX_AP_REQ)
return G_WRONG_TOKID;
if (g_OID_equal(&mech, gss_mech_krb5))
*mech_used_out = gss_mech_krb5;
else if (g_OID_equal(&mech, gss_mech_iakerb))
*mech_used_out = gss_mech_iakerb;
else if (g_OID_equal(&mech, gss_mech_krb5_wrong))
*mech_used_out = gss_mech_krb5_wrong;
else if (g_OID_equal(&mech, gss_mech_krb5_old))
*mech_used_out = gss_mech_krb5_old;
else
return G_WRONG_MECH;
*ap_req_out = make_data((uint8_t *)in.ptr, in.len);
return 0;
}
static OM_uint32
kg_accept_krb5(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle, gss_buffer_t input_token,
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_gss_ctx_ext_t exts)
{
krb5_context context;
krb5_gss_cred_id_t cred = 0;
krb5_data ap_rep, ap_req;
krb5_error_code code;
krb5_address addr, *paddr;
krb5_authenticator *authdat = 0;
krb5_gss_name_t name = NULL;
krb5_ui_4 gss_flags = 0;
krb5_gss_ctx_id_rec *ctx = NULL;
krb5_timestamp now;
gss_buffer_desc token;
krb5_auth_context auth_context = NULL;
krb5_ticket * ticket = NULL;
const gss_OID_desc *mech_used = NULL;
OM_uint32 major_status;
OM_uint32 tmp_minor_status;
krb5_error krb_error_data;
krb5_data scratch;
gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
krb5_gss_cred_id_t deleg_cred = NULL;
krb5int_access kaccess;
int cred_rcache = 0;
int no_encap = 0;
krb5_flags ap_req_options = 0;
krb5_enctype negotiated_etype;
krb5_authdata_context ad_context = NULL;
krb5_ap_req *request = NULL;
struct k5buf buf;
code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
if (code) {
*minor_status = code;
return(GSS_S_FAILURE);
}
code = krb5_gss_init_context(&context);
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
if (src_name)
*src_name = (gss_name_t) NULL;
output_token->length = 0;
output_token->value = NULL;
token.value = 0;
ap_req.data = 0;
ap_rep.data = 0;
if (mech_type)
*mech_type = GSS_C_NULL_OID;
if (delegated_cred_handle)
*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
GSS_C_ACCEPT, &defcred,
NULL, NULL);
if (major_status != GSS_S_COMPLETE) {
code = *minor_status;
goto fail;
}
verifier_cred_handle = defcred;
}
major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle,
GSS_C_NO_NAME);
if (GSS_ERROR(major_status)) {
code = *minor_status;
goto fail;
}
cred = (krb5_gss_cred_id_t)verifier_cred_handle;
if ((cred->usage != GSS_C_ACCEPT) &&
(cred->usage != GSS_C_BOTH)) {
code = 0;
major_status = GSS_S_NO_CRED;
goto fail;
}
code = parse_init_token(input_token, &mech_used, &ap_req);
if (code == G_WRONG_TOKID) {
major_status = GSS_S_CONTINUE_NEEDED;
code = KRB5KRB_AP_ERR_MSG_TYPE;
mech_used = gss_mech_krb5;
goto fail;
} else if (code == G_BAD_TOK_HEADER) {
ap_req = make_data(input_token->value, input_token->length);
mech_used = gss_mech_krb5;
no_encap = 1;
} else if (code) {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto fail;
}
if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
(input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
addr.addrtype = ADDRTYPE_INET;
addr.length = input_chan_bindings->initiator_address.length;
addr.contents = input_chan_bindings->initiator_address.value;
paddr = &addr;
} else {
paddr = NULL;
}
code = decode_krb5_ap_req(&ap_req, &request);
if (code) {
major_status = GSS_S_FAILURE;
goto done;
}
ticket = request->ticket;
if ((code = krb5_auth_con_init(context, &auth_context))) {
major_status = GSS_S_FAILURE;
save_error_info((OM_uint32)code, context);
goto fail;
}
if (cred->rcache) {
cred_rcache = 1;
if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (cred->req_enctypes) {
if ((code = krb5_auth_con_setpermetypes(context, auth_context,
cred->req_enctypes))) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
code = krb5_rd_req_decoded(context, &auth_context, request,
cred->acceptor_mprinc, cred->keytab,
&ap_req_options, NULL);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
krb5_auth_con_setflags(context, auth_context,
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
krb5_auth_con_getauthenticator(context, auth_context, &authdat);
major_status = process_checksum(minor_status, context, input_chan_bindings,
auth_context, ap_req_options,
authdat, exts,
delegated_cred_handle ? &deleg_cred : NULL,
&gss_flags, &code);
if (major_status != GSS_S_COMPLETE)
goto fail;
if (exts->iakerb.conv && !exts->iakerb.verified) {
major_status = GSS_S_BAD_SIG;
goto fail;
}
if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto fail;
}
if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
== NULL) {
code = ENOMEM;
major_status = GSS_S_FAILURE;
goto fail;
}
memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
ctx->magic = KG_CONTEXT;
ctx->mech_used = (gss_OID) mech_used;
ctx->auth_context = auth_context;
ctx->initiate = 0;
ctx->gss_flags = gss_flags | GSS_C_TRANS_FLAG;
ctx->seed_init = 0;
ctx->cred_rcache = cred_rcache;
if ( (code = krb5_merge_authdata(context,
ticket->enc_part2->authorization_data,
authdat->authorization_data,
&ctx->authdata))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0,
&ctx->here))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
&ad_context))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = kg_init_name(context, authdat->client, NULL, NULL,
ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
major_status = GSS_S_FAILURE;
goto fail;
}
authdat->client = NULL;
krb5_auth_con_set_authdata_context(context, auth_context, NULL);
if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
&ctx->subkey))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (ctx->subkey == NULL) {
if ((code = krb5_auth_con_getkey_k(context, auth_context,
&ctx->subkey))) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
if (ctx->subkey == NULL) {
major_status = GSS_S_FAILURE;
code = KRB5KDC_ERR_NULL_KEY;
goto fail;
}
ctx->enc = NULL;
ctx->seq = NULL;
ctx->have_acceptor_subkey = 0;
if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
ctx->krb_times = ticket->enc_part2->times;
ctx->krb_flags = ticket->enc_part2->flags;
if (delegated_cred_handle != NULL &&
deleg_cred == NULL &&
cred->usage == GSS_C_BOTH) {
major_status = create_constrained_deleg_creds(minor_status, cred,
ticket, &deleg_cred,
context);
if (GSS_ERROR(major_status))
goto fail;
ctx->gss_flags |= GSS_C_DELEG_FLAG;
}
{
krb5_int32 seq_temp;
krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
ctx->seq_recv = (uint32_t)seq_temp;
}
if ((code = krb5_timeofday(context, &now))) {
major_status = GSS_S_FAILURE;
goto fail;
}
code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
(ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
ctx->proto);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (ctx->gss_flags & GSS_C_DCE_STYLE)
ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
krb5_int32 seq_temp;
int cfx_generate_subkey;
if (ctx->proto == 0 &&
(ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
(ap_req_options & AP_OPTS_USE_SUBKEY)) {
code = (*kaccess.auth_con_get_subkey_enctype)(context,
auth_context,
&negotiated_etype);
if (code != 0) {
major_status = GSS_S_FAILURE;
goto fail;
}
switch (negotiated_etype) {
case ENCTYPE_DES3_CBC_SHA1:
case ENCTYPE_ARCFOUR_HMAC:
case ENCTYPE_ARCFOUR_HMAC_EXP:
ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
break;
}
}
if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
(ap_req_options & AP_OPTS_USE_SUBKEY))
cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
else
cfx_generate_subkey = 0;
if (cfx_generate_subkey) {
krb5_int32 acflags;
code = krb5_auth_con_getflags(context, auth_context, &acflags);
if (code == 0) {
acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
code = krb5_auth_con_setflags(context, auth_context, acflags);
}
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
major_status = GSS_S_FAILURE;
goto fail;
}
krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
ctx->seq_send = (uint32_t)seq_temp;
if (cfx_generate_subkey) {
code = krb5_auth_con_getsendsubkey_k(context, auth_context,
&ctx->acceptor_subkey);
if (code != 0) {
major_status = GSS_S_FAILURE;
goto fail;
}
ctx->have_acceptor_subkey = 1;
code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
&ctx->acceptor_subkey_cksumtype);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
if (ctx->gss_flags & GSS_C_DCE_STYLE) {
assert(ctx->have_acceptor_subkey);
code = data_to_gss(&ap_rep, output_token);
if (code)
{
major_status = GSS_S_FAILURE;
goto fail;
}
ctx->established = 0;
*context_handle = (gss_ctx_id_t)ctx;
*minor_status = 0;
major_status = GSS_S_CONTINUE_NEEDED;
goto fail;
} else
ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
ctx->established = 1;
token.length = g_token_size(mech_used, ap_rep.length);
token.value = gssalloc_malloc(token.length);
if (token.value == NULL) {
major_status = GSS_S_FAILURE;
code = ENOMEM;
goto fail;
}
k5_buf_init_fixed(&buf, token.value, token.length);
g_make_token_header(&buf, mech_used, ap_rep.length, KG_TOK_CTX_AP_REP);
k5_buf_add_len(&buf, ap_rep.data, ap_rep.length);
assert(buf.len == token.length);
ctx->established = 1;
} else {
token.length = 0;
token.value = NULL;
ctx->seq_send = ctx->seq_recv;
ctx->established = 1;
}
if (src_name) {
code = kg_duplicate_name(context, ctx->there, &name);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
if (mech_type)
*mech_type = (gss_OID) mech_used;
if (time_rec) {
*time_rec = ts_interval(ts_incr(now, -context->clockskew),
ctx->krb_times.endtime);
}
if (ret_flags)
*ret_flags = ctx->gss_flags;
*context_handle = (gss_ctx_id_t)ctx;
*output_token = token;
if (src_name)
*src_name = (gss_name_t) name;
if (delegated_cred_handle)
*delegated_cred_handle = (gss_cred_id_t) deleg_cred;
*minor_status = 0;
major_status = GSS_S_COMPLETE;
fail:
if (authdat)
krb5_free_authenticator(context, authdat);
if (auth_context && !ctx) {
if (cred_rcache)
(void)krb5_auth_con_setrcache(context, auth_context, NULL);
krb5_auth_con_free(context, auth_context);
}
if (ap_rep.data)
krb5_free_data_contents(context, &ap_rep);
if (major_status == GSS_S_COMPLETE ||
(major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
ctx->k5_context = context;
context = NULL;
goto done;
}
if (ctx)
(void) krb5_gss_delete_sec_context(&tmp_minor_status,
(gss_ctx_id_t *) &ctx, NULL);
if (deleg_cred) {
if (deleg_cred->ccache)
(void)krb5_cc_close(context, deleg_cred->ccache);
if (deleg_cred->name)
kg_release_name(context, &deleg_cred->name);
xfree(deleg_cred);
}
if (token.value)
xfree(token.value);
if (name) {
(void) kg_release_name(context, &name);
}
*minor_status = code;
if (cred != NULL && request != NULL &&
((gss_flags & GSS_C_MUTUAL_FLAG) ||
(request->ap_options & AP_OPTS_MUTUAL_REQUIRED) ||
major_status == GSS_S_CONTINUE_NEEDED)) {
unsigned int tmsglen;
memset(&krb_error_data, 0, sizeof(krb_error_data));
code -= ERROR_TABLE_BASE_krb5;
if (code < 0 || code > KRB_ERR_MAX)
code = 60 ;
krb_error_data.error = code;
(void) krb5_us_timeofday(context, &krb_error_data.stime,
&krb_error_data.susec);
krb_error_data.server = ticket->server;
code = krb5_mk_error(context, &krb_error_data, &scratch);
if (code)
goto done;
tmsglen = scratch.length;
token.length = g_token_size(mech_used, tmsglen);
token.value = gssalloc_malloc(token.length);
if (!token.value)
goto done;
k5_buf_init_fixed(&buf, token.value, token.length);
g_make_token_header(&buf, mech_used, tmsglen, KG_TOK_CTX_ERROR);
k5_buf_add_len(&buf, scratch.data, scratch.length);
assert(buf.len == token.length);
krb5_free_data_contents(context, &scratch);
*output_token = token;
}
done:
krb5_free_ap_req(context, request);
if (cred)
k5_mutex_unlock(&cred->lock);
if (defcred)
krb5_gss_release_cred(&tmp_minor_status, &defcred);
if (context) {
if (major_status && *minor_status)
save_error_info(*minor_status, context);
krb5_free_context(context);
}
return (major_status);
}
#endif
OM_uint32 KRB5_CALLCONV
krb5_gss_accept_sec_context_ext(
OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
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_gss_ctx_ext_t exts)
{
krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
if (ctx != NULL) {
if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
return kg_accept_dce(minor_status, context_handle,
verifier_cred_handle, input_token,
input_chan_bindings, src_name, mech_type,
output_token, ret_flags, time_rec,
delegated_cred_handle);
} else {
*minor_status = EINVAL;
save_error_string(EINVAL, "accept_sec_context called with existing context handle");
return GSS_S_FAILURE;
}
}
return kg_accept_krb5(minor_status, context_handle,
verifier_cred_handle, input_token,
input_chan_bindings, src_name, mech_type,
output_token, ret_flags, time_rec,
delegated_cred_handle, exts);
}
OM_uint32 KRB5_CALLCONV
krb5_gss_accept_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t verifier_cred_handle,
gss_buffer_t input_token,
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_gss_ctx_ext_rec exts;
memset(&exts, 0, sizeof(exts));
return krb5_gss_accept_sec_context_ext(minor_status,
context_handle,
verifier_cred_handle,
input_token,
input_chan_bindings,
src_name,
mech_type,
output_token,
ret_flags,
time_rec,
delegated_cred_handle,
&exts);
}