krb5: import Heimdal-597.121.1 AS/TGS client
Sync with most changes in AS/TGS client from Apple's Heimdal-597.121.1 (opensource.apple.com). Changes include: - FAST support in TGS client - Refactored pre-auth client to be more easily extensible - Pin KDC host and AD site name in API calls Note the completely refactored TGS client loop is not imported as that was considered too intrusive.
This commit is contained in:
773
lib/krb5/fast.c
773
lib/krb5/fast.c
@@ -32,7 +32,9 @@
|
||||
*/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <heim-ipc.h>
|
||||
#endif
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_fast_cf2(krb5_context context,
|
||||
@@ -93,3 +95,772 @@ _krb5_fast_armor_key(krb5_context context,
|
||||
armorkey,
|
||||
armor_crypto);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
check_fast(krb5_context context, struct krb5_fast_state *state)
|
||||
{
|
||||
if (state && (state->flags & KRB5_FAST_EXPECTED)) {
|
||||
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
|
||||
"Expected FAST, but no FAST "
|
||||
"was in the response from the KDC");
|
||||
return KRB5KRB_AP_ERR_MODIFIED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
make_local_fast_ap_fxarmor(krb5_context context,
|
||||
krb5_ccache armor_ccache,
|
||||
krb5_const_realm realm,
|
||||
krb5_data *armor_value,
|
||||
krb5_keyblock *armor_key,
|
||||
krb5_crypto *armor_crypto)
|
||||
{
|
||||
krb5_auth_context auth_context = NULL;
|
||||
krb5_creds cred, *credp = NULL;
|
||||
krb5_error_code ret;
|
||||
krb5_data empty;
|
||||
krb5_const_realm tgs_realm;
|
||||
|
||||
krb5_data_zero(&empty);
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
ret = krb5_auth_con_init (context, &auth_context);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS
|
||||
*/
|
||||
if (krb5_principal_is_anonymous(context, cred.client,
|
||||
KRB5_ANON_MATCH_UNAUTHENTICATED))
|
||||
tgs_realm = realm;
|
||||
else
|
||||
tgs_realm = cred.client->realm;
|
||||
|
||||
ret = krb5_make_principal(context, &cred.server,
|
||||
tgs_realm,
|
||||
KRB5_TGS_NAME,
|
||||
tgs_realm,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_auth_con_add_AuthorizationData(context, auth_context,
|
||||
KRB5_AUTHDATA_FX_FAST_ARMOR,
|
||||
&empty);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_mk_req_extended(context,
|
||||
&auth_context,
|
||||
AP_OPTS_USE_SUBKEY,
|
||||
NULL,
|
||||
credp,
|
||||
armor_value);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = _krb5_fast_armor_key(context,
|
||||
auth_context->local_subkey,
|
||||
auth_context->keyblock,
|
||||
armor_key,
|
||||
armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (auth_context)
|
||||
krb5_auth_con_free(context, auth_context);
|
||||
if (credp)
|
||||
krb5_free_creds(context, credp);
|
||||
krb5_free_principal(context, cred.server);
|
||||
krb5_free_principal(context, cred.client);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
|
||||
static heim_ipc armor_service = NULL;
|
||||
|
||||
static void
|
||||
fast_armor_init_ipc(void *ctx)
|
||||
{
|
||||
heim_ipc *ipc = ctx;
|
||||
heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static krb5_error_code
|
||||
make_fast_ap_fxarmor(krb5_context context,
|
||||
struct krb5_fast_state *state,
|
||||
krb5_const_realm realm,
|
||||
KrbFastArmor **armor)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
krb5_error_code ret;
|
||||
|
||||
ALLOC(fxarmor, 1);
|
||||
if (fxarmor == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
|
||||
#ifdef WIN32
|
||||
krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
|
||||
ret = ENOTSUP;
|
||||
goto out;
|
||||
#else
|
||||
KERB_ARMOR_SERVICE_REPLY msg;
|
||||
krb5_data request, reply;
|
||||
|
||||
heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
|
||||
if (armor_service == NULL) {
|
||||
krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
|
||||
ret = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
krb5_data_zero(&reply);
|
||||
|
||||
request.data = rk_UNCONST(realm);
|
||||
request.length = strlen(realm);
|
||||
|
||||
ret = heim_ipc_call(armor_service, &request, &reply, NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret, "Failed to get armor service credential");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
|
||||
krb5_data_free(&reply);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = copy_KrbFastArmor(fxarmor, &msg.armor);
|
||||
if (ret) {
|
||||
free_KERB_ARMOR_SERVICE_REPLY(&msg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
|
||||
free_KERB_ARMOR_SERVICE_REPLY(&msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
#endif /* WIN32 */
|
||||
} else {
|
||||
fxarmor->armor_type = 1;
|
||||
|
||||
ret = make_local_fast_ap_fxarmor(context,
|
||||
state->armor_ccache,
|
||||
realm,
|
||||
&fxarmor->armor_value,
|
||||
&state->armor_key,
|
||||
&state->armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
*armor = fxarmor;
|
||||
fxarmor = NULL;
|
||||
|
||||
out:
|
||||
if (fxarmor) {
|
||||
free_KrbFastArmor(fxarmor);
|
||||
free(fxarmor);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
unwrap_fast_rep(krb5_context context,
|
||||
struct krb5_fast_state *state,
|
||||
PA_DATA *pa,
|
||||
KrbFastResponse *fastrep)
|
||||
{
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
krb5_error_code ret;
|
||||
|
||||
memset(&fxfastrep, 0, sizeof(fxfastrep));
|
||||
|
||||
ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&fxfastrep, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
|
||||
krb5_data data;
|
||||
|
||||
ret = krb5_decrypt_EncryptedData(context,
|
||||
state->armor_crypto,
|
||||
KRB5_KU_FAST_REP,
|
||||
&fxfastrep.u.armored_data.enc_fast_rep,
|
||||
&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL);
|
||||
krb5_data_free(&data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
} else {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
set_anon_principal(krb5_context context, PrincipalName **p)
|
||||
{
|
||||
|
||||
ALLOC((*p), 1);
|
||||
if (*p == NULL)
|
||||
goto fail;
|
||||
|
||||
(*p)->name_type = KRB5_NT_PRINCIPAL;
|
||||
|
||||
ALLOC_SEQ(&(*p)->name_string, 2);
|
||||
if ((*p)->name_string.val == NULL)
|
||||
goto fail;
|
||||
|
||||
(*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
|
||||
if ((*p)->name_string.val[0] == NULL)
|
||||
goto fail;
|
||||
|
||||
(*p)->name_string.val[1] = strdup(KRB5_ANON_NAME);
|
||||
if ((*p)->name_string.val[1] == NULL)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
if (*p) {
|
||||
if ((*p)->name_string.val) {
|
||||
free((*p)->name_string.val[0]);
|
||||
free((*p)->name_string.val[1]);
|
||||
free((*p)->name_string.val);
|
||||
}
|
||||
free(*p);
|
||||
}
|
||||
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_create_armor(krb5_context context,
|
||||
struct krb5_fast_state *state,
|
||||
const char *realm)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (state->armor_crypto == NULL) {
|
||||
if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) {
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
|
||||
if (state->armor_crypto)
|
||||
krb5_crypto_destroy(context, state->armor_crypto);
|
||||
krb5_free_keyblock_contents(context, &state->armor_key);
|
||||
|
||||
/*
|
||||
* If we have a armor auth context, its because the caller
|
||||
* wants us to do an implicit FAST armor (TGS-REQ).
|
||||
*/
|
||||
if (state->armor_ac) {
|
||||
heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC");
|
||||
|
||||
ret = _krb5_fast_armor_key(context,
|
||||
state->armor_ac->local_subkey,
|
||||
state->armor_ac->keyblock,
|
||||
&state->armor_key,
|
||||
&state->armor_crypto);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC");
|
||||
|
||||
if (state->armor_data) {
|
||||
free_KrbFastArmor(state->armor_data);
|
||||
free(state->armor_data);
|
||||
}
|
||||
ret = make_fast_ap_fxarmor(context, state, realm,
|
||||
&state->armor_data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
heim_abort("unknown state type: %d", (int)state->type);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_wrap_req(krb5_context context,
|
||||
struct krb5_fast_state *state,
|
||||
krb5_data *checksum_data,
|
||||
KDC_REQ *req)
|
||||
{
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
krb5_error_code ret;
|
||||
KrbFastReq fastreq;
|
||||
krb5_data data, aschecksum_data;
|
||||
size_t size = 0;
|
||||
|
||||
if (state->flags & KRB5_FAST_DISABLED) {
|
||||
_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&fxreq, 0, sizeof(fxreq));
|
||||
memset(&fastreq, 0, sizeof(fastreq));
|
||||
krb5_data_zero(&data);
|
||||
krb5_data_zero(&aschecksum_data);
|
||||
|
||||
if (state->armor_crypto == NULL)
|
||||
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);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* In the case of a AS-REQ, remove all account names. Want to this
|
||||
* for TGS-REQ too, but due to layering this is tricky.
|
||||
*
|
||||
* 1. TGS-REQ need checksum of REQ-BODY
|
||||
* 2. FAST needs checksum of TGS-REQ, so, FAST needs to happen after TGS-REQ
|
||||
* 3. FAST privacy mangaling needs to happen before TGS-REQ does the checksum in 1.
|
||||
*
|
||||
* So lets not modify the bits for now for TGS-REQ
|
||||
*/
|
||||
if (state->flags & KRB5_FAST_AS_REQ) {
|
||||
free_KDC_REQ_BODY(&req->req_body);
|
||||
|
||||
req->req_body.realm = strdup(KRB5_ANON_REALM);
|
||||
if (req->req_body.realm == NULL) {
|
||||
ret = krb5_enomem(context);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = set_anon_principal(context, &req->req_body.cname);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ALLOC(req->req_body.till, 1);
|
||||
*req->req_body.till = 0;
|
||||
|
||||
heim_assert(checksum_data == NULL, "checksum data not NULL");
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY,
|
||||
aschecksum_data.data,
|
||||
aschecksum_data.length,
|
||||
&req->req_body,
|
||||
&size, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(aschecksum_data.length == size, "ASN.1 internal error");
|
||||
|
||||
checksum_data = &aschecksum_data;
|
||||
}
|
||||
|
||||
if (req->padata) {
|
||||
ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
|
||||
free_METHOD_DATA(req->padata);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ALLOC(req->padata, 1);
|
||||
if (req->padata == NULL) {
|
||||
ret = krb5_enomem(context);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
fxreq.u.armored_data.armor = state->armor_data;
|
||||
state->armor_data = NULL;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
heim_assert(state->armor_crypto != NULL,
|
||||
"FAST armor key missing when FAST started");
|
||||
|
||||
ret = krb5_create_checksum(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_REQ_CHKSUM, 0,
|
||||
checksum_data->data,
|
||||
checksum_data->length,
|
||||
&fxreq.u.armored_data.req_checksum);
|
||||
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);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
} 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_KrbFastReq(&fastreq);
|
||||
free_PA_FX_FAST_REQUEST(&fxreq);
|
||||
krb5_data_free(&data);
|
||||
krb5_data_free(&aschecksum_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_unwrap_error(krb5_context context,
|
||||
int32_t nonce,
|
||||
struct krb5_fast_state *state,
|
||||
METHOD_DATA *md,
|
||||
KRB_ERROR *error)
|
||||
{
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa;
|
||||
int idx;
|
||||
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
|
||||
if (error->error_code != KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
_krb5_debug(context, 10, "using FAST without FAST outer error code");
|
||||
|
||||
idx = 0;
|
||||
pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx);
|
||||
if (pa == NULL) {
|
||||
ret = KRB5_KDCREP_MODIFIED;
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("FAST fast response is missing FX-FAST", ""));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = unwrap_fast_rep(context, state, pa, &fastrep);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fastrep.strengthen_key || nonce != (int32_t)fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx);
|
||||
if (pa == NULL) {
|
||||
ret = KRB5_KDCREP_MODIFIED;
|
||||
krb5_set_error_message(context, ret, N_("No wrapped error", ""));
|
||||
goto out;
|
||||
}
|
||||
|
||||
free_KRB_ERROR(error);
|
||||
|
||||
ret = krb5_rd_error(context, &pa->padata_value, error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (error->e_data)
|
||||
_krb5_debug(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d",
|
||||
(int)error->e_data->length);
|
||||
|
||||
free_METHOD_DATA(md);
|
||||
md->val = fastrep.padata.val;
|
||||
md->len = fastrep.padata.len;
|
||||
|
||||
fastrep.padata.val = NULL;
|
||||
fastrep.padata.len = 0;
|
||||
|
||||
out:
|
||||
free_KrbFastResponse(&fastrep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce,
|
||||
krb5_data *chksumdata,
|
||||
struct krb5_fast_state *state, AS_REP *rep)
|
||||
{
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa = NULL;
|
||||
int idx = 0;
|
||||
|
||||
if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
/* find PA_FX_FAST_REPLY */
|
||||
|
||||
pa = krb5_find_padata(rep->padata->val, rep->padata->len,
|
||||
KRB5_PADATA_FX_FAST, &idx);
|
||||
if (pa == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
|
||||
ret = unwrap_fast_rep(context, state, pa, &fastrep);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
free_METHOD_DATA(rep->padata);
|
||||
ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (fastrep.strengthen_key) {
|
||||
if (state->strengthen_key)
|
||||
krb5_free_keyblock(context, state->strengthen_key);
|
||||
|
||||
ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nonce != (int32_t)fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
if (fastrep.finished) {
|
||||
PrincipalName cname;
|
||||
krb5_realm crealm = NULL;
|
||||
|
||||
if (chksumdata == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_verify_checksum(context, state->armor_crypto,
|
||||
KRB5_KU_FAST_FINISHED,
|
||||
chksumdata->data, chksumdata->length,
|
||||
&fastrep.finished->ticket_checksum);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* update */
|
||||
ret = copy_Realm(&fastrep.finished->crealm, &crealm);
|
||||
if (ret)
|
||||
goto out;
|
||||
free_Realm(&rep->crealm);
|
||||
rep->crealm = crealm;
|
||||
|
||||
ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
|
||||
if (ret)
|
||||
goto out;
|
||||
free_PrincipalName(&rep->cname);
|
||||
rep->cname = cname;
|
||||
} else if (chksumdata) {
|
||||
/* expected fastrep.finish but didn't get it */
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
}
|
||||
|
||||
out:
|
||||
free_KrbFastResponse(&fastrep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
_krb5_fast_free(krb5_context context, struct krb5_fast_state *state)
|
||||
{
|
||||
if (state->armor_ccache) {
|
||||
if (state->flags & KRB5_FAST_ANON_PKINIT_ARMOR)
|
||||
krb5_cc_destroy(context, state->armor_ccache);
|
||||
else
|
||||
krb5_cc_close(context, state->armor_ccache);
|
||||
}
|
||||
if (state->armor_service)
|
||||
krb5_free_principal(context, state->armor_service);
|
||||
if (state->armor_crypto)
|
||||
krb5_crypto_destroy(context, state->armor_crypto);
|
||||
if (state->strengthen_key)
|
||||
krb5_free_keyblock(context, state->strengthen_key);
|
||||
krb5_free_keyblock_contents(context, &state->armor_key);
|
||||
if (state->armor_data) {
|
||||
free_KrbFastArmor(state->armor_data);
|
||||
free(state->armor_data);
|
||||
}
|
||||
|
||||
if (state->anon_pkinit_ctx)
|
||||
krb5_init_creds_free(context, state->anon_pkinit_ctx);
|
||||
if (state->anon_pkinit_opt)
|
||||
krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_fast_anon_pkinit_step(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
struct krb5_fast_state *state,
|
||||
krb5_data *in,
|
||||
krb5_data *out,
|
||||
krb5_krbhst_info *hostinfo,
|
||||
unsigned int *flags)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_const_realm realm = _krb5_init_creds_get_cred_client(context, ctx)->realm;
|
||||
krb5_init_creds_context anon_pk_ctx;
|
||||
krb5_principal principal = NULL, anon_pk_client;
|
||||
krb5_ccache ccache = NULL;
|
||||
krb5_creds cred;
|
||||
krb5_data data = { 3, rk_UNCONST("yes") };
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
if (state->anon_pkinit_opt == NULL) {
|
||||
ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 60);
|
||||
krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE);
|
||||
|
||||
ret = krb5_make_principal(context, &principal, realm,
|
||||
KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_get_init_creds_opt_set_pkinit(context,
|
||||
state->anon_pkinit_opt,
|
||||
principal,
|
||||
NULL, NULL, NULL, NULL,
|
||||
KRB5_GIC_OPT_PKINIT_ANONYMOUS |
|
||||
KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR,
|
||||
NULL, NULL, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_init_creds_init(context, principal, NULL, NULL,
|
||||
_krb5_init_creds_get_cred_starttime(context, ctx),
|
||||
state->anon_pkinit_opt,
|
||||
&state->anon_pkinit_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
anon_pk_ctx = state->anon_pkinit_ctx;
|
||||
|
||||
ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, hostinfo, flags);
|
||||
if (ret ||
|
||||
(*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
|
||||
goto out;
|
||||
|
||||
ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!cred.flags.b.enc_pa_rep) {
|
||||
ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */
|
||||
goto out;
|
||||
}
|
||||
|
||||
anon_pk_client = _krb5_init_creds_get_cred_client(context, anon_pk_ctx);
|
||||
|
||||
ret = krb5_cc_initialize(context, ccache, anon_pk_client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_store_cred(context, ccache, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_cc_set_config(context, ccache, cred.server,
|
||||
"fast_avail", &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt))
|
||||
state->flags |= KRB5_FAST_KDC_VERIFIED;
|
||||
else
|
||||
state->flags &= ~(KRB5_FAST_KDC_VERIFIED);
|
||||
|
||||
state->armor_ccache = ccache;
|
||||
ccache = NULL;
|
||||
|
||||
krb5_init_creds_free(context, state->anon_pkinit_ctx);
|
||||
state->anon_pkinit_ctx = NULL;
|
||||
|
||||
krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
|
||||
state->anon_pkinit_opt = NULL;
|
||||
|
||||
*flags |= KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, principal);
|
||||
krb5_free_cred_contents(context, &cred);
|
||||
if (ccache)
|
||||
krb5_cc_destroy(context, ccache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user