Partial FAST
This commit is contained in:

committed by
Love Hörnquist Åstrand

parent
0ca5e44955
commit
7b398263da
@@ -75,17 +75,20 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_prompter_fct prompter;
|
||||
void *prompter_data;
|
||||
|
||||
krb5_ccache fast_ccache;
|
||||
|
||||
struct pa_info_data *ppaid;
|
||||
struct fast_state {
|
||||
int flags;
|
||||
enum PA_FX_FAST_REQUEST_enum type;
|
||||
unsigned int flags;
|
||||
#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
|
||||
#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
|
||||
#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
|
||||
#define KRB5_FAST_REPLY_REPLY_VERIFED 8
|
||||
#define KRB5_FAST_STRONG 16
|
||||
#define KRB5_FAST_EXPECTED 32
|
||||
krb5_keyblock *reply_key;
|
||||
krb5_ccache armor_ccache;
|
||||
krb5_crypto armor_crypto;
|
||||
krb5_keyblock armor_key;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
@@ -150,6 +153,15 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
memset(ctx->password, 0, strlen(ctx->password));
|
||||
free(ctx->password);
|
||||
}
|
||||
/*
|
||||
* FAST state
|
||||
*/
|
||||
if (ctx->fast_state.armor_ccache)
|
||||
krb5_cc_close(context, ctx->fast_state.armor_ccache);
|
||||
if (ctx->fast_state.armor_crypto)
|
||||
krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
|
||||
krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
|
||||
|
||||
krb5_data_free(&ctx->req_buffer);
|
||||
krb5_free_cred_contents(context, &ctx->cred);
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
@@ -1637,10 +1649,240 @@ krb5_init_creds_set_fast_ccache(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_ccache fast_ccache)
|
||||
{
|
||||
ctx->fast_ccache = fast_ccache;
|
||||
ctx->fast_state.armor_ccache = fast_ccache;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FAST
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
check_fast(krb5_context context, struct fast_state *state)
|
||||
{
|
||||
if (state->flags & KRB5_FAST_EXPECTED) {
|
||||
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
|
||||
"Expected FAST not not found");
|
||||
return KRB5KRB_AP_ERR_MODIFIED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_as_rep(krb5_context context, struct fast_state *state, AS_REP *rep)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
/*
|
||||
unwrap_req(rep);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
make_fast_ap_fxarmor(krb5_context context,
|
||||
struct fast_state *state,
|
||||
KrbFastArmor **armor)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
krb5_auth_context auth_context = NULL;
|
||||
krb5_creds cred, *credp = NULL;
|
||||
krb5_error_code ret;
|
||||
|
||||
ALLOC(fxarmor, 1);
|
||||
if (fxarmor == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fxarmor->armor_type = 1;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
ret = krb5_auth_con_init (context, &auth_context);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_get_principal(context, state->armor_ccache, &cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_make_principal(context, &cred.server,
|
||||
cred.client->realm,
|
||||
KRB5_TGS_NAME,
|
||||
cred.client->realm,
|
||||
NULL);
|
||||
if (ret) {
|
||||
krb5_free_principal(context, cred.client);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_get_credentials(context, 0, state->armor_ccache, &cred, &credp);
|
||||
krb5_free_principal(context, cred.server);
|
||||
krb5_free_principal(context, cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_mk_req_extended(context,
|
||||
&auth_context,
|
||||
AP_OPTS_USE_SUBKEY,
|
||||
NULL,
|
||||
credp,
|
||||
&fxarmor->armor_value);
|
||||
krb5_free_creds(context, credp);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (state->armor_crypto)
|
||||
krb5_crypto_destroy(context, state->armor_crypto);
|
||||
krb5_free_keyblock_contents(context, &state->armor_key);
|
||||
|
||||
ret = _krb5_fast_armor_key(context,
|
||||
auth_context->keyblock,
|
||||
auth_context->local_subkey,
|
||||
&state->armor_key,
|
||||
&state->armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*armor = fxarmor;
|
||||
fxarmor = NULL;
|
||||
out:
|
||||
if (fxarmor)
|
||||
free_KrbFastArmor(fxarmor);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
krb5_error_code ret;
|
||||
KrbFastReq fastreq;
|
||||
krb5_data data;
|
||||
size_t size;
|
||||
|
||||
memset(&fxreq, 0, sizeof(fxreq));
|
||||
memset(&fastreq, 0, sizeof(fastreq));
|
||||
krb5_data_zero(&data);
|
||||
|
||||
if (state->armor_crypto == NULL) {
|
||||
if (state->armor_ccache) {
|
||||
/*
|
||||
* Instead of keeping state in FX_COOKIE in the KDC, we
|
||||
* rebuild a new armor key for every request, because this
|
||||
* is what the MIT KDC expect and RFC6113 is vage about
|
||||
* what the behavior should be.
|
||||
*/
|
||||
state->type = choice_PA_FX_FAST_REQUEST_armored_data;
|
||||
} else {
|
||||
return check_fast(context, state);
|
||||
}
|
||||
}
|
||||
|
||||
state->flags |= KRB5_FAST_EXPECTED;
|
||||
|
||||
fastreq.fast_options.hide_client_names = 1;
|
||||
|
||||
ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
|
||||
free_KDC_REQ_BODY(&req->req_body);
|
||||
|
||||
req->req_body.realm = strdup(KRB5_ANON_REALM);
|
||||
ALLOC(req->req_body.cname, 1);
|
||||
req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
|
||||
ALLOC(req->req_body.cname->name_string.val, 2);
|
||||
req->req_body.cname->name_string.len = 2;
|
||||
req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
|
||||
req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
|
||||
|
||||
if (req->padata) {
|
||||
ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
|
||||
free_METHOD_DATA(req->padata);
|
||||
} else {
|
||||
ALLOC(req->padata, 1);
|
||||
}
|
||||
|
||||
|
||||
ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(data.length == size, "ASN.1 internal error");
|
||||
|
||||
fxreq.element = state->type;
|
||||
|
||||
if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
|
||||
size_t len;
|
||||
void *buf;
|
||||
|
||||
ret = make_fast_ap_fxarmor(context, state, &fxreq.u.armored_data.armor);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(len == size, "ASN.1 internal error");
|
||||
|
||||
ret = krb5_create_checksum(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_REQ_CHKSUM, 0,
|
||||
buf, len,
|
||||
&fxreq.u.armored_data.req_checksum);
|
||||
free(buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_ENC,
|
||||
data.data,
|
||||
data.length,
|
||||
0,
|
||||
&fxreq.u.armored_data.enc_fast_req);
|
||||
krb5_data_free(&data);
|
||||
|
||||
} else {
|
||||
krb5_data_free(&data);
|
||||
heim_assert(false, "unknown FAST type, internal error");
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(data.length == size, "ASN.1 internal error");
|
||||
|
||||
|
||||
ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
|
||||
if (ret)
|
||||
goto out;
|
||||
krb5_data_zero(&data);
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REQUEST(&fxreq);
|
||||
if (fxarmor) {
|
||||
free_KrbFastArmor(fxarmor);
|
||||
free(fxarmor);
|
||||
}
|
||||
krb5_data_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The core loop if krb5_get_init_creds() function family. Create the
|
||||
* packets and have the caller send them off to the KDC.
|
||||
@@ -1673,6 +1915,7 @@ krb5_init_creds_step(krb5_context context,
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
size_t size;
|
||||
AS_REQ req2;
|
||||
|
||||
krb5_data_zero(out);
|
||||
|
||||
@@ -1709,6 +1952,13 @@ krb5_init_creds_step(krb5_context context,
|
||||
if (ret == 0) {
|
||||
unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
|
||||
|
||||
/*
|
||||
* Unwrap AS-REP
|
||||
*/
|
||||
ret = fast_unwrap_as_rep(context, &ctx->fast_state, &rep.kdc_rep);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (ctx->flags.canonicalize) {
|
||||
eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
|
||||
eflags |= EXTRACT_TICKET_MATCH_REALM;
|
||||
@@ -1765,6 +2015,17 @@ krb5_init_creds_step(krb5_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unwrap KRB-ERROR
|
||||
*/
|
||||
ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
|
||||
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
|
||||
@@ -1845,11 +2106,23 @@ krb5_init_creds_step(krb5_context context,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Wrap with FAST
|
||||
*/
|
||||
copy_AS_REQ(&ctx->as_req, &req2);
|
||||
|
||||
ret = fast_wrap_req(context, &ctx->fast_state, &req2);
|
||||
if (ret) {
|
||||
free_AS_REQ(&req2);
|
||||
goto out;
|
||||
}
|
||||
|
||||
krb5_data_free(&ctx->req_buffer);
|
||||
|
||||
ASN1_MALLOC_ENCODE(AS_REQ,
|
||||
ctx->req_buffer.data, ctx->req_buffer.length,
|
||||
&ctx->as_req, &len, ret);
|
||||
&req2, &len, ret);
|
||||
free_AS_REQ(&req2);
|
||||
if (ret)
|
||||
goto out;
|
||||
if(len != ctx->req_buffer.length)
|
||||
|
Reference in New Issue
Block a user