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:
Luke Howard
2021-08-09 19:32:21 +10:00
parent b5a58df8eb
commit 47282cae34
15 changed files with 3028 additions and 1351 deletions

View File

@@ -1,9 +1,6 @@
-- in order of preference
- client: support KRB5_PADATA_ENCRYPTED_CHALLENGE in lib/krb5/init_creds_pw.c
- client: don't support ENC-TS in FAST
- client: plugin support for fast plugins
- kdc: plugin support for fast plugins
@@ -13,5 +10,3 @@
-- using PK-INIT anonymous
-- using host key
- client: tgs-req fast support
- kdc: tgs-req fast support

View File

@@ -207,6 +207,9 @@ AUTHDATA-TYPE ::= INTEGER {
KRB5-AUTHDATA-OSF-DCE(64),
KRB5-AUTHDATA-SESAME(65),
KRB5-AUTHDATA-OSF-DCE-PKI-CERTID(66),
KRB5-AUTHDATA-AUTHENTICATION-STRENGTH(70),
KRB5-AUTHDATA-FX-FAST-ARMOR(71),
KRB5-AUTHDATA-FX-FAST-USED(72),
KRB5-AUTHDATA-WIN2K-PAC(128),
KRB5-AUTHDATA-GSS-API-ETYPE-NEGOTIATION(129), -- Authenticator only
KRB5-AUTHDATA-SIGNTICKET-OLDER(-17),
@@ -843,10 +846,11 @@ PA-FX-FAST-REPLY ::= CHOICE {
}
KDCFastFlags ::= BIT STRING {
use_reply_key(0),
reply_key_used(1),
reply_key_replaced(2),
kdc_verfied(3)
use-reply-key(0),
reply-key-used(1),
reply-key-replaced(2),
kdc-verified(3),
requested-hidden-names(4)
}
-- KDCFastState is stored in FX_COOKIE

View File

@@ -22,6 +22,9 @@ error_code TOO_BIG, "Offset too large"
error_code BAD_HDBENT_ENCODING, "Invalid HDB entry encoding"
error_code RANDOM_OFFLINE, "No random source available"
error_code CONFIG_BADFORMAT, "Improper format of configuration file"
error_code PA_CONTINUE_NEEDED, "Need to continue preauth stepping"
error_code PA_CANT_CONTINUE, "Can't continue with this preauth"
error_code NO_MORE_PA_MECHS, "No more PA mechanisms available"
index 64
prefix HEIM_PKINIT

View File

@@ -64,6 +64,7 @@ enum {
HEIM_TID_ERROR = 133,
HEIM_TID_DATA = 134,
HEIM_TID_DB = 135,
HEIM_TID_PA_AUTH_MECH = 136,
HEIM_TID_USER = 255
};

View File

@@ -81,6 +81,7 @@ static krb5_error_code KRB5_LIB_CALL
pa_gss_step(krb5_context context,
krb5_gss_init_ctx gssic,
const krb5_creds *kcred,
gss_ctx_id_t *ctx,
KDCOptions flags,
krb5_data *enc_as_req,
krb5_data *in,
@@ -91,7 +92,6 @@ pa_gss_step(krb5_context context,
OM_uint32 major, minor;
gss_cred_id_t cred;
gss_ctx_id_t ctx;
gss_name_t target_name = GSS_C_NO_NAME;
OM_uint32 req_flags = GSS_C_MUTUAL_FLAG;
OM_uint32 ret_flags;
@@ -113,8 +113,6 @@ pa_gss_step(krb5_context context,
_krb5_init_creds_set_gss_cred(context, gssic, cred);
}
ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
ret = krb5_make_principal(context, &tgs_name, kcred->server->realm,
KRB5_TGS_NAME, kcred->server->realm, NULL);
if (ret)
@@ -129,7 +127,7 @@ pa_gss_step(krb5_context context,
major = gss_init_sec_context(&minor,
cred,
&ctx,
ctx,
target_name,
(gss_OID)_krb5_init_creds_get_gss_mechanism(context, gssic),
req_flags,
@@ -141,8 +139,6 @@ pa_gss_step(krb5_context context,
&ret_flags,
NULL);
_krb5_init_creds_set_gss_context(context, gssic, ctx);
_krb5_gss_buffer_to_data(&output_token, out);
if (major == GSS_S_COMPLETE) {
@@ -166,6 +162,7 @@ static krb5_error_code KRB5_LIB_CALL
pa_gss_finish(krb5_context context,
krb5_gss_init_ctx gssic,
const krb5_creds *kcred,
gss_ctx_id_t ctx,
krb5int32 nonce,
krb5_enctype enctype,
krb5_principal *client_p,
@@ -177,7 +174,6 @@ pa_gss_finish(krb5_context context,
OM_uint32 major, minor;
gss_name_t initiator_name = GSS_C_NO_NAME;
gss_ctx_id_t ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
*client_p = NULL;
*reply_key_p = NULL;

View File

@@ -50,7 +50,7 @@ _krb5_gss_map_error(OM_uint32 major, OM_uint32 minor)
ret = 0;
break;
case GSS_S_CONTINUE_NEEDED:
ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
ret = HEIM_ERR_PA_CONTINUE_NEEDED;
break;
case GSS_S_BAD_NAME:
case GSS_S_BAD_NAMETYPE:

View File

@@ -539,7 +539,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context,
{
krb5_kdc_flags f;
f.i = flags;
return _krb5_get_cred_kdc_any(context, f, ccache,
return _krb5_get_cred_kdc_any(context, f, ccache, NULL,
in_creds, NULL, NULL,
out_creds, ret_tgts);
}

View File

@@ -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;
}

View File

@@ -3,7 +3,7 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
* Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,8 +38,10 @@
static krb5_error_code
get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
krb5_ccache, krb5_creds *, krb5_principal,
Ticket *, krb5_creds **, krb5_creds ***);
krb5_ccache, struct krb5_fast_state *,
krb5_creds *, krb5_principal,
Ticket *, const char *, const char *,
krb5_creds **, krb5_creds ***);
/*
* Take the `body' and encode it into `padata' using the credentials
@@ -50,33 +52,31 @@ static krb5_error_code
make_pa_tgs_req(krb5_context context,
krb5_auth_context ac,
KDC_REQ_BODY *body,
PA_DATA *padata,
krb5_creds *creds)
krb5_ccache ccache,
krb5_creds *creds,
krb5_data *tgs_req)
{
u_char *buf;
krb5_error_code ret;
krb5_data in_data;
size_t buf_size;
size_t len = 0;
krb5_data in_data;
krb5_error_code ret;
uint8_t *buf;
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
if (ret)
goto out;
return ret;
if(buf_size != len)
krb5_abortx(context, "internal error in ASN.1 encoder");
in_data.length = len;
in_data.data = buf;
ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
&padata->padata_value,
ret = _krb5_mk_req_internal(context, &ac, 0, &in_data,
creds, tgs_req,
KRB5_KU_TGS_REQ_AUTH_CKSUM,
KRB5_KU_TGS_REQ_AUTH);
out:
free (buf);
if(ret)
return ret;
padata->padata_type = KRB5_PADATA_TGS_REQ;
return 0;
}
/*
@@ -138,6 +138,7 @@ set_auth_data (krb5_context context,
static krb5_error_code
init_tgs_req (krb5_context context,
krb5_ccache ccache,
struct krb5_fast_state *state,
krb5_addresses *addresses,
krb5_kdc_flags flags,
Ticket *second_ticket,
@@ -150,8 +151,11 @@ init_tgs_req (krb5_context context,
{
krb5_auth_context ac = NULL;
krb5_error_code ret = 0;
krb5_data tgs_req;
memset(t, 0, sizeof(*t));
memset(t, 0, sizeof(*t));
t->pvno = 5;
t->msg_type = krb_tgs_req;
if (in_creds->session.keytype) {
@@ -234,27 +238,20 @@ init_tgs_req (krb5_context context,
if (ret)
goto fail;
}
if (padata) {
if (t->padata == NULL) {
ALLOC(t->padata, 1);
if (t->padata == NULL) {
ret = krb5_enomem(context);
goto fail;
}
ALLOC_SEQ(t->padata, 1 + padata->len);
if (t->padata->val == NULL) {
ret = krb5_enomem(context);
}
ret = copy_METHOD_DATA(padata, t->padata);
if (ret)
goto fail;
}
{
size_t i;
for (i = 0; i < padata->len; i++) {
ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
if (ret) {
krb5_set_error_message(context, ret,
N_("malloc: out of memory", ""));
goto fail;
}
}
}
ret = krb5_auth_con_init(context, &ac);
if(ret)
@@ -264,19 +261,61 @@ init_tgs_req (krb5_context context,
if (ret)
goto fail;
ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
ac->local_subkey);
if (state) {
krb5_data empty;
krb5_data_zero(&empty);
ret = krb5_auth_con_add_AuthorizationData(context, ac,
KRB5_AUTHDATA_FX_FAST_USED,
&empty);
if (ret)
goto fail;
}
ret = set_auth_data(context, &t->req_body,
&in_creds->authdata, ac->local_subkey);
if (ret)
goto fail;
if (t->padata == NULL) {
ALLOC(t->padata, 1);
if (t->padata == NULL) {
ret = krb5_enomem(context);
goto fail;
}
}
ret = make_pa_tgs_req(context,
ac,
&t->req_body,
&t->padata->val[0],
krbtgt);
ccache,
krbtgt,
&tgs_req);
if(ret)
goto fail;
if (state) {
state->armor_ac = ac;
ret = _krb5_fast_create_armor(context, state, NULL);
state->armor_ac = NULL;
if (ret)
goto fail;
ret = _krb5_fast_wrap_req(context, state, &tgs_req, t);
if (ret)
goto fail;
/* Its ok if there is no fast in the TGS-REP, older heimdal only support it in the AS code path */
state->flags &= ~KRB5_FAST_EXPECTED;
}
ret = krb5_padata_add(context, t->padata, KRB5_PADATA_TGS_REQ,
tgs_req.data, tgs_req.length);
if (ret)
goto fail;
krb5_data_zero(&tgs_req);
ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
if (ret)
goto fail;
@@ -288,6 +327,8 @@ fail:
t->req_body.addresses = NULL;
free_TGS_REQ (t);
}
krb5_data_free(&tgs_req);
return ret;
}
@@ -336,6 +377,42 @@ _krb5_get_krbtgt(krb5_context context,
return 0;
}
static krb5_error_code
fast_tgs_strengthen_key(krb5_context context,
struct krb5_fast_state *state,
krb5_keyblock *reply_key,
krb5_keyblock *extract_key)
{
krb5_error_code ret;
if (state && state->strengthen_key) {
_krb5_debug(context, 5, "_krb5_fast_tgs_strengthen_key");
if (state->strengthen_key->keytype != reply_key->keytype) {
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
N_("strengthen_key %d not same enctype as reply key %d", ""),
state->strengthen_key->keytype, reply_key->keytype);
return KRB5KRB_AP_ERR_MODIFIED;
}
ret = _krb5_fast_cf2(context,
state->strengthen_key,
"strengthenkey",
reply_key,
"replykey",
extract_key,
NULL);
if (ret)
return ret;
} else {
ret = krb5_copy_keyblock_contents(context, reply_key, extract_key);
if (ret)
return ret;
}
return 0;
}
/* DCE compatible decrypt proc */
static krb5_error_code KRB5_CALLCONV
decrypt_tkt_with_subkey (krb5_context context,
@@ -344,11 +421,14 @@ decrypt_tkt_with_subkey (krb5_context context,
krb5_const_pointer skey,
krb5_kdc_rep *dec_rep)
{
const krb5_keyblock *subkey = skey;
struct krb5_decrypt_tkt_with_subkey_state *state;
krb5_error_code ret = 0;
krb5_data data;
size_t size;
krb5_crypto crypto;
krb5_keyblock extract_key;
state = (struct krb5_decrypt_tkt_with_subkey_state *)skey;
assert(usage == 0);
@@ -357,8 +437,14 @@ decrypt_tkt_with_subkey (krb5_context context,
/*
* start out with trying with subkey if we have one
*/
if (subkey) {
ret = krb5_crypto_init(context, subkey, 0, &crypto);
if (state->subkey) {
ret = fast_tgs_strengthen_key(context, state->fast_state,
state->subkey, &extract_key);
if (ret)
return ret;
ret = krb5_crypto_init(context, &extract_key, 0, &crypto);
krb5_free_keyblock_contents(context, &extract_key);
if (ret)
return ret;
ret = krb5_decrypt_EncryptedData (context,
@@ -370,7 +456,7 @@ decrypt_tkt_with_subkey (krb5_context context,
* If the is Windows 2000 DC, we need to retry with key usage
* 8 when doing ARCFOUR.
*/
if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
if (ret && state->subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
ret = krb5_decrypt_EncryptedData(context,
crypto,
8,
@@ -379,7 +465,11 @@ decrypt_tkt_with_subkey (krb5_context context,
}
krb5_crypto_destroy(context, crypto);
}
if (subkey == NULL || ret) {
if (state->subkey == NULL || ret) {
ret = fast_tgs_strengthen_key(context, state->fast_state, key, &extract_key);
if (ret)
return ret;
ret = krb5_crypto_init(context, key, 0, &crypto);
if (ret)
return ret;
@@ -412,19 +502,21 @@ decrypt_tkt_with_subkey (krb5_context context,
static krb5_error_code
get_cred_kdc(krb5_context context,
krb5_ccache id,
struct krb5_fast_state *fast_state,
krb5_kdc_flags flags,
krb5_addresses *addresses,
krb5_creds *in_creds,
krb5_creds *krbtgt,
krb5_principal impersonate_principal,
Ticket *second_ticket,
const char *kdc_hostname,
const char *sitename,
krb5_creds *out_creds)
{
TGS_REQ req;
krb5_data enc;
krb5_data resp;
krb5_kdc_rep rep = {0};
KRB_ERROR error;
krb5_error_code ret;
unsigned nonce;
krb5_keyblock *subkey = NULL;
@@ -503,6 +595,7 @@ get_cred_kdc(krb5_context context,
ret = init_tgs_req (context,
id,
fast_state,
addresses,
flags,
second_ticket,
@@ -535,6 +628,11 @@ get_cred_kdc(krb5_context context,
return ret;
krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
if (kdc_hostname)
krb5_sendto_set_hostname(context, stctx, kdc_hostname);
if (sitename)
krb5_sendto_set_sitename(context, stctx, sitename);
ret = krb5_sendto_context (context, stctx, &enc,
krbtgt->server->name.name_string.val[1],
&resp);
@@ -544,18 +642,33 @@ get_cred_kdc(krb5_context context,
goto out;
if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
struct krb5_decrypt_tkt_with_subkey_state state;
unsigned eflags = 0;
krb5_data data;
size_t size;
ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
&rep.kdc_rep.ticket, &size, ret);
if (ret)
goto out;
heim_assert(data.length == size, "ASN.1 internal error");
ret = _krb5_fast_unwrap_kdc_rep(context, nonce, &data,
fast_state, &rep.kdc_rep);
krb5_data_free(&data);
if (ret)
goto out;
ret = krb5_copy_principal(context,
in_creds->client,
&out_creds->client);
if(ret)
goto out2;
goto out;
ret = krb5_copy_principal(context,
in_creds->server,
&out_creds->server);
if(ret)
goto out2;
goto out;
/* this should go someplace else */
out_creds->times.endtime = in_creds->times.endtime;
@@ -565,6 +678,9 @@ get_cred_kdc(krb5_context context,
if (flags.b.request_anonymous)
eflags |= EXTRACT_TICKET_MATCH_ANON;
state.subkey = subkey;
state.fast_state = fast_state;
ret = _krb5_extract_ticket(context,
&rep,
out_creds,
@@ -576,12 +692,36 @@ get_cred_kdc(krb5_context context,
eflags,
NULL,
decrypt_tkt_with_subkey,
subkey);
out2:
krb5_free_kdc_rep(context, &rep);
} else if(krb5_rd_error(context, &resp, &error) == 0) {
ret = krb5_error_from_rd_error(context, &error, in_creds);
krb5_free_error_contents(context, &error);
&state);
} else if(krb5_rd_error(context, &resp, &rep.error) == 0) {
METHOD_DATA md;
memset(&md, 0, sizeof(md));
if (rep.error.e_data) {
ret = decode_METHOD_DATA(rep.error.e_data->data,
rep.error.e_data->length,
&md, NULL);
if (ret) {
krb5_set_error_message(context, ret,
N_("Failed to decode METHOD-DATA", ""));
goto out;
}
}
ret = _krb5_fast_unwrap_error(context, nonce, fast_state, &md, &rep.error);
free_METHOD_DATA(&md);
if (ret)
goto out;
ret = krb5_error_from_rd_error(context, &rep.error, in_creds);
/* log the failure */
if (_krb5_have_debug(context, 5)) {
const char *str = krb5_get_error_message(context, ret);
_krb5_debug(context, 5, "parse_tgs_rep: KRB-ERROR %d/%s", ret, str);
krb5_free_error_message(context, str);
}
} else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
ret = KRB5KRB_AP_ERR_V4_REPLY;
krb5_clear_error_message(context);
@@ -591,6 +731,7 @@ get_cred_kdc(krb5_context context,
}
out:
krb5_free_kdc_rep(context, &rep);
if (second_ticket == &second_ticket_data)
free_Ticket(&second_ticket_data);
free_METHOD_DATA(&padata);
@@ -610,12 +751,15 @@ out:
static krb5_error_code
get_cred_kdc_address(krb5_context context,
krb5_ccache id,
struct krb5_fast_state *fast_state,
krb5_kdc_flags flags,
krb5_addresses *addrs,
krb5_creds *in_creds,
krb5_creds *krbtgt,
krb5_principal impersonate_principal,
Ticket *second_ticket,
const char *kdc_hostname,
const char *sitename,
krb5_creds *out_creds)
{
krb5_error_code ret;
@@ -640,9 +784,9 @@ get_cred_kdc_address(krb5_context context,
addrs = NULL;
}
}
ret = get_cred_kdc(context, id, flags, addrs, in_creds,
krbtgt, impersonate_principal,
second_ticket, out_creds);
ret = get_cred_kdc(context, id, fast_state, flags, addrs,
in_creds, krbtgt, impersonate_principal,
second_ticket, kdc_hostname, sitename, out_creds);
krb5_free_addresses(context, &addresses);
return ret;
}
@@ -659,6 +803,9 @@ krb5_get_kdc_cred(krb5_context context,
{
krb5_error_code ret;
krb5_creds *krbtgt;
struct krb5_fast_state fast_state;
memset(&fast_state, 0, sizeof(fast_state));
*out_creds = calloc(1, sizeof(**out_creds));
if(*out_creds == NULL)
@@ -672,9 +819,11 @@ krb5_get_kdc_cred(krb5_context context,
*out_creds = NULL;
return ret;
}
ret = get_cred_kdc(context, id, flags, addresses,
in_creds, krbtgt, NULL, NULL, *out_creds);
ret = get_cred_kdc(context, id, &fast_state, flags,
addresses, in_creds, krbtgt,
NULL, NULL, NULL, NULL, *out_creds);
krb5_free_creds (context, krbtgt);
_krb5_fast_free(context, &fast_state);
if(ret) {
free(*out_creds);
*out_creds = NULL;
@@ -752,10 +901,13 @@ static krb5_error_code
get_cred_kdc_capath_worker(krb5_context context,
krb5_kdc_flags flags,
krb5_ccache ccache,
struct krb5_fast_state *fast_state,
krb5_creds *in_creds,
krb5_const_realm try_realm,
krb5_principal impersonate_principal,
Ticket *second_ticket,
const char *kdc_hostname,
const char *sitename,
krb5_creds **out_creds,
krb5_creds ***ret_tgts)
{
@@ -802,10 +954,13 @@ get_cred_kdc_capath_worker(krb5_context context,
ok_as_delegate = tgts.flags.b.ok_as_delegate;
}
ret = get_cred_kdc_address(context, ccache, flags, NULL,
ret = get_cred_kdc_address(context, ccache, fast_state,
flags, NULL,
in_creds, &tgts,
impersonate_principal,
second_ticket,
kdc_hostname,
sitename,
*out_creds);
krb5_free_cred_contents(context, &tgts);
if (ret == 0 &&
@@ -838,8 +993,10 @@ get_cred_kdc_capath_worker(krb5_context context,
while (1) {
heim_general_string tgt_inst;
ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
NULL, NULL, &tgt, ret_tgts);
ret = get_cred_kdc_capath(context, flags, ccache, fast_state,
&tmp_creds, NULL, NULL,
kdc_hostname, sitename,
&tgt, ret_tgts);
if (ret)
goto out;
@@ -870,9 +1027,9 @@ get_cred_kdc_capath_worker(krb5_context context,
goto out;
}
ret = get_cred_kdc_address(context, ccache, flags, NULL,
ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
in_creds, tgt, impersonate_principal,
second_ticket, *out_creds);
second_ticket, kdc_hostname, sitename, *out_creds);
if (ret == 0 &&
!krb5_principal_compare(context, in_creds->server,
(*out_creds)->server)) {
@@ -915,9 +1072,12 @@ static krb5_error_code
get_cred_kdc_capath(krb5_context context,
krb5_kdc_flags flags,
krb5_ccache ccache,
struct krb5_fast_state *fast_state,
krb5_creds *in_creds,
krb5_principal impersonate_principal,
Ticket *second_ticket,
const char *kdc_hostname,
const char *sitename,
krb5_creds **out_creds,
krb5_creds ***ret_tgts)
{
@@ -928,18 +1088,20 @@ get_cred_kdc_capath(krb5_context context,
server_realm = krb5_principal_get_realm(context, in_creds->server);
try_realm = client_realm;
ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
impersonate_principal, second_ticket, out_creds,
ret_tgts);
ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
in_creds, try_realm, impersonate_principal,
second_ticket, kdc_hostname, sitename,
out_creds, ret_tgts);
if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
try_realm = krb5_config_get_string(context, NULL, "capaths",
client_realm, server_realm, NULL);
if (try_realm != NULL && strcmp(try_realm, client_realm) != 0) {
ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
try_realm, impersonate_principal,
second_ticket, out_creds, ret_tgts);
ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
in_creds, try_realm, impersonate_principal,
second_ticket, kdc_hostname, sitename,
out_creds, ret_tgts);
}
}
@@ -963,9 +1125,12 @@ static krb5_error_code
get_cred_kdc_referral(krb5_context context,
krb5_kdc_flags flags,
krb5_ccache ccache,
struct krb5_fast_state *fast_state,
krb5_creds *in_creds,
krb5_principal impersonate_principal,
Ticket *second_ticket,
const char *kdc_hostname,
const char *sitename,
krb5_creds **out_creds)
{
krb5_realm start_realm = NULL;
@@ -1076,9 +1241,9 @@ get_cred_kdc_referral(krb5_context context,
ret = EINVAL;
if (ret) {
ret = get_cred_kdc_address(context, ccache, flags, NULL,
ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
&referral, &tgt, impersonate_principal,
second_ticket, &ticket);
second_ticket, kdc_hostname, sitename, &ticket);
if (ret)
goto out;
}
@@ -1197,14 +1362,45 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_cred_kdc_any(krb5_context context,
krb5_kdc_flags flags,
krb5_ccache ccache,
struct krb5_fast_state *fast_state,
krb5_creds *in_creds,
krb5_principal impersonate_principal,
Ticket *second_ticket,
krb5_creds **out_creds,
krb5_creds ***ret_tgts)
{
char *kdc_hostname = NULL;
char *sitename = NULL;
krb5_error_code ret;
krb5_deltat offset;
krb5_data data;
/*
* If we are using LKDC, lets pull out the addreses from the
* ticket and use that.
*/
ret = krb5_cc_get_config(context, ccache, NULL, "lkdc-hostname", &data);
if (ret == 0) {
kdc_hostname = malloc(data.length + 1);
if (kdc_hostname == NULL)
return krb5_enomem(context);
memcpy(kdc_hostname, data.data, data.length);
kdc_hostname[data.length] = '\0';
krb5_data_free(&data);
}
ret = krb5_cc_get_config(context, ccache, NULL, "sitename", &data);
if (ret == 0) {
sitename = malloc(data.length + 1);
if (sitename == NULL)
return krb5_enomem(context);
memcpy(sitename, data.data, data.length);
sitename[data.length] = '\0';
krb5_data_free(&data);
}
ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
if (ret == 0) {
@@ -1220,23 +1416,35 @@ _krb5_get_cred_kdc_any(krb5_context context,
ret = get_cred_kdc_capath(context,
flags,
ccache,
fast_state,
in_creds,
impersonate_principal,
second_ticket,
kdc_hostname,
sitename,
out_creds,
ret_tgts);
if (ret == 0 || skip_referrals(in_creds->server, &flags))
return ret;
goto out;
}
/* Otherwise try referrals */
return get_cred_kdc_referral(context,
ret = get_cred_kdc_referral(context,
flags,
ccache,
fast_state,
in_creds,
impersonate_principal,
second_ticket,
kdc_hostname,
sitename,
out_creds);
out:
free(kdc_hostname);
free(sitename);
return ret;
}
static krb5_error_code
@@ -1342,6 +1550,7 @@ krb5_get_credentials_with_flags(krb5_context context,
krb5_creds *in_creds,
krb5_creds **out_creds)
{
struct krb5_fast_state fast_state;
krb5_error_code ret;
krb5_name_canon_iterator name_canon_iter = NULL;
krb5_name_canon_rule_options rule_opts;
@@ -1351,6 +1560,8 @@ krb5_get_credentials_with_flags(krb5_context context,
krb5_creds *res_creds;
int i;
memset(&fast_state, 0, sizeof(fast_state));
if (_krb5_have_debug(context, 5)) {
char *unparsed;
@@ -1380,7 +1591,7 @@ krb5_get_credentials_with_flags(krb5_context context,
ret = krb5_name_canon_iterator_start(context, in_creds->server,
&name_canon_iter);
if (ret)
return ret;
goto out;
next_rule:
krb5_free_cred_contents(context, res_creds);
@@ -1416,7 +1627,7 @@ next_rule:
options |= KRB5_GC_NO_STORE;
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
in_creds, NULL, NULL, out_creds, &tgts);
for (i = 0; tgts && tgts[i]; i++) {
if ((options & KRB5_GC_NO_STORE) == 0)
@@ -1451,6 +1662,7 @@ out:
in_creds->server = save_princ;
krb5_free_creds(context, res_creds);
krb5_free_name_canon_iterator(context, name_canon_iter);
_krb5_fast_free(context, &fast_state);
if (ret)
return not_found(context, in_creds->server, ret);
return 0;
@@ -1569,6 +1781,7 @@ krb5_get_creds(krb5_context context,
krb5_const_principal inprinc,
krb5_creds **out_creds)
{
struct krb5_fast_state fast_state;
krb5_kdc_flags flags;
krb5_flags options;
krb5_creds in_creds;
@@ -1582,6 +1795,7 @@ krb5_get_creds(krb5_context context,
int type;
const char *comp;
memset(&fast_state, 0, sizeof(fast_state));
memset(&in_creds, 0, sizeof(in_creds));
in_creds.server = rk_UNCONST(inprinc);
@@ -1683,7 +1897,7 @@ next_rule:
flags.b.request_anonymous = 1;
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
&in_creds, opt ? opt->self : 0,
opt ? opt->ticket : 0, out_creds,
&tgts);
@@ -1717,6 +1931,7 @@ next_rule:
}
out:
_krb5_fast_free(context, &fast_state);
krb5_free_creds(context, res_creds);
krb5_free_principal(context, in_creds.client);
krb5_free_name_canon_iterator(context, name_canon_iter);

File diff suppressed because it is too large Load Diff

View File

@@ -136,6 +136,8 @@ struct ContentInfo;
struct AlgorithmIdentifier;
typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
struct krb5_dh_moduli;
struct krb5_fast_state;
struct krb5_gss_init_ctx_data;
/* v4 glue */
struct _krb5_krb_auth_data;
@@ -163,6 +165,7 @@ typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_step)(
krb5_context,
krb5_gss_init_ctx,
const krb5_creds *,
struct gss_ctx_id_t_desc_struct **,
KDCOptions options,
krb5_data *,
krb5_data *,
@@ -172,6 +175,7 @@ typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_finish)(
krb5_context,
krb5_gss_init_ctx,
const krb5_creds *,
struct gss_ctx_id_t_desc_struct *,
krb5int32,
krb5_enctype,
krb5_principal *,
@@ -187,10 +191,14 @@ typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)(
krb5_gss_init_ctx,
struct gss_ctx_id_t_desc_struct *);
#define KRB5_GSS_IC_FLAG_RELEASE_CRED 1
#include <krb5-private.h>
#include "heim_threads.h"
extern const char _krb5_wellknown_lkdc[];
#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
@@ -246,9 +254,10 @@ struct _krb5_get_init_creds_opt_private {
krb5_pk_init_ctx pk_init_ctx;
krb5_get_init_creds_tristate addressless;
int flags;
#define KRB5_INIT_CREDS_CANONICALIZE 1
#define KRB5_INIT_CREDS_NO_C_CANON_CHECK 2
#define KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK 4
#define KRB5_INIT_CREDS_DONE 1
#define KRB5_INIT_CREDS_CANONICALIZE 2
#define KRB5_INIT_CREDS_NO_C_CANON_CHECK 4
#define KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK 8
#define KRB5_INIT_CREDS_PKINIT_KX_VALID 32
#define KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK 64
struct {
@@ -421,6 +430,45 @@ struct krb5_pk_init_ctx_data {
#endif /* PKINIT */
struct krb5_fast_state {
enum PA_FX_FAST_REQUEST_enum type;
unsigned int flags;
#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 0x0001
#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 0x0002
#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 0x0004
#define KRB5_FAST_REPLY_REPLY_VERIFIED 0x0008
#define KRB5_FAST_STRONG 0x0010
#define KRB5_FAST_EXPECTED 0x0020 /* in exchange with KDC, fast was discovered */
#define KRB5_FAST_REQUIRED 0x0040 /* fast required by action of caller */
#define KRB5_FAST_DISABLED 0x0080
#define KRB5_FAST_AP_ARMOR_SERVICE 0x0100
#define KRB5_FAST_OPTIMISTIC 0x0200 /* Optimistic try, like Anon + PKINIT or service fast bit */
#define KRB5_FAST_REQUIRE_ENC_PA 0x0400
#define KRB5_FAST_AS_REQ 0x1000
#define KRB5_FAST_ANON_PKINIT_ARMOR 0x2000
#define KRB5_FAST_KDC_VERIFIED 0x4000
krb5_keyblock *reply_key;
krb5_ccache armor_ccache;
krb5_auth_context armor_ac;
KrbFastArmor *armor_data;
krb5_principal armor_service;
krb5_crypto armor_crypto;
krb5_keyblock armor_key;
krb5_keyblock *strengthen_key;
/* KRB5_FAST_ANON_PKINIT_ARMOR */
krb5_get_init_creds_opt *anon_pkinit_opt;
krb5_init_creds_context anon_pkinit_ctx;
};
struct krb5_decrypt_tkt_with_subkey_state {
krb5_keyblock *subkey;
struct krb5_fast_state *fast_state;
};
#define ISTILDE(x) (x == '~')
#ifdef _WIN32
# define ISPATHSEP(x) (x == '/' || x =='\\')

View File

@@ -65,7 +65,7 @@ is_invalid_tld_srv_target(const char *target)
static krb5_error_code
srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
const char *realm, const char *dns_type,
const char *realm, const char *dns_type, const char *sitename,
const char *proto, const char *service, int port)
{
char domain[1024];
@@ -93,6 +93,10 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
else
def_port = port;
if (sitename)
snprintf(domain, sizeof(domain), "_%s._%s.%s._sites.%s.",
service, proto, sitename, realm);
else
snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
r = rk_dns_lookup(domain, dns_type);
@@ -169,19 +173,23 @@ struct krb5_krbhst_data {
unsigned int flags;
int def_port;
int port; /* hardwired port number if != 0 */
#define KD_CONFIG 1
#define KD_SRV_UDP 2
#define KD_SRV_TCP 4
#define KD_SRV_HTTP 8
#define KD_FALLBACK 16
#define KD_CONFIG_EXISTS 32
#define KD_LARGE_MSG 64
#define KD_PLUGIN 128
#define KD_HOSTNAMES 256
#define KD_CONFIG 0x0001
#define KD_SRV_UDP 0x0002
#define KD_SRV_TCP 0x0004
#define KD_SITE_SRV_UDP 0x0008
#define KD_SITE_SRV_TCP 0x0010
#define KD_SRV_HTTP 0x0020
#define KD_SRV_KKDCP 0x0040
#define KD_FALLBACK 0x0080
#define KD_CONFIG_EXISTS 0x0100
#define KD_LARGE_MSG 0x0200
#define KD_PLUGIN 0x0400
#define KD_HOSTNAMES 0x0800
krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
krb5_krbhst_info**);
char *hostname;
char *sitename;
unsigned int fallback_count;
struct krb5_krbhst_info *hosts, **index, **end;
@@ -434,7 +442,7 @@ get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
static void
srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
const char *proto, const char *service)
const char *sitename, const char *proto, const char *service)
{
krb5_error_code ret;
krb5_krbhst_info **res;
@@ -443,8 +451,8 @@ srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
if (krb5_realm_is_lkdc(kd->realm))
return;
ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
kd->port);
ret = srv_find_realm(context, &res, &count, kd->realm, "SRV",
sitename, proto, service, kd->port);
_krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
kd->realm, proto, service, ret);
if (ret)
@@ -755,21 +763,28 @@ kdc_get_next(krb5_context context,
}
if(context->srv_lookup) {
if(kd->sitename && (kd->flags & KD_SITE_SRV_TCP) == 0) {
srv_get_hosts(context, kd, kd->sitename, "tcp", "kerberos");
kd->flags |= KD_SITE_SRV_TCP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
srv_get_hosts(context, kd, "udp", kd->srv_label);
srv_get_hosts(context, kd, NULL, "udp", kd->srv_label);
kd->flags |= KD_SRV_UDP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", kd->srv_label);
srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_HTTP) == 0) {
srv_get_hosts(context, kd, "http", kd->srv_label);
srv_get_hosts(context, kd, NULL, "http", kd->srv_label);
kd->flags |= KD_SRV_HTTP;
if(get_next(kd, host))
return 0;
@@ -821,7 +836,7 @@ admin_get_next(krb5_context context,
if(context->srv_lookup) {
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", kd->srv_label);
srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
@@ -875,13 +890,13 @@ kpasswd_get_next(krb5_context context,
if(context->srv_lookup) {
if((kd->flags & KD_SRV_UDP) == 0) {
srv_get_hosts(context, kd, "udp", kd->srv_label);
srv_get_hosts(context, kd, NULL, "udp", kd->srv_label);
kd->flags |= KD_SRV_UDP;
if(get_next(kd, host))
return 0;
}
if((kd->flags & KD_SRV_TCP) == 0) {
srv_get_hosts(context, kd, "tcp", kd->srv_label);
srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
kd->flags |= KD_SRV_TCP;
if(get_next(kd, host))
return 0;
@@ -917,6 +932,8 @@ krbhost_dealloc(void *ptr)
}
if (handle->hostname)
free(handle->hostname);
if (handle->sitename)
free(handle->sitename);
free(handle->realm);
}
@@ -1089,6 +1106,19 @@ krb5_krbhst_set_hostname(krb5_context context,
return 0;
}
krb5_error_code KRB5_LIB_FUNCTION
krb5_krbhst_set_sitename(krb5_context context,
krb5_krbhst_handle handle,
const char *sitename)
{
if (handle->sitename)
free(handle->sitename);
handle->sitename = strdup(sitename);
if (handle->sitename == NULL)
return krb5_enomem(context);
return 0;
}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
{

View File

@@ -607,6 +607,8 @@ EXPORTS
krb5_sendto_ctx_set_type
krb5_sendto_kdc
krb5_sendto_kdc_flags
krb5_sendto_set_hostname
krb5_sendto_set_sitename
krb5_set_config
krb5_set_config_files
krb5_set_debug_dest
@@ -799,10 +801,13 @@ EXPORTS
_krb5_init_creds_get_gss_mechanism
_krb5_init_creds_set_gss_cred
_krb5_init_creds_get_gss_cred
_krb5_init_creds_set_gss_context
_krb5_init_creds_get_gss_context
_krb5_init_creds_init_gss
; Private init_creds API
_krb5_init_creds_get_cred_starttime
_krb5_init_creds_get_cred_endtime
_krb5_init_creds_get_cred_client
; Shared with libkadm5
_krb5_load_plugins
_krb5_unload_plugins
@@ -834,6 +839,8 @@ EXPORTS
_krb5_HMAC_MD5_checksum
_krb5_crypto_set_flags
_krb5_expand_path_tokens ;!
_krb5_make_pa_enc_challenge
_krb5_validate_pa_enc_challenge
; kinit helper
krb5_get_init_creds_opt_set_pkinit_user_certs
@@ -842,14 +849,17 @@ EXPORTS
krb5_auth_con_getsendsubkey
krb5_init_creds_free
krb5_init_creds_get
krb5_init_creds_get_as_reply_key
krb5_init_creds_get_creds
krb5_init_creds_get_error
krb5_init_creds_init
krb5_init_creds_set_fast_anon_pkinit
krb5_init_creds_set_fast_ccache
krb5_init_creds_set_keytab
krb5_init_creds_set_kdc_hostname
krb5_init_creds_set_password
krb5_init_creds_set_service
krb5_init_creds_set_sitename
krb5_init_creds_step
krb5_init_creds_store
krb5_process_last_request

View File

@@ -151,6 +151,7 @@ struct krb5_sendto_ctx_data {
krb5_sendto_ctx_func func;
void *data;
char *hostname;
char *sitename;
krb5_krbhst_handle krbhst;
/* context2 */
@@ -181,6 +182,8 @@ dealloc_sendto_ctx(void *ptr)
krb5_sendto_ctx ctx = (krb5_sendto_ctx)ptr;
if (ctx->hostname)
free(ctx->hostname);
if (ctx->sitename)
free(ctx->sitename);
heim_release(ctx->hosts);
heim_release(ctx->krbhst);
}
@@ -244,15 +247,28 @@ krb5_sendto_set_hostname(krb5_context context,
* disposing of any previous value after.
*/
newname = strdup(hostname);
if (newname == NULL) {
krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
return ENOMEM;
}
if (newname == NULL)
return krb5_enomem(context);
free(ctx->hostname);
ctx->hostname = newname;
return 0;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendto_set_sitename(krb5_context context,
krb5_sendto_ctx ctx,
const char *sitename)
{
char *newname;
newname = strdup(sitename);
if (newname == NULL)
return krb5_enomem(context);
free(ctx->sitename);
ctx->sitename = newname;
return 0;
}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_sendto_ctx_set_krb5hst(krb5_context context,
krb5_sendto_ctx ctx,
@@ -1189,7 +1205,11 @@ krb5_sendto_context(krb5_context context,
if (ret)
goto out;
}
if (ctx->sitename) {
ret = krb5_krbhst_set_sitename(context, handle, ctx->sitename);
if (ret)
goto out;
}
} else {
handle = heim_retain(ctx->krbhst);
}
@@ -1266,6 +1286,24 @@ krb5_sendto_context(krb5_context context,
&ctx->response, &action);
if (ret)
goto out;
/*
* If we are not done, ask to continue/reset
*/
switch (action) {
case KRB5_SENDTO_DONE:
break;
case KRB5_SENDTO_RESET:
case KRB5_SENDTO_CONTINUE:
/* free response to clear it out so we don't loop */
krb5_data_free(&ctx->response);
break;
default:
ret = KRB5_KDC_UNREACH;
krb5_set_error_message(context, ret,
"sendto filter funcation return unsupported state: %d", (int)action);
goto out;
}
}
break;
case KRB5_SENDTO_FAILED:

View File

@@ -600,6 +600,8 @@ HEIMDAL_KRB5_2.0 {
krb5_sendto_ctx_set_type;
krb5_sendto_kdc;
krb5_sendto_kdc_flags;
krb5_sendto_set_hostname;
krb5_sendto_set_sitename;
krb5_set_config;
krb5_set_config_files;
krb5_set_debug_dest;
@@ -791,10 +793,13 @@ HEIMDAL_KRB5_2.0 {
_krb5_init_creds_get_gss_mechanism;
_krb5_init_creds_set_gss_cred;
_krb5_init_creds_get_gss_cred;
_krb5_init_creds_set_gss_context;
_krb5_init_creds_get_gss_context;
_krb5_init_creds_init_gss;
# Private init_creds API
_krb5_init_creds_get_cred_starttime;
_krb5_init_creds_get_cred_endtime;
_krb5_init_creds_get_cred_client;
# Shared with libkadm5
_krb5_load_plugins;
_krb5_unload_plugins;
@@ -824,6 +829,8 @@ HEIMDAL_KRB5_2.0 {
_krb5_s4u2self_to_checksumdata;
_krb5_HMAC_MD5_checksum;
_krb5_crypto_set_flags;
_krb5_make_pa_enc_challenge;
_krb5_validate_pa_enc_challenge;
# kinit helper
krb5_get_init_creds_opt_set_pkinit_user_certs;
@@ -834,10 +841,13 @@ HEIMDAL_KRB5_2.0 {
krb5_init_creds_set_fast_anon_pkinit;
krb5_init_creds_set_fast_ccache;
krb5_init_creds_set_keytab;
krb5_init_creds_set_kdc_hostname;
krb5_init_creds_get;
krb5_init_creds_get_as_reply_key;
krb5_init_creds_get_creds;
krb5_init_creds_get_error;
krb5_init_creds_set_password;
krb5_init_creds_set_sitename;
krb5_init_creds_step;
krb5_init_creds_store;
krb5_init_creds_free;