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

@@ -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,
@@ -526,14 +524,12 @@ tgs_make_reply(astgs_request_t r,
krb5_principal server_principal,
hdb_entry_ex *client,
krb5_principal client_principal,
const char *tgt_realm,
const char *tgt_realm,
krb5_pac mspac,
uint16_t rodc_id,
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);