accept_sec_context.c [plain text]
#include "k5-int.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 krb5_error_code
rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
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 = krb5_cc_store_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 =
krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
k5_mutex_destroy(&cred->lock);
retval = ENOMEM;
xfree(cred);
cred = NULL;
goto cleanup;
}
cred->usage = GSS_C_INITIATE;
cred->prerfc_mech = 1;
cred->rfc_mech = 1;
cred->keytab = NULL;
cred->tgt_expire = creds[0]->times.endtime;
cred->ccache = ccache;
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;
}
OM_uint32
krb5_gss_accept_sec_context(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)
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_context context;
unsigned char *ptr, *ptr2;
char *sptr;
long tmp;
size_t md5len;
int bigend;
krb5_gss_cred_id_t cred = 0;
krb5_data ap_rep, ap_req;
unsigned int i;
krb5_error_code code;
krb5_address addr, *paddr;
krb5_authenticator *authdat = 0;
krb5_checksum reqcksum;
krb5_principal name = NULL;
krb5_ui_4 gss_flags = 0;
int decode_req_message = 0;
krb5_gss_ctx_id_rec *ctx = 0;
krb5_timestamp now;
gss_buffer_desc token;
krb5_auth_context auth_context = NULL;
krb5_ticket * ticket = NULL;
int option_id;
krb5_data option;
const gss_OID_desc *mech_used = NULL;
OM_uint32 major_status = GSS_S_FAILURE;
OM_uint32 tmp_minor_status;
krb5_error krb_error_data;
krb5_data scratch;
gss_cred_id_t cred_handle = NULL;
krb5_gss_cred_id_t deleg_cred = NULL;
krb5int_access kaccess;
int cred_rcache = 0;
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;
reqcksum.contents = 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 (*context_handle != GSS_C_NO_CONTEXT) {
*minor_status = EINVAL;
save_error_string(EINVAL, "accept_sec_context called with existing context handle");
krb5_free_context(context);
return(GSS_S_FAILURE);
}
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, &cred_handle,
NULL, NULL);
if (major_status != GSS_S_COMPLETE) {
code = *minor_status;
goto fail;
}
} else {
major_status = krb5_gss_validate_cred(minor_status,
verifier_cred_handle);
if (GSS_ERROR(major_status)) {
code = *minor_status;
goto fail;
}
cred_handle = verifier_cred_handle;
}
cred = (krb5_gss_cred_id_t) cred_handle;
if ((cred->usage != GSS_C_ACCEPT) &&
(cred->usage != GSS_C_BOTH)) {
code = 0;
major_status = GSS_S_NO_CRED;
goto fail;
}
ptr = (unsigned char *) input_token->value;
if (!(code = g_verify_token_header(gss_mech_krb5,
&(ap_req.length),
&ptr, KG_TOK_CTX_AP_REQ,
input_token->length, 1))) {
mech_used = gss_mech_krb5;
} else if ((code == G_WRONG_MECH)
&&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
&(ap_req.length),
&ptr, KG_TOK_CTX_AP_REQ,
input_token->length, 1))) {
mech_used = gss_mech_krb5_wrong;
} else if ((code == G_WRONG_MECH) &&
!(code = g_verify_token_header(gss_mech_krb5_old,
&(ap_req.length),
&ptr, KG_TOK_CTX_AP_REQ,
input_token->length, 1))) {
mech_used = gss_mech_krb5_old;
} else 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 {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto fail;
}
sptr = (char *) ptr;
TREAD_STR(sptr, ap_req.data, ap_req.length);
decode_req_message = 1;
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;
}
if ((code = krb5_auth_con_init(context, &auth_context))) {
major_status = GSS_S_FAILURE;
save_error_info(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 ((code = krb5_rd_req(context, &auth_context, &ap_req, cred->princ,
cred->keytab, NULL, &ticket))) {
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);
#if 0
if ((authdat->authenticator->subkey == NULL) ||
(authdat->ticket->enc_part2 == NULL)) {
code = KG_NO_SUBKEY;
major_status = GSS_S_FAILURE;
goto fail;
}
#endif
{
code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
(authdat->checksum->length < 24)) {
code = 0;
major_status = GSS_S_BAD_BINDINGS;
goto fail;
}
ptr = (unsigned char *) authdat->checksum->contents;
bigend = 0;
TREAD_INT(ptr, tmp, bigend);
if (tmp != md5len) {
ptr = (unsigned char *) authdat->checksum->contents;
bigend = 1;
TREAD_INT(ptr, tmp, bigend);
if (tmp != md5len) {
code = KG_BAD_LENGTH;
major_status = GSS_S_FAILURE;
goto fail;
}
}
if ((code = kg_checksum_channel_bindings(context,
input_chan_bindings,
&reqcksum, bigend))) {
major_status = GSS_S_BAD_BINDINGS;
goto fail;
}
TREAD_STR(ptr, ptr2, reqcksum.length);
if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
xfree(reqcksum.contents);
reqcksum.contents = 0;
code = 0;
major_status = GSS_S_BAD_BINDINGS;
goto fail;
}
}
xfree(reqcksum.contents);
reqcksum.contents = 0;
TREAD_INT(ptr, gss_flags, bigend);
#if 0
gss_flags &= ~GSS_C_DELEG_FLAG;
#endif
decode_req_message = 0;
if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
i = authdat->checksum->length - 24;
if (i >= 4) {
TREAD_INT16(ptr, option_id, bigend);
TREAD_INT16(ptr, option.length, bigend);
i -= 4;
if (i < option.length || option.length < 0) {
code = KG_BAD_LENGTH;
major_status = GSS_S_FAILURE;
goto fail;
}
TREAD_STR(ptr, ptr2, option.length);
option.data = (char *) ptr2;
i -= option.length;
if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
major_status = GSS_S_FAILURE;
goto fail;
}
code = rd_and_store_for_creds(context, auth_context, &option,
(delegated_cred_handle) ?
&deleg_cred : NULL);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
}
#ifdef CFX_EXERCISE
{
FILE *f = fopen("/tmp/gsslog", "a");
if (f) {
fprintf(f,
"initial context token with delegation, %d extra bytes\n",
i);
fclose(f);
}
}
#endif
} else {
#ifdef CFX_EXERCISE
{
FILE *f = fopen("/tmp/gsslog", "a");
if (f) {
if (gss_flags & GSS_C_DELEG_FLAG)
fprintf(f,
"initial context token, delegation flag but too small\n");
else
fprintf(f,
"initial context token, %d extra bytes\n",
authdat->checksum->length - 24);
fclose(f);
}
}
#endif
}
}
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->mech_used = (gss_OID) mech_used;
ctx->auth_context = auth_context;
ctx->initiate = 0;
ctx->gss_flags = (GSS_C_TRANS_FLAG |
((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
ctx->seed_init = 0;
ctx->big_endian = bigend;
ctx->cred_rcache = cred_rcache;
if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
xfree(ctx);
ctx = 0;
code = G_VALIDATE_FAILED;
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = krb5_copy_authdata(context, ticket->enc_part2->authorization_data, &ctx->apple_authdata_if_relevant))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = krb5_copy_principal(context, ticket->server, &ctx->here))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if ((code = krb5_auth_con_getrecvsubkey(context, auth_context,
&ctx->subkey))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (ctx->subkey == NULL) {
if ((code = krb5_auth_con_getkey(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->proto = 0;
switch(ctx->subkey->enctype) {
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES_CBC_CRC:
ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
ctx->signalg = SGN_ALG_DES_MAC_MD5;
ctx->cksum_size = 8;
ctx->sealalg = SEAL_ALG_DES;
if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
major_status = GSS_S_FAILURE;
goto fail;
}
for (i=0; i<ctx->enc->length; i++)
ctx->enc->contents[i] ^= 0xf0;
goto copy_subkey_to_seq;
case ENCTYPE_DES3_CBC_SHA1:
ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
ctx->cksum_size = 20;
ctx->sealalg = SEAL_ALG_DES3KD;
copy_subkey:
if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
major_status = GSS_S_FAILURE;
goto fail;
}
copy_subkey_to_seq:
if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
major_status = GSS_S_FAILURE;
goto fail;
}
break;
case ENCTYPE_ARCFOUR_HMAC:
ctx->signalg = SGN_ALG_HMAC_MD5 ;
ctx->cksum_size = 8;
ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
goto copy_subkey;
default:
ctx->signalg = -1;
ctx->sealalg = -1;
ctx->proto = 1;
code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
&ctx->cksumtype);
if (code)
goto fail;
code = krb5_c_checksum_length(context, ctx->cksumtype,
&ctx->cksum_size);
if (code)
goto fail;
ctx->have_acceptor_subkey = 0;
goto copy_subkey;
}
ctx->endtime = ticket->enc_part2->times.endtime;
ctx->krb_flags = ticket->enc_part2->flags;
krb5_free_ticket(context, ticket);
{
krb5_ui_4 seq_temp;
krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
ctx->seq_recv = seq_temp;
}
if ((code = krb5_timeofday(context, &now))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (ctx->endtime < now) {
code = 0;
major_status = GSS_S_CREDENTIALS_EXPIRED;
goto fail;
}
g_order_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 (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
unsigned char * ptr3;
krb5_ui_4 seq_temp;
int cfx_generate_subkey;
if (ctx->proto == 1)
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 = seq_temp & 0xffffffffL;
if (cfx_generate_subkey) {
code = krb5_auth_con_getsendsubkey(context, auth_context,
&ctx->acceptor_subkey);
if (code != 0) {
major_status = GSS_S_FAILURE;
goto fail;
}
code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
ctx->acceptor_subkey->enctype,
&ctx->acceptor_subkey_cksumtype);
if (code) {
major_status = GSS_S_FAILURE;
goto fail;
}
ctx->have_acceptor_subkey = 1;
}
ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
ctx->established = 1;
token.length = g_token_size(mech_used, ap_rep.length);
if ((token.value = (unsigned char *) xmalloc(token.length))
== NULL) {
major_status = GSS_S_FAILURE;
code = ENOMEM;
goto fail;
}
ptr3 = token.value;
g_make_token_header(mech_used, ap_rep.length,
&ptr3, KG_TOK_CTX_AP_REP);
TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
ctx->established = 1;
} else {
token.length = 0;
token.value = NULL;
ctx->seq_send = ctx->seq_recv;
ctx->established = 1;
}
if (src_name) {
if ((code = krb5_copy_principal(context, ctx->there, &name))) {
major_status = GSS_S_FAILURE;
goto fail;
}
if (! kg_save_name((gss_name_t) name)) {
code = G_VALIDATE_FAILED;
major_status = GSS_S_FAILURE;
goto fail;
}
}
if (mech_type)
*mech_type = (gss_OID) mech_used;
if (time_rec)
*time_rec = ctx->endtime - now;
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 && deleg_cred) {
if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
major_status = GSS_S_FAILURE;
code = G_VALIDATE_FAILED;
goto fail;
}
*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 (reqcksum.contents)
xfree(reqcksum.contents);
if (ap_rep.data)
krb5_free_data_contents(context, &ap_rep);
if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED) {
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->princ)
krb5_free_principal(context, deleg_cred->princ);
xfree(deleg_cred);
}
if (token.value)
xfree(token.value);
if (name) {
(void) kg_delete_name((gss_name_t) name);
krb5_free_principal(context, name);
}
*minor_status = code;
if (decode_req_message) {
krb5_ap_req * request;
if (decode_krb5_ap_req(&ap_req, &request))
goto done;
if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
gss_flags |= GSS_C_MUTUAL_FLAG;
krb5_free_ap_req(context, request);
}
if (cred
&& ((gss_flags & GSS_C_MUTUAL_FLAG)
|| (major_status == GSS_S_CONTINUE_NEEDED))) {
unsigned int tmsglen;
int toktype;
memset(&krb_error_data, 0, sizeof(krb_error_data));
code -= ERROR_TABLE_BASE_krb5;
if (code < 0 || code > 128)
code = 60 ;
krb_error_data.error = code;
(void) krb5_us_timeofday(context, &krb_error_data.stime,
&krb_error_data.susec);
krb_error_data.server = cred->princ;
code = krb5_mk_error(context, &krb_error_data, &scratch);
if (code)
goto done;
tmsglen = scratch.length;
toktype = KG_TOK_CTX_ERROR;
token.length = g_token_size(mech_used, tmsglen);
token.value = (unsigned char *) xmalloc(token.length);
if (!token.value)
goto done;
ptr = token.value;
g_make_token_header(mech_used, tmsglen, &ptr, toktype);
TWRITE_STR(ptr, scratch.data, scratch.length);
krb5_free_data_contents(context, &scratch);
*output_token = token;
}
done:
if (!verifier_cred_handle && cred_handle) {
krb5_gss_release_cred(&tmp_minor_status, &cred_handle);
}
if (context) {
if (major_status && *minor_status)
save_error_info(*minor_status, context);
krb5_free_context(context);
}
return (major_status);
}
#endif