kdc: sync KDC FAST with Heimdal-597.121.1

Import KDC FAST from Apple's Heimdal-597.121.1, adding support for:

  - PA-ENC-CHALLENGE
  - reply key strengthening
  - FAST authentication in TGS

kuser: Apple sync (squash)

krb5_init_creds_store_config/krb5_init_creds_warn_user in kinit
This commit is contained in:
Luke Howard
2021-08-11 12:24:34 +10:00
parent 47282cae34
commit 9b55215a2a
8 changed files with 478 additions and 367 deletions

View File

@@ -46,6 +46,7 @@ get_fastuser_crypto(astgs_request_t r, krb5_enctype enctype,
*crypto = NULL;
/* TODO: salt cookie key with client name and realm */
ret = krb5_make_principal(r->context, &fast_princ,
KRB5_WELLKNOWN_ORG_H5L_REALM,
KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL);
@@ -116,7 +117,7 @@ fast_parse_cookie(astgs_request_t r, const PA_DATA *pa)
goto out;
if (r->fast.expiration < kdc_time) {
kdc_log(r->context, r->config, 2, "fast cookie expired");
kdc_log(r->context, r->config, 2, "FAST cookie expired");
ret = KRB5KDC_ERR_POLICY;
goto out;
}
@@ -144,11 +145,14 @@ fast_add_cookie(astgs_request_t r, METHOD_DATA *method_data)
&r->fast, &size, ret);
if (ret)
return ret;
heim_assert(size == data.length, "internal asn1 encoder error");
heim_assert(size == data.length, "internal asn.1 encoder error");
ret = get_fastuser_crypto(r, KRB5_ENCTYPE_NULL, &crypto);
if (ret)
if (ret) {
kdc_log(r->context, r->config, 0,
"Failed to find FAST principal for cookie encryption: %d", ret);
goto out;
}
ret = krb5_encrypt_EncryptedData(r->context, crypto,
KRB5_KU_H5L_COOKIE,
@@ -158,7 +162,7 @@ fast_add_cookie(astgs_request_t r, METHOD_DATA *method_data)
if (ret)
goto out;
free(data.data);
krb5_data_free(&data);
shell.version = "H5L1";
@@ -167,14 +171,16 @@ fast_add_cookie(astgs_request_t r, METHOD_DATA *method_data)
free_EncryptedData(&shell.cookie);
if (ret)
goto out;
heim_assert(size == data.length, "internal asn1 encoder error");
heim_assert(size == data.length, "internal asn.1 encoder error");
ret = krb5_padata_add(r->context, method_data,
KRB5_PADATA_FX_COOKIE,
data.data, data.length);
if (ret == 0)
krb5_data_zero(&data);
out:
if (ret)
free(data.data);
krb5_data_free(&data);
return ret;
}
@@ -209,8 +215,7 @@ _kdc_fast_mk_response(krb5_context context,
&fastrep, &size, ret);
if (ret)
return ret;
if (buf.length != size)
krb5_abortx(context, "internal asn.1 error");
heim_assert(size == buf.length, "internal asn.1 encoder error");
fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
@@ -230,8 +235,7 @@ _kdc_fast_mk_response(krb5_context context,
free_PA_FX_FAST_REPLY(&fxfastrep);
if (ret)
return ret;
if (data->length != size)
krb5_abortx(context, "internal asn.1 error");
heim_assert(size == data->length, "internal asn.1 encoder error");
return 0;
}
@@ -244,9 +248,8 @@ _kdc_fast_mk_error(astgs_request_t r,
const KDC_REQ_BODY *req_body,
krb5_error_code outer_error,
const char *e_text,
krb5_principal error_client,
krb5_principal error_server,
const PrincipalName *error_client_name,
const Realm *error_client_realm,
time_t *csec, int *cusec,
krb5_data *error_msg)
{
@@ -257,15 +260,16 @@ _kdc_fast_mk_error(astgs_request_t r,
krb5_data_zero(&e_data);
if (armor_crypto || (r && r->fast.fast_state.len)) {
if (r)
heim_assert(r != NULL, "invalid request in _kdc_fast_mk_error");
/*
* FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
*/
if (armor_crypto || r->fast.fast_state.len) {
ret = fast_add_cookie(r, error_method);
else
ret = krb5_padata_add(context, error_method,
KRB5_PADATA_FX_COOKIE,
NULL, 0);
if (ret) {
kdc_log(r->context, r->config, 1, "failed to add fast cookie with: %d", ret);
kdc_log(r->context, r->config, 1,
"Failed to add FAST cookie: %d", ret);
free_METHOD_DATA(error_method);
return ret;
}
@@ -280,13 +284,12 @@ _kdc_fast_mk_error(astgs_request_t r,
/* first add the KRB-ERROR to the fast errors */
ret = krb5_mk_error_ext(context,
ret = krb5_mk_error(context,
outer_error,
e_text,
NULL,
error_client,
error_server,
error_client_name,
error_client_realm,
NULL,
NULL,
&e_data);
@@ -301,10 +304,14 @@ _kdc_fast_mk_error(astgs_request_t r,
return ret;
}
error_client_name = NULL;
error_client_realm = NULL;
error_server = NULL;
outer_error = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
e_text = NULL;
if (r->fast.flags.requested_hidden_names) {
error_client = NULL;
error_server = NULL;
}
csec = 0;
cusec = 0;
ret = _kdc_fast_mk_response(context, armor_crypto,
error_method, NULL, NULL,
@@ -325,17 +332,15 @@ _kdc_fast_mk_error(astgs_request_t r,
error_method, &size, ret);
if (ret)
return ret;
if (e_data.length != size)
krb5_abortx(context, "internal asn.1 error");
heim_assert(size == e_data.length, "internal asn.1 encoder error");
}
ret = krb5_mk_error_ext(context,
ret = krb5_mk_error(context,
outer_error,
e_text,
(e_data.length ? &e_data : NULL),
error_client,
error_server,
error_client_name,
error_client_realm,
csec,
cusec,
error_msg);
@@ -345,7 +350,9 @@ _kdc_fast_mk_error(astgs_request_t r,
}
static krb5_error_code
fast_unwrap_request(astgs_request_t r)
fast_unwrap_request(astgs_request_t r,
krb5_ticket *tgs_ticket,
krb5_auth_context tgs_ac)
{
krb5_principal armor_server = NULL;
hdb_entry_ex *armor_user = NULL;
@@ -358,44 +365,60 @@ fast_unwrap_request(astgs_request_t r)
krb5_error_code ret;
krb5_ap_req ap_req;
KrbFastReq fastreq;
size_t len, size;
krb5_data data;
const PA_DATA *pa;
krb5_data data;
size_t len;
int i = 0;
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST);
if (pa == NULL)
if (pa == NULL) {
if (tgs_ac && r->fast_asserted) {
kdc_log(r->context, r->config, 1,
"Client asserted FAST but did not include FX-FAST pa-data");
ret = KRB5KRB_AP_ERR_MODIFIED;
goto out;
}
kdc_log(r->context, r->config, 10, "Not a FAST request");
return 0;
}
ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
pa->padata_value.length,
&fxreq,
&len);
if (ret)
goto out;
if (len != pa->padata_value.length) {
ret = KRB5KDC_ERR_PREAUTH_FAILED;
if (ret) {
kdc_log(r->context, r->config, 4,
"Failed to decode PA-FX-FAST-REQUEST: %d", ret);
goto out;
}
if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
kdc_log(r->context, r->config, 2,
"AS-REQ FAST contain unknown type: %d", (int)fxreq.element);
kdc_log(r->context, r->config, 4,
"PA-FX-FAST-REQUEST contains unknown type: %d",
(int)fxreq.element);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out;
}
/* pull out armor key */
if (fxreq.u.armored_data.armor == NULL) {
kdc_log(r->context, r->config, 2,
/*
* If check for armor data or it's not a TGS-REQ with implicit
* armor.
*/
if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
kdc_log(r->context, r->config, 4,
"AS-REQ armor missing");
ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out;
}
/*
*
*/
if (tgs_ac == NULL) {
if (fxreq.u.armored_data.armor->armor_type != 1) {
kdc_log(r->context, r->config, 2,
"AS-REQ armor type not ap-req");
kdc_log(r->context, r->config, 4,
"Incorrect AS-REQ armor type");
ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out;
}
@@ -404,7 +427,7 @@ fast_unwrap_request(astgs_request_t r)
&fxreq.u.armored_data.armor->armor_value,
&ap_req);
if(ret) {
kdc_log(r->context, r->config, 2, "AP-REQ decode failed");
kdc_log(r->context, r->config, 4, "Failed to decode AP-REQ");
goto out;
}
@@ -419,14 +442,12 @@ fast_unwrap_request(astgs_request_t r)
}
ret = _kdc_db_fetch(r->context, r->config, armor_server,
HDB_F_GET_KRBTGT
| HDB_F_DELAY_NEW_KEYS,
HDB_F_GET_KRBTGT | HDB_F_DELAY_NEW_KEYS,
NULL, NULL, &armor_user);
if(ret == HDB_ERR_NOT_FOUND_HERE) {
kdc_log(r->context, r->config, 5,
"armor key does not have secrets at this KDC, "
"Armor key does not have secrets at this KDC, "
"need to proxy");
free_AP_REQ(&ap_req);
goto out;
} else if (ret) {
free_AP_REQ(&ap_req);
@@ -453,6 +474,11 @@ fast_unwrap_request(astgs_request_t r)
free_AP_REQ(&ap_req);
if (ret)
goto out;
} else {
heim_assert(tgs_ticket != NULL, "TGS authentication context without ticket");
ac = tgs_ac;
ticket = tgs_ticket;
}
if (ac->remote_subkey == NULL) {
krb5_auth_con_free(r->context, ac);
@@ -462,29 +488,19 @@ fast_unwrap_request(astgs_request_t r)
goto out;
}
r->fast.flags.kdc_verified =
!_kdc_is_anonymous_pkinit(r->context, ticket->client);
ret = _krb5_fast_armor_key(r->context,
ac->remote_subkey,
&ticket->ticket.key,
&armorkey,
&r->armor_crypto);
krb5_auth_con_free(r->context, ac);
krb5_free_ticket(r->context, ticket);
if (ret)
goto out;
krb5_free_keyblock_contents(r->context, &armorkey);
/* verify req-checksum of the outer body */
ret = _kdc_verify_checksum(r->context, r->armor_crypto,
KRB5_KU_FAST_REQ_CHKSUM,
&r->req.req_body._save,
&fxreq.u.armored_data.req_checksum);
if (ret) {
kdc_log(r->context, r->config, 2,
"FAST request have a bad checksum");
goto out;
}
ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
KRB5_KU_FAST_ENC,
&fxreq.u.armored_data.enc_fast_req,
@@ -495,24 +511,57 @@ fast_unwrap_request(astgs_request_t r)
goto out;
}
ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size);
if (ret) {
ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
krb5_data_free(&data);
goto out;
}
if (data.length != size) {
krb5_data_free(&data);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out;
}
krb5_data_free(&data);
free_KDC_REQ_BODY(&r->req.req_body);
ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
if (ret)
goto out;
/* check for unsupported mandatory options */
/*
* verify req-checksum of the outer body
*/
if (tgs_ac) {
/*
* -- For TGS, contains the checksum performed over the type
* -- AP-REQ in the PA-TGS-REQ padata.
*/
i = 0;
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_TGS_REQ);
if (pa == NULL) {
kdc_log(r->context, r->config, 4,
"FAST TGS request missing TGS-REQ padata");
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
ret = _kdc_verify_checksum(r->context, r->armor_crypto,
KRB5_KU_FAST_REQ_CHKSUM,
&pa->padata_value,
&fxreq.u.armored_data.req_checksum);
if (ret) {
kdc_log(r->context, r->config, 2,
"Bad checksum in FAST TGS request");
goto out;
}
} else {
/*
* -- For AS, contains the checksum performed over the type
* -- KDC-REQ-BODY for the req-body field of the KDC-REQ
* -- structure;
*/
ret = _kdc_verify_checksum(r->context, r->armor_crypto,
KRB5_KU_FAST_REQ_CHKSUM,
&r->req.req_body._save,
&fxreq.u.armored_data.req_checksum);
if (ret) {
kdc_log(r->context, r->config, 2,
"Bad checksum in FAST AS request");
goto out;
}
}
/*
* check for unsupported mandatory options
*/
if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
kdc_log(r->context, r->config, 2,
"FAST unsupported mandatory option set");
@@ -520,6 +569,8 @@ fast_unwrap_request(astgs_request_t r)
goto out;
}
r->fast.flags.requested_hidden_names = fastreq.fast_options.hide_client_names;
/* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
if (r->req.padata)
free_METHOD_DATA(r->req.padata);
@@ -530,10 +581,22 @@ fast_unwrap_request(astgs_request_t r)
if (ret)
goto out;
free_KDC_REQ_BODY(&r->req.req_body);
ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body);
if (ret)
goto out;
free_KrbFastReq(&fastreq);
free_PA_FX_FAST_REQUEST(&fxreq);
kdc_log(r->context, r->config, 5, "Client selected FAST");
out:
if (ac && ac != tgs_ac)
krb5_auth_con_free(r->context, ac);
if (ticket && ticket != tgs_ticket)
krb5_free_ticket(r->context, ticket);
if (armor_server)
krb5_free_principal(r->context, armor_server);
if(armor_user)
@@ -542,30 +605,70 @@ fast_unwrap_request(astgs_request_t r)
return ret;
}
/*
*
*/
krb5_error_code
_kdc_fast_unwrap_request(astgs_request_t r)
_kdc_fast_unwrap_request(astgs_request_t r,
krb5_ticket *tgs_ticket,
krb5_auth_context tgs_ac)
{
krb5_error_code ret;
const PA_DATA *pa;
int i = 0;
ret = fast_unwrap_request(r);
ret = fast_unwrap_request(r, tgs_ticket, tgs_ac);
if (ret)
return ret;
/*
* Non-FAST mechanisms may use FX-COOKIE to manage state.
* FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
*/
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
if (pa) {
if (pa)
ret = fast_parse_cookie(r, pa);
return ret;
}
/*
* Strengthen reply key by mixing with a random key that is
* protected by FAST.
*/
krb5_error_code
_kdc_fast_strengthen_reply_key(astgs_request_t r)
{
if (r->armor_crypto) {
krb5_keyblock new_reply_key;
krb5_error_code ret;
kdc_log(r->context, r->config, 5,
"FAST strengthen reply key with strengthen-key");
heim_assert(r->reply_key.keytype != KRB5_ENCTYPE_NULL, "NULL reply key enctype");
ret = krb5_generate_random_keyblock(r->context, r->reply_key.keytype,
&r->strengthen_key);
if (ret)
krb5_abortx(r->context, "random generator fail");
ret = _krb5_fast_cf2(r->context,
&r->strengthen_key, "strengthenkey",
&r->reply_key, "replykey",
&new_reply_key, NULL);
if (ret)
return ret;
krb5_free_keyblock_contents(r->context, &r->reply_key);
r->reply_key = new_reply_key;
}
return 0;
}
/*
* Zero and free KDCFastState
*/
void
_kdc_free_fast_state(KDCFastState *state)
{

View File

@@ -269,6 +269,8 @@ eval_kinit(heim_dict_t o)
ret = krb5_init_creds_set_fast_ccache(kdc_context, ctx, fast_cc);
if (ret)
krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_fast_ccache");
fast_cc = NULL;
}
if (password) {

View File

@@ -93,6 +93,13 @@ struct astgs_request_desc {
krb5_timestamp pa_endtime;
krb5_timestamp pa_max_life;
krb5_keyblock strengthen_key;
const Key *ticket_key;
/* only valid for tgs-req */
unsigned int rk_is_subkey : 1;
unsigned int fast_asserted : 1;
krb5_crypto armor_crypto;
KDCFastState fast;

View File

@@ -555,62 +555,10 @@ pa_gss_validate(astgs_request_t r, const PA_DATA *pa)
return ret;
}
/*
*
*/
static krb5_error_code
make_pa_enc_challange(astgs_request_t r, krb5_crypto crypto)
{
krb5_context context = r->context;
METHOD_DATA *md = &r->outpadata;
PA_ENC_TS_ENC p;
unsigned char *buf;
size_t buf_size;
size_t len;
EncryptedData encdata;
krb5_error_code ret;
int32_t usec;
int usec2;
krb5_us_timeofday (context, &p.patimestamp, &usec);
usec2 = usec;
p.pausec = &usec2;
ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
if (ret)
return ret;
if(buf_size != len)
krb5_abortx(context, "internal error in ASN.1 encoder");
ret = krb5_encrypt_EncryptedData(context,
crypto,
KRB5_KU_ENC_CHALLENGE_KDC,
buf,
len,
0,
&encdata);
free(buf);
if (ret)
return ret;
ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
free_EncryptedData(&encdata);
if (ret)
return ret;
if(buf_size != len)
krb5_abortx(context, "internal error in ASN.1 encoder");
ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
if (ret)
free(buf);
return ret;
}
static krb5_error_code
pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
{
krb5_data pepper1, pepper2, ts_data;
krb5_data pepper1, pepper2;
int invalidPassword = 0;
EncryptedData enc_data;
krb5_enctype aenctype;
@@ -652,10 +600,11 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
krb5_crypto_getenctype(r->context, r->armor_crypto, &aenctype);
kdc_log(r->context, r->config, 5, "FAST armor enctype is: %d", (int)aenctype);
for (i = 0; i < r->client->entry.keys.len; i++) {
krb5_crypto challangecrypto, longtermcrypto;
krb5_keyblock challangekey;
PA_ENC_TS_ENC p;
krb5_crypto challengecrypto, longtermcrypto;
krb5_keyblock challengekey;
k = &r->client->entry.keys.val[i];
@@ -665,26 +614,32 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
ret = krb5_crypto_fx_cf2(r->context, r->armor_crypto, longtermcrypto,
&pepper1, &pepper2, aenctype,
&challangekey);
&challengekey);
if (ret) {
krb5_crypto_destroy(r->context, longtermcrypto);
if (ret)
continue;
}
ret = krb5_crypto_init(r->context, &challangekey, 0,
&challangecrypto);
if (ret)
ret = krb5_crypto_init(r->context, &challengekey, 0,
&challengecrypto);
krb5_free_keyblock_contents(r->context, &challengekey);
if (ret) {
krb5_crypto_destroy(r->context, longtermcrypto);
continue;
}
ret = krb5_decrypt_EncryptedData(r->context, challangecrypto,
ret = _krb5_validate_pa_enc_challenge(r->context,
challengecrypto,
KRB5_KU_ENC_CHALLENGE_CLIENT,
&enc_data,
&ts_data);
r->cname);
krb5_crypto_destroy(r->context, challengecrypto);
if (ret) {
const char *msg = krb5_get_error_message(r->context, ret);
krb5_error_code ret2;
char *str = NULL;
invalidPassword = 1;
invalidPassword = (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY);
ret2 = krb5_enctype_to_string(r->context, k->key.keytype, &str);
if (ret2)
@@ -695,54 +650,51 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
krb5_free_error_message(r->context, msg);
free(str);
krb5_crypto_destroy(r->context, longtermcrypto);
continue;
}
ret = decode_PA_ENC_TS_ENC(ts_data.data,
ts_data.length,
&p,
&size);
krb5_data_free(&ts_data);
/*
* Found a key that the client used, lets pick that as the reply key
*/
krb5_free_keyblock_contents(r->context, &r->reply_key);
ret = krb5_copy_keyblock_contents(r->context, &k->key, &r->reply_key);
if (ret) {
krb5_crypto_destroy(r->context, challangecrypto);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
_kdc_r_log(r, 4, "Failed to decode PA-ENC-TS_ENC -- %s",
r->cname);
continue;
}
if (labs(kdc_time - p.patimestamp) > r->context->max_skew) {
char client_time[100];
krb5_crypto_destroy(r->context, challangecrypto);
krb5_format_time(r->context, p.patimestamp,
client_time, sizeof(client_time), TRUE);
ret = KRB5KRB_AP_ERR_SKEW;
_kdc_r_log(r, 4, "Too large time skew, "
"client time %s is out by %u > %u seconds -- %s",
client_time,
(unsigned)labs(kdc_time - p.patimestamp),
r->context->max_skew,
r->cname);
free_PA_ENC_TS_ENC(&p);
krb5_crypto_destroy(r->context, longtermcrypto);
goto out;
}
free_PA_ENC_TS_ENC(&p);
krb5_free_keyblock_contents(r->context, &challengekey);
ret = make_pa_enc_challange(r, challangecrypto);
krb5_crypto_destroy(r->context, challangecrypto);
/*
* Provide KDC authentication to the client, uses a different
* challenge key (different pepper).
*/
pepper1.data = "kdcchallengearmor";
pepper1.length = strlen(pepper1.data);
ret = krb5_crypto_fx_cf2(r->context, r->armor_crypto, longtermcrypto,
&pepper1, &pepper2, aenctype,
&challengekey);
krb5_crypto_destroy(r->context, longtermcrypto);
if (ret)
goto out;
ret = krb5_crypto_init(r->context, &challengekey, 0, &challengecrypto);
krb5_free_keyblock_contents(r->context, &challengekey);
if (ret)
goto out;
ret = _krb5_make_pa_enc_challenge(r->context, challengecrypto,
KRB5_KU_ENC_CHALLENGE_KDC,
&r->outpadata);
krb5_crypto_destroy(r->context, challengecrypto);
if (ret)
goto out;
set_salt_padata(&r->outpadata, k->salt);
krb5_free_keyblock_contents(r->context, &r->reply_key);
ret = krb5_copy_keyblock_contents(r->context, &k->key, &r->reply_key);
if (ret)
goto out;
/*
* Success
@@ -753,10 +705,11 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
goto out;
}
ret = KRB5KDC_ERR_PREAUTH_FAILED;
if (invalidPassword && r->clientdb->hdb_auth_status) {
r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client,
HDB_AUTH_WRONG_PASSWORD);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
}
out:
free_EncryptedData(&enc_data);
@@ -1016,11 +969,11 @@ log_patypes(astgs_request_t r, METHOD_DATA *padata)
krb5_error_code
_kdc_encode_reply(krb5_context context,
krb5_kdc_configuration *config,
krb5_crypto armor_crypto, uint32_t nonce,
astgs_request_t r, uint32_t nonce,
KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek,
krb5_enctype etype,
int skvno, const EncryptionKey *skey,
int ckvno, const EncryptionKey *reply_key,
int ckvno,
int rk_is_subkey,
const char **e_text,
krb5_data *reply)
@@ -1066,10 +1019,9 @@ _kdc_encode_reply(krb5_context context,
return ret;
}
if (armor_crypto) {
krb5_data data;
krb5_keyblock *strengthen_key = NULL;
if (r && r->armor_crypto) {
KrbFastFinished finished;
krb5_data data;
kdc_log(context, config, 4, "FAST armor protection");
@@ -1088,7 +1040,7 @@ _kdc_encode_reply(krb5_context context,
if (data.length != len)
krb5_abortx(context, "internal asn.1 error");
ret = krb5_create_checksum(context, armor_crypto,
ret = krb5_create_checksum(context, r->armor_crypto,
KRB5_KU_FAST_FINISHED, 0,
data.data, data.length,
&finished.ticket_checksum);
@@ -1096,8 +1048,8 @@ _kdc_encode_reply(krb5_context context,
if (ret)
return ret;
ret = _kdc_fast_mk_response(context, armor_crypto,
rep->padata, strengthen_key, &finished,
ret = _kdc_fast_mk_response(context, r->armor_crypto,
rep->padata, &r->strengthen_key, &finished,
nonce, &data);
free_Checksum(&finished.ticket_checksum);
if (ret)
@@ -1120,9 +1072,9 @@ _kdc_encode_reply(krb5_context context,
return ret;
/*
* Hide client name of privacy reasons
* Hide client name for privacy reasons
*/
if (1 /* r->fast_options.hide_client_names */) {
if (r->fast.flags.requested_hidden_names) {
Realm anon_realm = KRB5_ANON_REALM;
free_Realm(&rep->crealm);
@@ -1152,7 +1104,7 @@ _kdc_encode_reply(krb5_context context,
*e_text = "KDC internal error";
return KRB5KRB_ERR_GENERIC;
}
ret = krb5_crypto_init(context, reply_key, 0, &crypto);
ret = krb5_crypto_init(context, &r->reply_key, 0, &crypto);
if (ret) {
const char *msg = krb5_get_error_message(context, ret);
free(buf);
@@ -1988,7 +1940,6 @@ _kdc_as_rep(astgs_request_t r)
Key *skey;
int found_pa = 0;
int i, flags = HDB_F_FOR_AS_REQ;
METHOD_DATA error_method;
const PA_DATA *pa;
krb5_boolean is_tgs;
const char *msg;
@@ -1996,13 +1947,11 @@ _kdc_as_rep(astgs_request_t r)
Key *krbtgt_key;
memset(&rep, 0, sizeof(rep));
error_method.len = 0;
error_method.val = NULL;
/*
* Look for FAST armor and unwrap
*/
ret = _kdc_fast_unwrap_request(r);
ret = _kdc_fast_unwrap_request(r, NULL, NULL);
if (ret) {
_kdc_r_log(r, 1, "FAST unwrap request from %s failed: %d", from, ret);
goto out;
@@ -2081,10 +2030,10 @@ _kdc_as_rep(astgs_request_t r)
r->cname, fixed_client_name);
free(fixed_client_name);
ret = _kdc_fast_mk_error(r, &error_method, r->armor_crypto,
ret = _kdc_fast_mk_error(r, &r->outpadata, r->armor_crypto,
&req->req_body, KRB5_KDC_ERR_WRONG_REALM,
NULL, r->server_princ, NULL,
&r->client->entry.principal->realm,
NULL,
r->client->entry.principal, r->server_princ,
NULL, NULL, r->reply);
goto out;
}
@@ -2174,7 +2123,7 @@ _kdc_as_rep(astgs_request_t r)
NULL, &ckey, &default_salt);
if (ret2 == 0) {
ret2 = get_pa_etype_info_both(context, config, &b->etype,
&error_method, ckey, !default_salt);
&r->outpadata, ckey, !default_salt);
if (ret2 != 0)
ret = ret2;
}
@@ -2203,7 +2152,7 @@ _kdc_as_rep(astgs_request_t r)
for (n = 0; n < sizeof(pat) / sizeof(pat[0]); n++) {
if ((pat[n].flags & PA_ANNOUNCE) == 0)
continue;
ret = krb5_padata_add(context, &error_method,
ret = krb5_padata_add(context, &r->outpadata,
pat[n].type, NULL, 0);
if (ret)
goto out;
@@ -2217,7 +2166,7 @@ _kdc_as_rep(astgs_request_t r)
NULL, &ckey, &default_salt);
if (ret == 0) {
ret = get_pa_etype_info_both(context, config, &b->etype,
&error_method, ckey, !default_salt);
&r->outpadata, ckey, !default_salt);
if (ret)
goto out;
}
@@ -2253,7 +2202,7 @@ _kdc_as_rep(astgs_request_t r)
* with in a preauth mech.
*/
ret = _kdc_check_access(r, req, &error_method);
ret = _kdc_check_access(r, req, &r->outpadata);
if(ret)
goto out;
@@ -2577,6 +2526,14 @@ _kdc_as_rep(astgs_request_t r)
r->et.flags.enc_pa_rep = r->ek.flags.enc_pa_rep = 1;
/*
* update reply-key with strengthen-key
*/
ret = _kdc_fast_strengthen_reply_key(r);
if (ret)
goto out;
/*
* Add REQ_ENC_PA_REP if client supports it
*/
@@ -2599,11 +2556,11 @@ _kdc_as_rep(astgs_request_t r)
*/
ret = _kdc_encode_reply(context, config,
r->armor_crypto, req->req_body.nonce,
r, req->req_body.nonce,
&rep, &r->et, &r->ek, setype,
r->server->entry.kvno, &skey->key,
r->client->entry.kvno,
&r->reply_key, 0, &r->e_text, r->reply);
0, &r->e_text, r->reply);
if (ret)
goto out;
@@ -2624,16 +2581,12 @@ out:
*/
if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0)
ret = _kdc_fast_mk_error(r,
(ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
? &r->outpadata : &error_method,
&r->outpadata,
r->armor_crypto,
&req->req_body,
ret, r->e_text,
r->client_princ,
r->server_princ,
r->client_princ ?
&r->client_princ->name : NULL,
r->client_princ ?
&r->client_princ->realm : NULL,
NULL, NULL,
r->reply);
@@ -2641,8 +2594,6 @@ out:
free_EncKDCRepPart(&r->ek);
_kdc_free_fast_state(&r->fast);
if (error_method.len)
free_METHOD_DATA(&error_method);
if (r->outpadata.len)
free_METHOD_DATA(&r->outpadata);
if (r->client_princ) {
@@ -2665,5 +2616,7 @@ out:
}
krb5_free_keyblock_contents(r->context, &r->reply_key);
krb5_free_keyblock_contents(r->context, &r->session_key);
krb5_free_keyblock_contents(r->context, &r->strengthen_key);
return ret;
}

View File

@@ -515,8 +515,6 @@ static krb5_error_code
tgs_make_reply(astgs_request_t r,
krb5_principal tgt_name,
const EncTicketPart *tgt,
const krb5_keyblock *replykey,
int rk_is_subkey,
const EncryptionKey *serverkey,
const EncryptionKey *krbtgtkey,
const krb5_keyblock *sessionkey,
@@ -532,8 +530,6 @@ tgs_make_reply(astgs_request_t r,
krb5_boolean add_ticket_sig,
const METHOD_DATA *enc_pa_data)
{
krb5_context context = r->context;
krb5_kdc_configuration *config = r->config;
KDC_REQ_BODY *b = &r->req.req_body;
const char **e_text = &r->e_text;
krb5_data *reply = r->reply;
@@ -570,17 +566,17 @@ tgs_make_reply(astgs_request_t r,
*/
#define GLOBAL_FORCE_TRANSITED_CHECK \
(config->trpolicy == TRPOLICY_ALWAYS_CHECK)
(r->config->trpolicy == TRPOLICY_ALWAYS_CHECK)
#define GLOBAL_ALLOW_PER_PRINCIPAL \
(config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
(r->config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
(config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
(r->config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
/* these will consult the database in future release */
#define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
ret = fix_transited_encoding(context, config,
ret = fix_transited_encoding(r->context, r->config,
!f.disable_transited_check ||
GLOBAL_FORCE_TRANSITED_CHECK ||
PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
@@ -588,8 +584,8 @@ tgs_make_reply(astgs_request_t r,
PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
&tgt->transited, &et,
krb5_principal_get_realm(context, client_principal),
krb5_principal_get_realm(context, server->entry.principal),
krb5_principal_get_realm(r->context, client_principal),
krb5_principal_get_realm(r->context, server->entry.principal),
tgt_realm);
if(ret)
goto out;
@@ -682,20 +678,20 @@ tgs_make_reply(astgs_request_t r,
et.authorization_data = calloc(1, sizeof(*et.authorization_data));
if (et.authorization_data == NULL) {
ret = ENOMEM;
krb5_set_error_message(context, ret, "malloc: out of memory");
krb5_set_error_message(r->context, ret, "malloc: out of memory");
goto out;
}
}
for(i = 0; i < auth_data->len ; i++) {
ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
if (ret) {
krb5_set_error_message(context, ret, "malloc: out of memory");
krb5_set_error_message(r->context, ret, "malloc: out of memory");
goto out;
}
}
}
ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
ret = krb5_copy_keyblock_contents(r->context, sessionkey, &et.key);
if (ret)
goto out;
et.crealm = rep.crealm;
@@ -732,10 +728,10 @@ tgs_make_reply(astgs_request_t r,
goto out;
}
if (krb5_enctype_valid(context, serverkey->keytype) != 0
if (krb5_enctype_valid(r->context, serverkey->keytype) != 0
&& _kdc_is_weak_exception(server->entry.principal, serverkey->keytype))
{
krb5_enctype_enable(context, serverkey->keytype);
krb5_enctype_enable(r->context, serverkey->keytype);
is_weak = 1;
}
@@ -748,7 +744,7 @@ tgs_make_reply(astgs_request_t r,
if (mspac && !et.flags.anonymous) {
/* The PAC should be the last change to the ticket. */
ret = _krb5_kdc_pac_sign_ticket(context, mspac, tgt_name, serverkey,
ret = _krb5_kdc_pac_sign_ticket(r->context, mspac, tgt_name, serverkey,
krbtgtkey, rodc_id, add_ticket_sig, &et);
if (ret)
goto out;
@@ -764,15 +760,14 @@ tgs_make_reply(astgs_request_t r,
CAST session key. Should the DES3 etype be added to the
etype list, even if we don't want a session key with
DES3? */
ret = _kdc_encode_reply(context, config, NULL, 0,
ret = _kdc_encode_reply(r->context, r->config, r, b->nonce,
&rep, &et, &ek, serverkey->keytype,
kvno,
serverkey, 0, replykey, rk_is_subkey,
serverkey, 0, r->rk_is_subkey,
e_text, reply);
if (is_weak)
krb5_enctype_disable(context, serverkey->keytype);
krb5_enctype_disable(r->context, serverkey->keytype);
r->reply_key.keytype = replykey->keytype;
_log_astgs_req(r, serverkey->keytype);
out:
@@ -887,6 +882,33 @@ need_referral(krb5_context context, krb5_kdc_configuration *config,
return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
}
static krb5_error_code
validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data)
{
krb5_error_code ret;
krb5_data data;
krb5_data_zero(&data);
ret = _krb5_get_ad(r->context, auth_data, NULL,
KRB5_AUTHDATA_FX_FAST_USED, &data);
if (ret == 0) {
r->fast_asserted = 1;
krb5_data_free(&data);
}
ret = _krb5_get_ad(r->context, auth_data, NULL,
KRB5_AUTHDATA_FX_FAST_ARMOR, &data);
if (ret == 0) {
kdc_log(r->context, r->config, 2,
"Invalid ticket usage: TGS-REQ contains AD-fx-fast-armor");
krb5_data_free(&data);
return KRB5KRB_AP_ERR_BAD_INTEGRITY;
}
return 0;
}
static krb5_error_code
tgs_parse_request(astgs_request_t r,
const PA_DATA *tgs_req,
@@ -898,10 +920,7 @@ tgs_parse_request(astgs_request_t r,
const struct sockaddr *from_addr,
time_t **csec,
int **cusec,
AuthorizationData **auth_data,
krb5_keyblock **replykey,
Key **header_key,
int *rk_is_subkey)
AuthorizationData **auth_data)
{
krb5_context context = r->context;
krb5_kdc_configuration *config = r->config;
@@ -925,7 +944,6 @@ tgs_parse_request(astgs_request_t r,
*auth_data = NULL;
*csec = NULL;
*cusec = NULL;
*replykey = NULL;
memset(&ap_req, 0, sizeof(ap_req));
ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
@@ -1070,7 +1088,7 @@ next_kvno:
goto out;
}
*header_key = tkey;
r->ticket_key = tkey;
{
krb5_authenticator auth;
@@ -1091,7 +1109,11 @@ next_kvno:
goto out;
}
**cusec = auth->cusec;
ret = validate_fast_ad(r, auth->authorization_data);
krb5_free_authenticator(context, &auth);
if (ret)
goto out;
}
}
@@ -1103,7 +1125,7 @@ next_kvno:
}
usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
*rk_is_subkey = 1;
r->rk_is_subkey = 1;
ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
if(ret){
@@ -1115,7 +1137,7 @@ next_kvno:
}
if(subkey == NULL){
usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
*rk_is_subkey = 0;
r->rk_is_subkey = 0;
ret = krb5_auth_con_getkey(context, ac, &subkey);
if(ret) {
@@ -1134,12 +1156,16 @@ next_kvno:
goto out;
}
*replykey = subkey;
krb5_free_keyblock_contents(r->context, &r->reply_key);
ret = krb5_copy_keyblock_contents(r->context, subkey, &r->reply_key);
krb5_free_keyblock(r->context, subkey);
if (ret)
goto out;
if (b->enc_authorization_data) {
krb5_data ad;
ret = krb5_crypto_init(context, subkey, 0, &crypto);
ret = krb5_crypto_init(context, &r->reply_key, 0, &crypto);
if (ret) {
const char *msg = krb5_get_error_message(context, ret);
krb5_auth_con_free(context, ac);
@@ -1175,8 +1201,21 @@ next_kvno:
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
goto out;
}
ret = validate_fast_ad(r, *auth_data);
if (ret)
goto out;
}
/*
* Check for FAST request
*/
ret = _kdc_fast_unwrap_request(r, *ticket, ac);
if (ret)
goto out;
krb5_auth_con_free(context, ac);
out:
@@ -1316,9 +1355,6 @@ static krb5_error_code
tgs_build_reply(astgs_request_t priv,
hdb_entry_ex *krbtgt,
krb5_enctype krbtgt_etype,
Key *tkey_check,
const krb5_keyblock *replykey,
int rk_is_subkey,
krb5_ticket *ticket,
const char **e_text,
AuthorizationData **auth_data,
@@ -1781,7 +1817,7 @@ server_lookup:
priv->client = client;
ret = check_PAC(context, config, cp, NULL, client, server, krbtgt, krbtgt,
&tkey_check->key, &tkey_check->key, tgt, &kdc_issued, &mspac);
&priv->ticket_key->key, &priv->ticket_key->key, tgt, &kdc_issued, &mspac);
if (ret) {
const char *msg = krb5_get_error_message(context, ret);
_kdc_audit_addreason((kdc_request_t)priv, "PAC check failed");
@@ -2101,7 +2137,7 @@ server_lookup:
* a S4U_DELEGATION_INFO blob to the PAC.
*/
ret = check_PAC(context, config, tp, dp, adclient, server, krbtgt, client,
&clientkey->key, &tkey_check->key, &adtkt, &ad_kdc_issued, &mspac);
&clientkey->key, &priv->ticket_key->key, &adtkt, &ad_kdc_issued, &mspac);
if (adclient)
_kdc_free_ent(context, adclient);
if (ret) {
@@ -2230,8 +2266,6 @@ server_lookup:
ret = tgs_make_reply(priv,
tp,
tgt,
replykey,
rk_is_subkey,
ekey,
&tkey_sign->key,
&sessionkey,
@@ -2297,16 +2331,13 @@ _kdc_tgs_rep(astgs_request_t r)
AuthorizationData *auth_data = NULL;
krb5_error_code ret;
int i = 0;
const PA_DATA *tgs_req;
Key *header_key = NULL;
const PA_DATA *tgs_req, *pa;
hdb_entry_ex *krbtgt = NULL;
krb5_ticket *ticket = NULL;
const char *e_text = NULL;
krb5_enctype krbtgt_etype = ETYPE_NULL;
krb5_keyblock *replykey = NULL;
int rk_is_subkey = 0;
time_t *csec = NULL;
int *cusec = NULL;
@@ -2317,8 +2348,16 @@ _kdc_tgs_rep(astgs_request_t r)
goto out;
}
tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
i = 0;
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST_ARMOR);
if (pa) {
kdc_log(r->context, r->config, 10, "Found TGS-REQ FAST armor inside TGS-REQ pa-data");
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
i = 0;
tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
if(tgs_req == NULL){
ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
@@ -2333,10 +2372,7 @@ _kdc_tgs_rep(astgs_request_t r)
&e_text,
from, from_addr,
&csec, &cusec,
&auth_data,
&replykey,
&header_key,
&rk_is_subkey);
&auth_data);
if (ret == HDB_ERR_NOT_FOUND_HERE) {
/* kdc_log() is called in tgs_parse_request() */
goto out;
@@ -2347,19 +2383,13 @@ _kdc_tgs_rep(astgs_request_t r)
goto out;
}
{
const PA_DATA *pa = _kdc_find_padata(req, &i, KRB5_PADATA_FX_FAST);
if (pa)
kdc_log(context, config, 5, "Got TGS FAST request");
}
ret = _kdc_fast_strengthen_reply_key(r);
if (ret)
goto out;
ret = tgs_build_reply(r,
krbtgt,
krbtgt_etype,
header_key,
replykey,
rk_is_subkey,
ticket,
&e_text,
&auth_data,
@@ -2378,21 +2408,15 @@ _kdc_tgs_rep(astgs_request_t r)
}
out:
if (replykey)
krb5_free_keyblock(context, replykey);
if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
/* XXX add fast wrapping on the error */
METHOD_DATA error_method = { 0, NULL };
kdc_log(context, config, 5, "tgs-req: sending error: %d to client", ret);
ret = _kdc_fast_mk_error(r,
&error_method,
NULL,
NULL,
ret, NULL,
NULL,
r->armor_crypto,
&req->req_body,
ret, r->e_text,
NULL, NULL,
csec, cusec,
data);
@@ -2400,11 +2424,17 @@ out:
}
free(csec);
free(cusec);
krb5_free_keyblock_contents(r->context, &r->reply_key);
krb5_free_keyblock_contents(r->context, &r->strengthen_key);
if (ticket)
krb5_free_ticket(context, ticket);
if(krbtgt)
_kdc_free_ent(context, krbtgt);
_kdc_free_fast_state(&r->fast);
if (auth_data) {
free_AuthorizationData(auth_data);
free(auth_data);

View File

@@ -1077,6 +1077,18 @@ get_new_tickets(krb5_context context,
goto out;
}
ret = krb5_init_creds_store_config(context, ctx, tempccache);
if (ret) {
krb5_warn(context, ret, "krb5_init_creds_store_config");
goto out;
}
ret = krb5_init_creds_warn_user(context, ctx);
if (ret) {
krb5_warn(context, ret, "krb5_init_creds_warn_user");
goto out;
}
krb5_init_creds_free(context, ctx);
ctx = NULL;

View File

@@ -862,6 +862,8 @@ EXPORTS
krb5_init_creds_set_sitename
krb5_init_creds_step
krb5_init_creds_store
krb5_init_creds_store_config
krb5_init_creds_warn_user
krb5_process_last_request
; testing

View File

@@ -850,7 +850,9 @@ HEIMDAL_KRB5_2.0 {
krb5_init_creds_set_sitename;
krb5_init_creds_step;
krb5_init_creds_store;
krb5_init_creds_store_config;
krb5_init_creds_free;
krb5_init_creds_warn_user;
# testing
krb5_time_abs;