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; *crypto = NULL;
/* TODO: salt cookie key with client name and realm */
ret = krb5_make_principal(r->context, &fast_princ, ret = krb5_make_principal(r->context, &fast_princ,
KRB5_WELLKNOWN_ORG_H5L_REALM, KRB5_WELLKNOWN_ORG_H5L_REALM,
KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL); 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; goto out;
if (r->fast.expiration < kdc_time) { 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; ret = KRB5KDC_ERR_POLICY;
goto out; goto out;
} }
@@ -140,15 +141,18 @@ fast_add_cookie(astgs_request_t r, METHOD_DATA *method_data)
r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME; r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME;
ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length,
&r->fast, &size, ret); &r->fast, &size, ret);
if (ret) if (ret)
return 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); 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; goto out;
}
ret = krb5_encrypt_EncryptedData(r->context, crypto, ret = krb5_encrypt_EncryptedData(r->context, crypto,
KRB5_KU_H5L_COOKIE, KRB5_KU_H5L_COOKIE,
@@ -157,24 +161,26 @@ fast_add_cookie(astgs_request_t r, METHOD_DATA *method_data)
krb5_crypto_destroy(r->context, crypto); krb5_crypto_destroy(r->context, crypto);
if (ret) if (ret)
goto out; goto out;
free(data.data); krb5_data_free(&data);
shell.version = "H5L1"; shell.version = "H5L1";
ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length,
&shell, &size, ret); &shell, &size, ret);
free_EncryptedData(&shell.cookie); free_EncryptedData(&shell.cookie);
if (ret) if (ret)
goto out; 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, ret = krb5_padata_add(r->context, method_data,
KRB5_PADATA_FX_COOKIE, KRB5_PADATA_FX_COOKIE,
data.data, data.length); data.data, data.length);
if (ret == 0)
krb5_data_zero(&data);
out: out:
if (ret) krb5_data_free(&data);
free(data.data);
return ret; return ret;
} }
@@ -209,9 +215,8 @@ _kdc_fast_mk_response(krb5_context context,
&fastrep, &size, ret); &fastrep, &size, ret);
if (ret) if (ret)
return ret; return ret;
if (buf.length != size) heim_assert(size == buf.length, "internal asn.1 encoder error");
krb5_abortx(context, "internal asn.1 error");
fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data; fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data;
ret = krb5_encrypt_EncryptedData(context, ret = krb5_encrypt_EncryptedData(context,
@@ -230,9 +235,8 @@ _kdc_fast_mk_response(krb5_context context,
free_PA_FX_FAST_REPLY(&fxfastrep); free_PA_FX_FAST_REPLY(&fxfastrep);
if (ret) if (ret)
return ret; return ret;
if (data->length != size) heim_assert(size == data->length, "internal asn.1 encoder error");
krb5_abortx(context, "internal asn.1 error");
return 0; return 0;
} }
@@ -244,9 +248,8 @@ _kdc_fast_mk_error(astgs_request_t r,
const KDC_REQ_BODY *req_body, const KDC_REQ_BODY *req_body,
krb5_error_code outer_error, krb5_error_code outer_error,
const char *e_text, const char *e_text,
krb5_principal error_client,
krb5_principal error_server, krb5_principal error_server,
const PrincipalName *error_client_name,
const Realm *error_client_realm,
time_t *csec, int *cusec, time_t *csec, int *cusec,
krb5_data *error_msg) krb5_data *error_msg)
{ {
@@ -257,15 +260,16 @@ _kdc_fast_mk_error(astgs_request_t r,
krb5_data_zero(&e_data); krb5_data_zero(&e_data);
if (armor_crypto || (r && r->fast.fast_state.len)) { heim_assert(r != NULL, "invalid request in _kdc_fast_mk_error");
if (r)
ret = fast_add_cookie(r, error_method); /*
else * FX-COOKIE can be used outside of FAST, e.g. SRP or GSS.
ret = krb5_padata_add(context, error_method, */
KRB5_PADATA_FX_COOKIE, if (armor_crypto || r->fast.fast_state.len) {
NULL, 0); ret = fast_add_cookie(r, error_method);
if (ret) { 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); free_METHOD_DATA(error_method);
return ret; return ret;
} }
@@ -277,19 +281,18 @@ _kdc_fast_mk_error(astgs_request_t r,
memset(&fxfastrep, 0, sizeof(fxfastrep)); memset(&fxfastrep, 0, sizeof(fxfastrep));
memset(&fastrep, 0, sizeof(fastrep)); memset(&fastrep, 0, sizeof(fastrep));
/* first add the KRB-ERROR to the fast errors */ /* first add the KRB-ERROR to the fast errors */
ret = krb5_mk_error_ext(context, ret = krb5_mk_error(context,
outer_error, outer_error,
e_text, e_text,
NULL, NULL,
error_server, error_client,
error_client_name, error_server,
error_client_realm, NULL,
NULL, NULL,
NULL, &e_data);
&e_data);
if (ret) if (ret)
return ret; return ret;
@@ -301,18 +304,22 @@ _kdc_fast_mk_error(astgs_request_t r,
return ret; return ret;
} }
error_client_name = NULL; outer_error = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
error_client_realm = NULL; e_text = NULL;
error_server = NULL; if (r->fast.flags.requested_hidden_names) {
e_text = NULL; error_client = NULL;
error_server = NULL;
}
csec = 0;
cusec = 0;
ret = _kdc_fast_mk_response(context, armor_crypto, ret = _kdc_fast_mk_response(context, armor_crypto,
error_method, NULL, NULL, error_method, NULL, NULL,
req_body->nonce, &e_data); req_body->nonce, &e_data);
free_METHOD_DATA(error_method); free_METHOD_DATA(error_method);
if (ret) if (ret)
return ret; return ret;
ret = krb5_padata_add(context, error_method, ret = krb5_padata_add(context, error_method,
KRB5_PADATA_FX_FAST, KRB5_PADATA_FX_FAST,
e_data.data, e_data.length); e_data.data, e_data.length);
@@ -321,31 +328,31 @@ _kdc_fast_mk_error(astgs_request_t r,
} }
if (error_method && error_method->len) { if (error_method && error_method->len) {
ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length,
error_method, &size, ret); error_method, &size, ret);
if (ret) if (ret)
return ret; return ret;
if (e_data.length != size) heim_assert(size == e_data.length, "internal asn.1 encoder error");
krb5_abortx(context, "internal asn.1 error");
} }
ret = krb5_mk_error_ext(context, ret = krb5_mk_error(context,
outer_error, outer_error,
e_text, e_text,
(e_data.length ? &e_data : NULL), (e_data.length ? &e_data : NULL),
error_server, error_client,
error_client_name, error_server,
error_client_realm, csec,
csec, cusec,
cusec, error_msg);
error_msg);
krb5_data_free(&e_data); krb5_data_free(&e_data);
return ret; return ret;
} }
static krb5_error_code 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; krb5_principal armor_server = NULL;
hdb_entry_ex *armor_user = NULL; hdb_entry_ex *armor_user = NULL;
@@ -358,101 +365,120 @@ fast_unwrap_request(astgs_request_t r)
krb5_error_code ret; krb5_error_code ret;
krb5_ap_req ap_req; krb5_ap_req ap_req;
KrbFastReq fastreq; KrbFastReq fastreq;
size_t len, size;
krb5_data data;
const PA_DATA *pa; const PA_DATA *pa;
krb5_data data;
size_t len;
int i = 0; int i = 0;
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); 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; return 0;
}
ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data,
pa->padata_value.length, pa->padata_value.length,
&fxreq, &fxreq,
&len); &len);
if (ret) if (ret) {
goto out; kdc_log(r->context, r->config, 4,
if (len != pa->padata_value.length) { "Failed to decode PA-FX-FAST-REQUEST: %d", ret);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out; goto out;
} }
if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) {
kdc_log(r->context, r->config, 2, kdc_log(r->context, r->config, 4,
"AS-REQ FAST contain unknown type: %d", (int)fxreq.element); "PA-FX-FAST-REQUEST contains unknown type: %d",
(int)fxreq.element);
ret = KRB5KDC_ERR_PREAUTH_FAILED; ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out; goto out;
} }
/* pull out armor key */ /*
if (fxreq.u.armored_data.armor == NULL) { * If check for armor data or it's not a TGS-REQ with implicit
kdc_log(r->context, r->config, 2, * armor.
*/
if (fxreq.u.armored_data.armor == NULL && tgs_ac == NULL) {
kdc_log(r->context, r->config, 4,
"AS-REQ armor missing"); "AS-REQ armor missing");
ret = KRB5KDC_ERR_PREAUTH_FAILED; ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out; goto out;
} }
if (fxreq.u.armored_data.armor->armor_type != 1) { /*
kdc_log(r->context, r->config, 2, *
"AS-REQ armor type not ap-req"); */
ret = KRB5KDC_ERR_PREAUTH_FAILED; if (tgs_ac == NULL) {
goto out; if (fxreq.u.armored_data.armor->armor_type != 1) {
} kdc_log(r->context, r->config, 4,
"Incorrect AS-REQ armor type");
ret = krb5_decode_ap_req(r->context, ret = KRB5KDC_ERR_PREAUTH_FAILED;
&fxreq.u.armored_data.armor->armor_value, goto out;
&ap_req); }
if(ret) {
kdc_log(r->context, r->config, 2, "AP-REQ decode failed");
goto out;
}
/* Save that principal that was in the request */ ret = krb5_decode_ap_req(r->context,
ret = _krb5_principalname2krb5_principal(r->context, &fxreq.u.armored_data.armor->armor_value,
&armor_server, &ap_req);
ap_req.ticket.sname, if(ret) {
ap_req.ticket.realm); kdc_log(r->context, r->config, 4, "Failed to decode AP-REQ");
if (ret) { goto out;
free_AP_REQ(&ap_req); }
goto out;
}
ret = _kdc_db_fetch(r->context, r->config, armor_server, /* Save that principal that was in the request */
HDB_F_GET_KRBTGT ret = _krb5_principalname2krb5_principal(r->context,
| HDB_F_DELAY_NEW_KEYS, &armor_server,
NULL, NULL, &armor_user); ap_req.ticket.sname,
if(ret == HDB_ERR_NOT_FOUND_HERE) { ap_req.ticket.realm);
kdc_log(r->context, r->config, 5, if (ret) {
"armor key does not have secrets at this KDC, " free_AP_REQ(&ap_req);
"need to proxy"); goto out;
free_AP_REQ(&ap_req); }
goto out;
} else if (ret) {
free_AP_REQ(&ap_req);
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
ret = hdb_enctype2key(r->context, &armor_user->entry, NULL, ret = _kdc_db_fetch(r->context, r->config, armor_server,
ap_req.ticket.enc_part.etype, HDB_F_GET_KRBTGT | HDB_F_DELAY_NEW_KEYS,
&armor_key); NULL, NULL, &armor_user);
if (ret) { if(ret == HDB_ERR_NOT_FOUND_HERE) {
free_AP_REQ(&ap_req); kdc_log(r->context, r->config, 5,
goto out; "Armor key does not have secrets at this KDC, "
} "need to proxy");
goto out;
} else if (ret) {
free_AP_REQ(&ap_req);
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
ret = krb5_verify_ap_req2(r->context, &ac, ret = hdb_enctype2key(r->context, &armor_user->entry, NULL,
&ap_req, ap_req.ticket.enc_part.etype,
armor_server, &armor_key);
&armor_key->key, if (ret) {
0, free_AP_REQ(&ap_req);
&ap_req_options, goto out;
&ticket, }
KRB5_KU_AP_REQ_AUTH);
free_AP_REQ(&ap_req); ret = krb5_verify_ap_req2(r->context, &ac,
if (ret) &ap_req,
goto out; armor_server,
&armor_key->key,
0,
&ap_req_options,
&ticket,
KRB5_KU_AP_REQ_AUTH);
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) { if (ac->remote_subkey == NULL) {
krb5_auth_con_free(r->context, ac); krb5_auth_con_free(r->context, ac);
@@ -460,31 +486,21 @@ fast_unwrap_request(astgs_request_t r)
"FAST AP-REQ remote subkey missing"); "FAST AP-REQ remote subkey missing");
ret = KRB5KDC_ERR_PREAUTH_FAILED; ret = KRB5KDC_ERR_PREAUTH_FAILED;
goto out; goto out;
} }
r->fast.flags.kdc_verified =
!_kdc_is_anonymous_pkinit(r->context, ticket->client);
ret = _krb5_fast_armor_key(r->context, ret = _krb5_fast_armor_key(r->context,
ac->remote_subkey, ac->remote_subkey,
&ticket->ticket.key, &ticket->ticket.key,
&armorkey, &armorkey,
&r->armor_crypto); &r->armor_crypto);
krb5_auth_con_free(r->context, ac);
krb5_free_ticket(r->context, ticket);
if (ret) if (ret)
goto out; goto out;
krb5_free_keyblock_contents(r->context, &armorkey); 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, ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto,
KRB5_KU_FAST_ENC, KRB5_KU_FAST_ENC,
&fxreq.u.armored_data.enc_fast_req, &fxreq.u.armored_data.enc_fast_req,
@@ -495,24 +511,57 @@ fast_unwrap_request(astgs_request_t r)
goto out; goto out;
} }
ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); ret = decode_KrbFastReq(data.data, data.length, &fastreq, NULL);
if (ret) {
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); 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) if (ret)
goto out; 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) { if (FastOptions2int(fastreq.fast_options) & 0xfffc) {
kdc_log(r->context, r->config, 2, kdc_log(r->context, r->config, 2,
"FAST unsupported mandatory option set"); "FAST unsupported mandatory option set");
@@ -520,6 +569,8 @@ fast_unwrap_request(astgs_request_t r)
goto out; 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 */ /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */
if (r->req.padata) if (r->req.padata)
free_METHOD_DATA(r->req.padata); free_METHOD_DATA(r->req.padata);
@@ -530,10 +581,22 @@ fast_unwrap_request(astgs_request_t r)
if (ret) if (ret)
goto out; 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_KrbFastReq(&fastreq);
free_PA_FX_FAST_REQUEST(&fxreq); free_PA_FX_FAST_REQUEST(&fxreq);
kdc_log(r->context, r->config, 5, "Client selected FAST");
out: 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) if (armor_server)
krb5_free_principal(r->context, armor_server); krb5_free_principal(r->context, armor_server);
if(armor_user) if(armor_user)
@@ -542,30 +605,70 @@ fast_unwrap_request(astgs_request_t r)
return ret; return ret;
} }
/*
*
*/
krb5_error_code 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; krb5_error_code ret;
const PA_DATA *pa; const PA_DATA *pa;
int i = 0; int i = 0;
ret = fast_unwrap_request(r); ret = fast_unwrap_request(r, tgs_ticket, tgs_ac);
if (ret) if (ret)
return 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); pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
if (pa) { if (pa)
ret = fast_parse_cookie(r, 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) if (ret)
return ret; return ret;
krb5_free_keyblock_contents(r->context, &r->reply_key);
r->reply_key = new_reply_key;
} }
return 0; return 0;
} }
/*
* Zero and free KDCFastState
*/
void void
_kdc_free_fast_state(KDCFastState *state) _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); ret = krb5_init_creds_set_fast_ccache(kdc_context, ctx, fast_cc);
if (ret) if (ret)
krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_fast_ccache"); krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_fast_ccache");
fast_cc = NULL;
} }
if (password) { if (password) {

View File

@@ -93,6 +93,13 @@ struct astgs_request_desc {
krb5_timestamp pa_endtime; krb5_timestamp pa_endtime;
krb5_timestamp pa_max_life; 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; krb5_crypto armor_crypto;
KDCFastState fast; KDCFastState fast;

View File

@@ -555,62 +555,10 @@ pa_gss_validate(astgs_request_t r, const PA_DATA *pa)
return ret; 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 static krb5_error_code
pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa) 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; int invalidPassword = 0;
EncryptedData enc_data; EncryptedData enc_data;
krb5_enctype aenctype; krb5_enctype aenctype;
@@ -652,39 +600,46 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
krb5_crypto_getenctype(r->context, r->armor_crypto, &aenctype); 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++) { for (i = 0; i < r->client->entry.keys.len; i++) {
krb5_crypto challangecrypto, longtermcrypto; krb5_crypto challengecrypto, longtermcrypto;
krb5_keyblock challangekey; krb5_keyblock challengekey;
PA_ENC_TS_ENC p;
k = &r->client->entry.keys.val[i]; k = &r->client->entry.keys.val[i];
ret = krb5_crypto_init(r->context, &k->key, 0, &longtermcrypto); ret = krb5_crypto_init(r->context, &k->key, 0, &longtermcrypto);
if (ret) if (ret)
continue; continue;
ret = krb5_crypto_fx_cf2(r->context, r->armor_crypto, longtermcrypto, ret = krb5_crypto_fx_cf2(r->context, r->armor_crypto, longtermcrypto,
&pepper1, &pepper2, aenctype, &pepper1, &pepper2, aenctype,
&challangekey); &challengekey);
krb5_crypto_destroy(r->context, longtermcrypto); if (ret) {
if (ret) krb5_crypto_destroy(r->context, longtermcrypto);
continue; continue;
}
ret = krb5_crypto_init(r->context, &challangekey, 0, ret = krb5_crypto_init(r->context, &challengekey, 0,
&challangecrypto); &challengecrypto);
if (ret) krb5_free_keyblock_contents(r->context, &challengekey);
if (ret) {
krb5_crypto_destroy(r->context, longtermcrypto);
continue; continue;
}
ret = krb5_decrypt_EncryptedData(r->context, challangecrypto,
KRB5_KU_ENC_CHALLENGE_CLIENT, ret = _krb5_validate_pa_enc_challenge(r->context,
&enc_data, challengecrypto,
&ts_data); KRB5_KU_ENC_CHALLENGE_CLIENT,
&enc_data,
r->cname);
krb5_crypto_destroy(r->context, challengecrypto);
if (ret) { if (ret) {
const char *msg = krb5_get_error_message(r->context, ret); const char *msg = krb5_get_error_message(r->context, ret);
krb5_error_code ret2; krb5_error_code ret2;
char *str = NULL; char *str = NULL;
invalidPassword = 1; invalidPassword = (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY);
ret2 = krb5_enctype_to_string(r->context, k->key.keytype, &str); ret2 = krb5_enctype_to_string(r->context, k->key.keytype, &str);
if (ret2) if (ret2)
@@ -695,68 +650,66 @@ pa_enc_chal_validate(astgs_request_t r, const PA_DATA *pa)
krb5_free_error_message(r->context, msg); krb5_free_error_message(r->context, msg);
free(str); free(str);
krb5_crypto_destroy(r->context, longtermcrypto);
continue; continue;
} }
ret = decode_PA_ENC_TS_ENC(ts_data.data, /*
ts_data.length, * Found a key that the client used, lets pick that as the reply key
&p, */
&size);
krb5_data_free(&ts_data);
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) { krb5_free_keyblock_contents(r->context, &r->reply_key);
char client_time[100]; ret = krb5_copy_keyblock_contents(r->context, &k->key, &r->reply_key);
if (ret) {
krb5_crypto_destroy(r->context, challangecrypto); krb5_crypto_destroy(r->context, longtermcrypto);
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);
goto out; 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) if (ret)
goto out; goto out;
set_salt_padata(&r->outpadata, k->salt); 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 * Success
*/ */
if (r->clientdb->hdb_auth_status) if (r->clientdb->hdb_auth_status)
r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client, r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client,
HDB_AUTH_SUCCESS); HDB_AUTH_SUCCESS);
goto out; goto out;
} }
ret = KRB5KDC_ERR_PREAUTH_FAILED;
if (invalidPassword && r->clientdb->hdb_auth_status) { if (invalidPassword && r->clientdb->hdb_auth_status) {
r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client, r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client,
HDB_AUTH_WRONG_PASSWORD); HDB_AUTH_WRONG_PASSWORD);
ret = KRB5KDC_ERR_PREAUTH_FAILED;
} }
out: out:
free_EncryptedData(&enc_data); free_EncryptedData(&enc_data);
@@ -1016,11 +969,11 @@ log_patypes(astgs_request_t r, METHOD_DATA *padata)
krb5_error_code krb5_error_code
_kdc_encode_reply(krb5_context context, _kdc_encode_reply(krb5_context context,
krb5_kdc_configuration *config, 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, KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek,
krb5_enctype etype, krb5_enctype etype,
int skvno, const EncryptionKey *skey, int skvno, const EncryptionKey *skey,
int ckvno, const EncryptionKey *reply_key, int ckvno,
int rk_is_subkey, int rk_is_subkey,
const char **e_text, const char **e_text,
krb5_data *reply) krb5_data *reply)
@@ -1066,10 +1019,9 @@ _kdc_encode_reply(krb5_context context,
return ret; return ret;
} }
if (armor_crypto) { if (r && r->armor_crypto) {
krb5_data data;
krb5_keyblock *strengthen_key = NULL;
KrbFastFinished finished; KrbFastFinished finished;
krb5_data data;
kdc_log(context, config, 4, "FAST armor protection"); kdc_log(context, config, 4, "FAST armor protection");
@@ -1088,7 +1040,7 @@ _kdc_encode_reply(krb5_context context,
if (data.length != len) if (data.length != len)
krb5_abortx(context, "internal asn.1 error"); 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, KRB5_KU_FAST_FINISHED, 0,
data.data, data.length, data.data, data.length,
&finished.ticket_checksum); &finished.ticket_checksum);
@@ -1096,8 +1048,8 @@ _kdc_encode_reply(krb5_context context,
if (ret) if (ret)
return ret; return ret;
ret = _kdc_fast_mk_response(context, armor_crypto, ret = _kdc_fast_mk_response(context, r->armor_crypto,
rep->padata, strengthen_key, &finished, rep->padata, &r->strengthen_key, &finished,
nonce, &data); nonce, &data);
free_Checksum(&finished.ticket_checksum); free_Checksum(&finished.ticket_checksum);
if (ret) if (ret)
@@ -1120,9 +1072,9 @@ _kdc_encode_reply(krb5_context context,
return ret; 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; Realm anon_realm = KRB5_ANON_REALM;
free_Realm(&rep->crealm); free_Realm(&rep->crealm);
@@ -1152,7 +1104,7 @@ _kdc_encode_reply(krb5_context context,
*e_text = "KDC internal error"; *e_text = "KDC internal error";
return KRB5KRB_ERR_GENERIC; 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) { if (ret) {
const char *msg = krb5_get_error_message(context, ret); const char *msg = krb5_get_error_message(context, ret);
free(buf); free(buf);
@@ -1988,7 +1940,6 @@ _kdc_as_rep(astgs_request_t r)
Key *skey; Key *skey;
int found_pa = 0; int found_pa = 0;
int i, flags = HDB_F_FOR_AS_REQ; int i, flags = HDB_F_FOR_AS_REQ;
METHOD_DATA error_method;
const PA_DATA *pa; const PA_DATA *pa;
krb5_boolean is_tgs; krb5_boolean is_tgs;
const char *msg; const char *msg;
@@ -1996,13 +1947,11 @@ _kdc_as_rep(astgs_request_t r)
Key *krbtgt_key; Key *krbtgt_key;
memset(&rep, 0, sizeof(rep)); memset(&rep, 0, sizeof(rep));
error_method.len = 0;
error_method.val = NULL;
/* /*
* Look for FAST armor and unwrap * Look for FAST armor and unwrap
*/ */
ret = _kdc_fast_unwrap_request(r); ret = _kdc_fast_unwrap_request(r, NULL, NULL);
if (ret) { if (ret) {
_kdc_r_log(r, 1, "FAST unwrap request from %s failed: %d", from, ret); _kdc_r_log(r, 1, "FAST unwrap request from %s failed: %d", from, ret);
goto out; goto out;
@@ -2081,10 +2030,10 @@ _kdc_as_rep(astgs_request_t r)
r->cname, fixed_client_name); r->cname, fixed_client_name);
free(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, &req->req_body, KRB5_KDC_ERR_WRONG_REALM,
NULL, r->server_princ, NULL, NULL,
&r->client->entry.principal->realm, r->client->entry.principal, r->server_princ,
NULL, NULL, r->reply); NULL, NULL, r->reply);
goto out; goto out;
} }
@@ -2174,7 +2123,7 @@ _kdc_as_rep(astgs_request_t r)
NULL, &ckey, &default_salt); NULL, &ckey, &default_salt);
if (ret2 == 0) { if (ret2 == 0) {
ret2 = get_pa_etype_info_both(context, config, &b->etype, ret2 = get_pa_etype_info_both(context, config, &b->etype,
&error_method, ckey, !default_salt); &r->outpadata, ckey, !default_salt);
if (ret2 != 0) if (ret2 != 0)
ret = ret2; ret = ret2;
} }
@@ -2203,7 +2152,7 @@ _kdc_as_rep(astgs_request_t r)
for (n = 0; n < sizeof(pat) / sizeof(pat[0]); n++) { for (n = 0; n < sizeof(pat) / sizeof(pat[0]); n++) {
if ((pat[n].flags & PA_ANNOUNCE) == 0) if ((pat[n].flags & PA_ANNOUNCE) == 0)
continue; continue;
ret = krb5_padata_add(context, &error_method, ret = krb5_padata_add(context, &r->outpadata,
pat[n].type, NULL, 0); pat[n].type, NULL, 0);
if (ret) if (ret)
goto out; goto out;
@@ -2217,7 +2166,7 @@ _kdc_as_rep(astgs_request_t r)
NULL, &ckey, &default_salt); NULL, &ckey, &default_salt);
if (ret == 0) { if (ret == 0) {
ret = get_pa_etype_info_both(context, config, &b->etype, ret = get_pa_etype_info_both(context, config, &b->etype,
&error_method, ckey, !default_salt); &r->outpadata, ckey, !default_salt);
if (ret) if (ret)
goto out; goto out;
} }
@@ -2253,7 +2202,7 @@ _kdc_as_rep(astgs_request_t r)
* with in a preauth mech. * with in a preauth mech.
*/ */
ret = _kdc_check_access(r, req, &error_method); ret = _kdc_check_access(r, req, &r->outpadata);
if(ret) if(ret)
goto out; 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; 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 * 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, ret = _kdc_encode_reply(context, config,
r->armor_crypto, req->req_body.nonce, r, req->req_body.nonce,
&rep, &r->et, &r->ek, setype, &rep, &r->et, &r->ek, setype,
r->server->entry.kvno, &skey->key, r->server->entry.kvno, &skey->key,
r->client->entry.kvno, r->client->entry.kvno,
&r->reply_key, 0, &r->e_text, r->reply); 0, &r->e_text, r->reply);
if (ret) if (ret)
goto out; goto out;
@@ -2624,16 +2581,12 @@ out:
*/ */
if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0) if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0)
ret = _kdc_fast_mk_error(r, ret = _kdc_fast_mk_error(r,
(ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) &r->outpadata,
? &r->outpadata : &error_method,
r->armor_crypto, r->armor_crypto,
&req->req_body, &req->req_body,
ret, r->e_text, ret, r->e_text,
r->client_princ,
r->server_princ, r->server_princ,
r->client_princ ?
&r->client_princ->name : NULL,
r->client_princ ?
&r->client_princ->realm : NULL,
NULL, NULL, NULL, NULL,
r->reply); r->reply);
@@ -2641,8 +2594,6 @@ out:
free_EncKDCRepPart(&r->ek); free_EncKDCRepPart(&r->ek);
_kdc_free_fast_state(&r->fast); _kdc_free_fast_state(&r->fast);
if (error_method.len)
free_METHOD_DATA(&error_method);
if (r->outpadata.len) if (r->outpadata.len)
free_METHOD_DATA(&r->outpadata); free_METHOD_DATA(&r->outpadata);
if (r->client_princ) { 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->reply_key);
krb5_free_keyblock_contents(r->context, &r->session_key); krb5_free_keyblock_contents(r->context, &r->session_key);
krb5_free_keyblock_contents(r->context, &r->strengthen_key);
return ret; return ret;
} }

View File

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

View File

@@ -1077,6 +1077,18 @@ get_new_tickets(krb5_context context,
goto out; 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); krb5_init_creds_free(context, ctx);
ctx = NULL; ctx = NULL;

View File

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

View File

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