Compare commits

..

No commits in common. "c8" and "c8-beta" have entirely different histories.
c8 ... c8-beta

16 changed files with 1 additions and 5555 deletions

View File

@ -1,673 +0,0 @@
From bf3e55bcd66c5d35fddadc94fd680bdd57508bce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 22 Dec 2022 03:05:23 -0500
Subject: [PATCH] Add PAC full checksums
A paper by Tom Tervoort noted that computing the PAC privsvr checksum
over only the server checksum is vulnerable to collision attacks
(CVE-2022-37967). In response, Microsoft has added a second KDC
checksum over the full contents of the PAC. Generate and verify full
KDC checksums in PACs for service tickets. Update the t_pac.c ticket
test case to use a ticket issued by a recent version of Active
Directory (provided by Stefan Metzmacher).
ticket: 9084 (new)
---
doc/appdev/refs/macros/index.rst | 1 +
src/include/krb5/krb5.hin | 1 +
src/lib/krb5/krb/pac.c | 92 +++++++++--------
src/lib/krb5/krb/pac_sign.c | 146 +++++++++++++++-----------
src/lib/krb5/krb/t_pac.c | 171 ++++++++++++++++++-------------
src/tests/t_authdata.py | 5 +-
6 files changed, 242 insertions(+), 174 deletions(-)
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index c6ea088742..22ef2b2f42 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -247,6 +247,7 @@ Public
KRB5_PAC_SERVER_CHECKSUM.rst
KRB5_PAC_TICKET_CHECKSUM.rst
KRB5_PAC_UPN_DNS_INFO.rst
+ KRB5_PAC_FULL_CHECKSUM.rst
KRB5_PADATA_AFS3_SALT.rst
KRB5_PADATA_AP_REQ.rst
KRB5_PADATA_AS_CHECKSUM.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 8e59628bd9..12a1d441b8 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -8187,6 +8187,7 @@ krb5_verify_authdata_kdc_issued(krb5_context context,
#define KRB5_PAC_TICKET_CHECKSUM 16 /**< Ticket checksum */
#define KRB5_PAC_ATTRIBUTES_INFO 17 /**< PAC attributes */
#define KRB5_PAC_REQUESTOR 18 /**< PAC requestor SID */
+#define KRB5_PAC_FULL_CHECKSUM 19 /**< KDC full checksum */
struct krb5_pac_data;
/** PAC data structure to convey authorization information */
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 2f6ad4e1df..9c00178a28 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -500,7 +500,8 @@ zero_signature(krb5_context context, const krb5_pac pac, krb5_ui_4 type,
size_t i;
assert(type == KRB5_PAC_SERVER_CHECKSUM ||
- type == KRB5_PAC_PRIVSVR_CHECKSUM);
+ type == KRB5_PAC_PRIVSVR_CHECKSUM ||
+ type == KRB5_PAC_FULL_CHECKSUM);
assert(data->length >= pac->data.length);
for (i = 0; i < pac->pac->cBuffers; i++) {
@@ -567,17 +568,17 @@ verify_checksum(krb5_context context, const krb5_pac pac, uint32_t buffer_type,
}
static krb5_error_code
-verify_server_checksum(krb5_context context, const krb5_pac pac,
- const krb5_keyblock *server)
+verify_pac_checksums(krb5_context context, const krb5_pac pac,
+ krb5_boolean expect_full_checksum,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr)
{
krb5_error_code ret;
- krb5_data copy; /* PAC with zeroed checksums */
+ krb5_data copy, server_checksum;
+ /* Make a copy of the PAC with zeroed out server and privsvr checksums. */
ret = krb5int_copy_data_contents(context, &pac->data, &copy);
if (ret)
return ret;
-
- /* Zero out both checksum buffers */
ret = zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM, &copy);
if (ret)
goto cleanup;
@@ -585,32 +586,46 @@ verify_server_checksum(krb5_context context, const krb5_pac pac,
if (ret)
goto cleanup;
- ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ if (server != NULL) {
+ /* Verify the server checksum over the PAC copy. */
+ ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ }
-cleanup:
- free(copy.data);
- return ret;
-}
+ if (privsvr != NULL && expect_full_checksum) {
+ /* Zero the full checksum buffer in the copy and verify the full
+ * checksum over the copy with all three checksums zeroed. */
+ ret = zero_signature(context, pac, KRB5_PAC_FULL_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ ret = verify_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ }
-static krb5_error_code
-verify_kdc_checksum(krb5_context context, const krb5_pac pac,
- const krb5_keyblock *privsvr)
-{
- krb5_error_code ret;
- krb5_data server_checksum;
+ if (privsvr != NULL) {
+ /* Verify the privsvr checksum over the server checksum. */
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
+ &server_checksum);
+ if (ret)
+ return ret;
+ if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+ server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
+ server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &server_checksum);
- if (ret)
- return ret;
- if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
- server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
- server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
+ ret = verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
+ if (ret)
+ goto cleanup;
+ }
+
+ pac->verified = TRUE;
- return verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
+cleanup:
+ free(copy.data);
+ return ret;
}
/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
@@ -638,6 +653,7 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
krb5_authdata **authdata, *orig, **ifrel = NULL, **recoded_ifrel = NULL;
uint8_t z = 0;
krb5_authdata zpac = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC, 1, &z };
+ krb5_boolean is_service_tkt;
size_t i, j;
*pac_out = NULL;
@@ -679,7 +695,8 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
if (ret)
goto cleanup;
- if (privsvr != NULL && k5_pac_should_have_ticket_signature(server_princ)) {
+ is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
+ if (privsvr != NULL && is_service_tkt) {
/* To check the PAC ticket signatures, re-encode the ticket with the
* PAC contents replaced by a single zero. */
orig = ifrel[j];
@@ -703,8 +720,9 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
goto cleanup;
}
- ret = krb5_pac_verify_ext(context, pac, enc_tkt->times.authtime, NULL,
- server, privsvr, FALSE);
+ ret = verify_pac_checksums(context, pac, is_service_tkt, server, privsvr);
+ if (ret)
+ goto cleanup;
*pac_out = pac;
pac = NULL;
@@ -740,14 +758,8 @@ krb5_pac_verify_ext(krb5_context context,
{
krb5_error_code ret;
- if (server != NULL) {
- ret = verify_server_checksum(context, pac, server);
- if (ret != 0)
- return ret;
- }
-
- if (privsvr != NULL) {
- ret = verify_kdc_checksum(context, pac, privsvr);
+ if (server != NULL || privsvr != NULL) {
+ ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
if (ret != 0)
return ret;
}
@@ -759,8 +771,6 @@ krb5_pac_verify_ext(krb5_context context,
return ret;
}
- pac->verified = TRUE;
-
return 0;
}
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index 0f9581abbb..8ea61ac17b 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -187,26 +187,41 @@ k5_pac_encode_header(krb5_context context, krb5_pac pac)
return 0;
}
-krb5_error_code KRB5_CALLCONV
-krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal, const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_data *data)
+/* Find the buffer of type buftype in pac and write within it a checksum of
+ * type cksumtype over data. Set *cksum_out to the checksum. */
+static krb5_error_code
+compute_pac_checksum(krb5_context context, krb5_pac pac, uint32_t buftype,
+ const krb5_keyblock *key, krb5_cksumtype cksumtype,
+ const krb5_data *data, krb5_data *cksum_out)
{
- return krb5_pac_sign_ext(context, pac, authtime, principal, server_key,
- privsvr_key, FALSE, data);
+ krb5_error_code ret;
+ krb5_data buf;
+ krb5_crypto_iov iov[2];
+
+ ret = k5_pac_locate_buffer(context, pac, buftype, &buf);
+ if (ret)
+ return ret;
+
+ assert(buf.length > PAC_SIGNATURE_DATA_LENGTH);
+ *cksum_out = make_data(buf.data + PAC_SIGNATURE_DATA_LENGTH,
+ buf.length - PAC_SIGNATURE_DATA_LENGTH);
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = *data;
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data = *cksum_out;
+ return krb5_c_make_checksum_iov(context, cksumtype, key,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, iov, 2);
}
-krb5_error_code KRB5_CALLCONV
-krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal,
- const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
- krb5_data *data)
+static krb5_error_code
+sign_pac(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal, const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_boolean is_service_tkt, krb5_data *data)
{
krb5_error_code ret;
- krb5_data server_cksum, privsvr_cksum;
+ krb5_data full_cksum, server_cksum, privsvr_cksum;
krb5_cksumtype server_cksumtype, privsvr_cksumtype;
- krb5_crypto_iov iov[2];
data->length = 0;
data->data = NULL;
@@ -214,67 +229,53 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
if (principal != NULL) {
ret = k5_insert_client_info(context, pac, authtime, principal,
with_realm);
- if (ret != 0)
+ if (ret)
return ret;
}
- /* Create zeroed buffers for both checksums */
+ /* Create zeroed buffers for all checksums. */
ret = k5_insert_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM,
server_key, &server_cksumtype);
- if (ret != 0)
+ if (ret)
return ret;
-
ret = k5_insert_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
privsvr_key, &privsvr_cksumtype);
- if (ret != 0)
+ if (ret)
return ret;
+ if (is_service_tkt) {
+ ret = k5_insert_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM,
+ privsvr_key, &privsvr_cksumtype);
+ if (ret)
+ return ret;
+ }
- /* Now, encode the PAC header so that the checksums will include it */
+ /* Encode the PAC header so that the checksums will include it. */
ret = k5_pac_encode_header(context, pac);
- if (ret != 0)
- return ret;
-
- /* Generate the server checksum over the entire PAC */
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &server_cksum);
- if (ret != 0)
+ if (ret)
return ret;
- assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
-
- iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[0].data = pac->data;
-
- iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
- iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
+ if (is_service_tkt) {
+ /* Generate a full KDC checksum over the whole PAC. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_FULL_CHECKSUM,
+ privsvr_key, privsvr_cksumtype,
+ &pac->data, &full_cksum);
+ if (ret)
+ return ret;
+ }
- ret = krb5_c_make_checksum_iov(context, server_cksumtype,
- server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
- iov, sizeof(iov)/sizeof(iov[0]));
- if (ret != 0)
+ /* Generate the server checksum over the whole PAC, including the full KDC
+ * checksum if we added one. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM,
+ server_key, server_cksumtype, &pac->data,
+ &server_cksum);
+ if (ret)
return ret;
- /* Generate the privsvr checksum over the server checksum buffer */
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
+ /* Generate the privsvr checksum over the server checksum buffer. */
+ ret = compute_pac_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
+ privsvr_key, privsvr_cksumtype, &server_cksum,
&privsvr_cksum);
- if (ret != 0)
- return ret;
-
- assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
-
- iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
-
- iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
- iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
- iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
-
- ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
- privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
- iov, sizeof(iov)/sizeof(iov[0]));
- if (ret != 0)
+ if (ret)
return ret;
data->data = k5memdup(pac->data.data, pac->data.length, &ret);
@@ -288,6 +289,26 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
return 0;
}
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal, const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_data *data)
+{
+ return sign_pac(context, pac, authtime, principal, server_key,
+ privsvr_key, FALSE, FALSE, data);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_data *data)
+{
+ return sign_pac(context, pac, authtime, principal, server_key, privsvr_key,
+ with_realm, FALSE, data);
+}
+
/* Add a signature over der_enc_tkt in privsvr to pac. der_enc_tkt should be
* encoded with a dummy PAC authdata element containing a single zero byte. */
static krb5_error_code
@@ -359,6 +380,7 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
krb5_error_code ret;
krb5_data *der_enc_tkt = NULL, pac_data = empty_data();
krb5_authdata **list, *pac_ad;
+ krb5_boolean is_service_tkt;
size_t count;
/* Reallocate space for another authdata element in enc_tkt. */
@@ -377,7 +399,8 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
memmove(list + 1, list, (count + 1) * sizeof(*list));
list[0] = pac_ad;
- if (k5_pac_should_have_ticket_signature(server_princ)) {
+ is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
+ if (is_service_tkt) {
ret = encode_krb5_enc_tkt_part(enc_tkt, &der_enc_tkt);
if (ret)
goto cleanup;
@@ -388,9 +411,8 @@ krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
goto cleanup;
}
- ret = krb5_pac_sign_ext(context, pac, enc_tkt->times.authtime,
- client_princ, server, privsvr, with_realm,
- &pac_data);
+ ret = sign_pac(context, pac, enc_tkt->times.authtime, client_princ, server,
+ privsvr, with_realm, is_service_tkt, &pac_data);
if (ret)
goto cleanup;
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index 173bde7bab..81f1642ab0 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -607,78 +607,102 @@ check_pac(krb5_context context, int index, const unsigned char *pdata,
static const krb5_keyblock ticket_sig_krbtgt_key = {
0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- 32, U("\x7a\x58\x98\xd2\xaf\xa6\xaf\xc0\x6a\xce\x06\x04\x4b\xc2\x70\x84"
- "\x9b\x8e\x0a\x6c\x4c\x07\xdc\x6f\xbb\x48\x43\xe1\xd2\xaa\x97\xf7")
+ 32, U("\x03\x73\x81\xEC\x43\x96\x7B\xC2\xAC\x3D\xF5\x2A\xAE\x95\xA6\x8E"
+ "\xBE\x24\x58\xDB\xCE\x52\x28\x20\xAF\x5E\xB7\x04\xA2\x22\x71\x4F")
};
static const krb5_keyblock ticket_sig_server_key = {
- 0, ENCTYPE_ARCFOUR_HMAC,
- 16, U("\xed\x23\x11\x20\x7a\x21\x44\x20\xbf\xc0\x8d\x36\xf7\xf6\xb2\x3e")
+ 0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 32, U("\x11\x4A\x84\xE3\x14\x8F\xAA\xB1\xFA\x7B\x53\x51\xB2\x8A\xC2\xF1"
+ "\xFD\x19\x6D\x61\xE0\xF3\xF2\x3E\x1F\xDB\xD3\xC1\x79\x7D\xC1\xEE")
};
+/* A ticket issued by an Active Directory KDC (Windows Server 2022), containing
+ * a PAC with a full checksum. */
static const krb5_data ticket_data = {
- .length = 972, .data =
- "\x61\x82\x03\xC8\x30\x82\x03\xC4\xA0\x03\x02\x01\x05\xA1\x0A\x1B"
- "\x08\x43\x44\x4F\x4D\x2E\x43\x4F\x4D\xA2\x0F\x30\x0D\xA0\x03\x02"
- "\x01\x01\xA1\x06\x30\x04\x1B\x02\x73\x31\xA3\x82\x03\x9E\x30\x82"
- "\x03\x9A\xA0\x03\x02\x01\x17\xA1\x03\x02\x01\x03\xA2\x82\x03\x8C"
- "\x04\x82\x03\x88\x44\x31\x61\x20\x17\xC9\xFE\xBC\xAC\x46\xB5\x77"
- "\xE9\x68\x04\x4C\x9B\x31\x91\x0C\xC1\xD4\xDD\xEF\xC7\x34\x20\x08"
- "\x90\x91\xE8\x79\xE0\xB5\x03\x26\xA4\x65\xDE\xEC\x47\x03\x2A\x8F"
- "\x61\xE7\x4D\x38\x5A\x42\x95\x5A\xF9\x2F\x41\x2C\x2A\x6E\x60\xA1"
- "\xEB\x51\xB3\xBD\x4C\x00\x41\x2A\x44\x76\x08\x37\x1A\x51\xFD\x65"
- "\x67\x7E\xBF\x3D\x90\x86\xE3\x9A\x54\x6B\x67\xA8\x08\x7A\x73\xCC"
- "\xC3\xB7\x4B\xD5\x5C\x3A\x14\x6C\xC1\x5F\x54\x4B\x92\x55\xB4\xB7"
- "\x92\x23\x3F\x53\x89\x47\x8E\x1F\x8B\xB9\xDB\x3B\x93\xE8\x70\xE4"
- "\x24\xB8\x9D\xF0\x0E\x35\x28\xF8\x7A\x27\x5D\xF7\x25\x97\x9C\xF5"
- "\x9F\x9F\x64\x04\xF2\xA3\xAB\x11\x15\xB6\xDA\x18\xD6\x46\xD5\xE6"
- "\xB8\x08\xDE\x0A\x62\xFD\xF8\xAA\x52\x90\xD9\x67\x29\xB2\xCD\x06"
- "\xB6\xB0\x50\x2B\x3F\x0F\xA3\xA5\xBF\xAA\x6E\x40\x03\xD6\x5F\x02"
- "\xBC\xD8\x18\x47\x97\x09\xD7\xE4\x96\x3B\xCB\xEB\x92\x2C\x3C\x49"
- "\xFF\x1F\x71\xE0\x52\x94\x0F\x8B\x9F\xB8\x2A\xBB\x9C\xE2\xA3\xDD"
- "\x38\x89\xE2\xB1\x0B\x9E\x1F\x7A\xB3\xE3\xD2\xB0\x94\xDC\x87\xBE"
- "\x37\xA6\xD3\xB3\x29\x35\x9A\x72\xC3\x7A\xF1\xA9\xE6\xC5\xD1\x26"
- "\x83\x65\x44\x17\xBA\x55\xA8\x5E\x94\x26\xED\xE9\x8A\x93\x11\x5D"
- "\x7E\x20\x1B\x9C\x15\x9E\x13\x37\x03\x4D\xDD\x99\x51\xD8\x66\x29"
- "\x6A\xB9\xFB\x49\xFE\x52\x78\xDA\x86\x85\xA9\xA3\xB9\xEF\xEC\xAD"
- "\x35\xA6\x8D\xAC\x0F\x75\x22\xBB\x0B\x49\x1C\x13\x52\x40\xC9\x52"
- "\x69\x09\x54\xD1\x0F\x94\x3F\x22\x48\x67\xB0\x96\x28\xAA\xE6\x28"
- "\xD9\x0C\x08\xEF\x51\xED\x15\x5E\xA2\x53\x59\xA5\x03\xB4\x06\x20"
- "\x3D\xCC\xB4\xC5\xF8\x8C\x73\x67\xA3\x21\x3D\x19\xCD\xD4\x12\x28"
- "\xD2\x93\xDE\x0D\xF0\x71\x10\x50\xD6\x33\x35\x04\x11\x64\x43\x39"
- "\xC3\xDF\x96\xE3\x66\xE3\x85\xCA\xE7\x67\x14\x3A\xF0\x43\xAA\xBB"
- "\xD4\x1D\xB5\x24\xB5\x74\x90\x25\xA7\x87\x7E\xDB\xD3\x83\x8A\x3A"
- "\x69\xA8\x2D\xAF\xB7\xB8\xF3\xDC\x13\xAF\x45\x61\x3F\x59\x39\x7E"
- "\x69\xDE\x0C\x04\xF1\x10\x6B\xB4\x56\xFA\x21\x9F\x72\x2B\x60\x86"
- "\xE3\x23\x0E\xC4\x51\xF6\xBE\xD8\xE1\x5F\xEE\x73\x4C\x17\x4C\x2C"
- "\x1B\xFB\x9F\x1F\x7A\x3B\x07\x5B\x8E\xF1\x01\xAC\xD6\x30\x94\x8A"
- "\x5D\x22\x6F\x08\xCE\xED\x5E\xB6\xDB\x86\x8C\x87\xEB\x8D\x91\xFF"
- "\x0A\x86\x30\xBD\xC0\xF8\x25\xE7\xAE\x24\x35\xF2\xFC\xE5\xFD\x1B"
- "\xB0\x05\x4A\xA3\xE5\xEB\x2E\x05\xAD\x99\x67\x49\x87\xE6\xB3\x87"
- "\x82\xA4\x59\xA7\x6E\xDD\xF2\xB6\x66\xE8\xF7\x70\xF5\xBD\xC9\x0E"
- "\xFA\x9C\x79\x84\xD4\x9B\x05\x0E\xBB\xF5\xDB\xEF\xFC\xCC\x26\xF2"
- "\x93\xCF\xD2\x04\x3C\xA9\x2C\x65\x42\x97\x86\xD8\x38\x0A\x1E\xF6"
- "\xD6\xCA\x30\xB5\x1A\xEC\xFB\xBA\x3B\x84\x57\xB0\xFD\xFB\xE6\xBC"
- "\xF2\x76\xF6\x4C\xBB\xAB\xB1\x31\xA1\x27\x7C\xE6\xE6\x81\xB6\xCE"
- "\x84\x86\x40\xB6\x40\x33\xC4\xF8\xB4\x15\xCF\xAA\xA5\x51\x78\xB9"
- "\x8B\x50\x25\xB2\x88\x86\x96\x72\x8C\x71\x4D\xB5\x3A\x94\x86\x77"
- "\x0E\x95\x9B\x16\x93\xEF\x3A\x11\x79\xBA\x83\xF7\x74\xD3\x8D\xBA"
- "\x15\xE1\x2C\x04\x57\xA8\x92\x1E\x9D\x00\x8E\x20\xFD\x30\x70\xE7"
- "\xF5\x65\x2F\x19\x0C\x94\xBA\x03\x71\x12\x96\xCD\xC8\xB4\x96\xDB"
- "\xCE\x19\xC2\xDF\x3C\xC2\xF6\x3D\x53\xED\x98\xA5\x41\x72\x2A\x22"
- "\x7B\xF3\x2B\x17\x6C\xE1\x39\x7D\xAE\x9B\x11\xF9\xC1\xA6\x9E\x9F"
- "\x89\x3C\x12\xAA\x94\x74\xA7\x4F\x70\xE8\xB9\xDE\x04\xF0\x9D\x39"
- "\x24\x2D\x92\xE8\x46\x2D\x2E\xF0\x40\x66\x1A\xD9\x27\xF9\x98\xF1"
- "\x81\x1D\x70\x62\x63\x30\x6D\xCD\x84\x04\x5F\xFA\x83\xD3\xEC\x8D"
- "\x86\xFB\x40\x61\xC1\x8A\x45\xFF\x7B\xD9\xD4\x18\x61\x7F\x51\xE3"
- "\xFC\x1E\x18\xF0\xAF\xC6\x18\x2C\xE1\x6D\x5D\xF9\x62\xFC\x20\xA3"
- "\xB2\x8A\x5F\xE5\xBB\x29\x0F\x99\x63\x07\x88\x38\x3A\x3B\x73\x2A"
- "\x6D\xDA\x3D\xA8\x0D\x8F\x56\x41\x89\x82\xE5\xB8\x61\x00\x64\x7D"
- "\x17\x0C\xCE\x03\x55\x8F\xF4\x5B\x0D\x50\xF2\xEB\x05\x67\xBE\xDB"
- "\x7B\x75\xC5\xEA\xA1\xAB\x1D\xB0\x3C\x6D\x42\x08\x0B\x9A\x45\x20"
- "\xA8\x8F\xE5\x67\x47\x30\xDE\x93\x5F\x43\x05\xEB\xA8\x2D\x80\xF5"
- "\x1A\xB8\x4A\x4E\x42\x2D\x0B\x7A\xDC\x46\x20\x2D\x13\x17\xDD\x4B"
- "\x94\x96\xAA\x1F\x06\x0C\x1F\x62\x07\x9C\x40\xA1"
+ .length = 1307, .data =
+ "\x61\x82\x05\x17\x30\x82\x05\x13\xA0\x03\x02\x01\x05\xA1\x0F\x1B"
+ "\x0D\x57\x32\x30\x32\x32\x2D\x4C\x37\x2E\x42\x41\x53\x45\xA2\x2A"
+ "\x30\x28\xA0\x03\x02\x01\x01\xA1\x21\x30\x1F\x1B\x04\x63\x69\x66"
+ "\x73\x1B\x17\x77\x32\x30\x32\x32\x2D\x31\x31\x38\x2E\x77\x32\x30"
+ "\x32\x32\x2D\x6C\x37\x2E\x62\x61\x73\x65\xA3\x82\x04\xCD\x30\x82"
+ "\x04\xC9\xA0\x03\x02\x01\x12\xA1\x03\x02\x01\x05\xA2\x82\x04\xBB"
+ "\x04\x82\x04\xB7\x44\x5C\x7B\x5A\x3F\x2E\xA3\x50\x34\xDE\xB0\x69"
+ "\x23\x2D\x47\x89\x2C\xC0\xA3\xF9\xDD\x70\xAA\xA5\x1E\xFE\x74\xE5"
+ "\x19\xA2\x4F\x65\x6C\x9E\x00\xB4\x60\x00\x7C\x0C\x29\x43\x31\x99"
+ "\x77\x02\x73\xED\xB9\x40\xF5\xD2\xD1\xC9\x20\x0F\xE3\x38\xF9\xCC"
+ "\x5E\x2A\xBD\x1F\x91\x66\x1A\xD8\x2A\x80\x3C\x2C\x00\x3C\x1E\xC9"
+ "\x2A\x29\x19\x19\x96\x18\x54\x03\x97\x8F\x1D\x5F\xDB\xE9\x66\x68"
+ "\xCD\xB1\xD5\x00\x35\x69\x49\x45\xF1\x6A\x78\x7B\x37\x71\x87\x14"
+ "\x1C\x98\x4D\x69\xCB\x1B\xD8\xF5\xA3\xD8\x53\x4A\x75\x76\x62\xBA"
+ "\x6C\x3F\xEA\x8B\x97\x21\xCA\x8A\x46\x4B\x38\xDA\x09\x9F\x5A\xC8"
+ "\x38\xFF\x34\x97\x5B\xA2\xE5\xBA\xC9\x87\x17\xD8\x08\x05\x7A\x83"
+ "\x04\xD6\x02\x8E\x9B\x18\xB6\x40\x1A\xF7\x47\x25\x24\x3E\x37\x1E"
+ "\xF6\xC1\x3A\x1F\xCA\xB3\x43\x5A\xAE\x94\x83\x31\xAF\xFB\xEE\xED"
+ "\x46\x71\xEF\xE2\x37\x37\x15\xFE\x1B\x0B\x9E\xF8\x3E\x0C\x43\x96"
+ "\xB6\x0A\x04\x78\xF8\x5E\xAA\x33\x1F\xE2\x07\x5A\x8D\xC4\x4E\x32"
+ "\x6D\xD6\xA0\xC5\xEA\x3D\x12\x59\xD4\x41\x40\x4E\xA1\xD8\xBE\xED"
+ "\x17\xCB\x68\xCC\x59\xCB\x53\xB2\x0E\x58\x8A\xA9\x33\x7F\x6F\x2B"
+ "\x37\x89\x08\x44\xBA\xC7\x67\x17\xBB\x91\xF7\xC3\x0F\x00\xF8\xAA"
+ "\xA1\x33\xA6\x08\x47\xCA\xFA\xE8\x49\x27\x45\x46\xF1\xC1\xC3\x5F"
+ "\xE2\x45\x0A\x7D\x64\x52\x8C\x2E\xE1\xDE\xFF\xB2\x64\xEC\x69\x98"
+ "\x15\xDF\x9E\xB1\xEB\xD6\x9D\x08\x06\x4E\x73\xC1\x0B\x71\x21\x05"
+ "\x9E\xBC\xA2\x17\xCF\xB3\x70\xF4\xEF\xB8\x69\xA9\x94\x27\xFD\x5E"
+ "\x72\xB1\x2D\xD2\x20\x1B\x57\x80\xAB\x38\x97\xCF\x22\x68\x4F\xB8"
+ "\xB7\x17\x53\x25\x67\x0B\xED\xD1\x58\x20\x0D\x45\xF9\x09\xFA\xE7"
+ "\x61\x3E\xDB\xC2\x59\x7B\x3A\x3B\x59\x81\x51\xAA\xA4\x81\xF4\x96"
+ "\x3B\xE1\x6F\x6F\xF4\x8E\x68\x9E\xBA\x1E\x0F\xF2\x44\x68\x11\xFC"
+ "\x2B\x5F\xBE\xF2\xEA\x07\x80\xB9\xCA\x9E\x41\xBD\x2F\x81\xF5\x11"
+ "\x2A\x12\xF3\x4F\xD6\x12\x16\x0F\x21\x90\xF1\xD3\x1E\xF1\xA4\x94"
+ "\x46\xEA\x30\xF3\x84\x06\xC1\xA4\x51\xFC\x43\x35\xBD\xEF\x4D\x89"
+ "\x1D\xA5\x44\xB2\x69\xC4\x0F\xBF\x86\x01\x08\x44\x77\xD5\xB4\xB7"
+ "\x5C\x3F\xA7\xD4\x2F\x39\x73\x85\x88\xEE\xB1\x64\x1D\x80\x6C\xEE"
+ "\x6E\x31\x90\x92\x0D\xA1\xB7\xC4\x5C\xCC\xEE\x91\xC8\xCB\x11\x2D"
+ "\x4A\x1A\x7D\x43\x8F\xEB\x60\x09\xED\x1B\x07\x58\xBE\xBC\xBD\x29"
+ "\xF3\xB3\xA3\x4F\xC5\x8A\x30\x33\xB9\xA9\x9F\x43\x08\x27\x15\xC4"
+ "\x9C\x5D\x8E\xBD\x5C\x05\xC6\x05\x9C\x87\x60\x08\x1E\xE2\x52\xB8"
+ "\x45\x8D\x28\xB6\x2C\x15\x46\x74\x9F\x0E\xAA\x6B\x70\x3A\x2A\x55"
+ "\x45\x26\xB2\x58\x4D\x35\xA6\xF1\x96\xBE\x60\xB2\x71\x7B\xF8\x54"
+ "\xB9\x90\x21\x8E\xB9\x0F\x35\x98\x5E\x88\xEB\x1A\x53\xB4\x59\x7F"
+ "\xAF\x69\x1C\x61\x67\xF4\xF6\xBD\xAC\x24\xCD\xB7\xA9\x67\xE8\xA1"
+ "\x83\x85\x5F\x11\x74\x1F\xF7\x4C\x78\x36\xEF\x50\x74\x88\x58\x4B"
+ "\x1A\x9F\x84\x9A\x9A\x05\x92\xEC\x1D\xD5\xF3\xC4\x95\x51\x28\xE2"
+ "\x3F\x32\x87\xB2\xFD\x21\x27\x66\xE4\x6B\x85\x2F\xDC\x7B\xC0\x22"
+ "\xEB\x7A\x94\x20\x5A\x7B\xD3\x7A\xB9\x5B\xF8\x1A\x5A\x84\x4E\xA1"
+ "\x73\x41\x53\xD2\x60\xF7\x7C\xEE\x68\x59\x85\x80\xFC\x3D\x70\x4B"
+ "\x04\x32\xE7\xF2\xFD\xBD\xB3\xD9\x21\xE2\x37\x56\xA2\x16\xCC\xDE"
+ "\x8A\xD3\xBC\x71\xEF\x58\x19\x0E\x45\x8A\x5B\x53\xD6\x77\x30\x6A"
+ "\xA7\xF8\x68\x06\x4E\x07\xCA\xCE\x30\xD7\x35\xAB\x1A\xC7\x18\xD4"
+ "\xC6\x2F\x1A\xFF\xE9\x7A\x94\x0B\x76\x5E\x7E\x29\x0C\xE6\xD3\x3B"
+ "\x5B\x44\x96\xA8\xF1\x29\x23\x95\xD9\x79\xB3\x39\xFC\x76\xED\xE1"
+ "\x1E\x67\x4E\xF7\xE8\x7B\x7A\x12\x9E\xD8\x4B\x35\x09\x0A\xF2\xC1"
+ "\x63\x5B\xEE\xFD\x2A\xC2\xA6\x66\x30\x3C\x1F\x95\xAF\x65\x22\x95"
+ "\x14\x1D\xF5\xD5\xDC\x38\x79\x35\x1C\xCD\x24\x47\xE0\xFD\x08\xC8"
+ "\xF4\x15\x55\x9F\xD9\xC7\xAC\x3F\x67\xB3\x4F\xEB\x26\x7C\x8E\xD6"
+ "\x74\xB3\x0A\xCD\xE7\xFA\xBE\x7E\xA3\x3E\xEC\x61\x50\x77\x52\x56"
+ "\xCF\x90\x5D\x48\xFB\xD4\x2C\x6C\x61\x8B\xDD\x2B\xF5\x92\x1F\x30"
+ "\xBF\x3F\x80\x0D\x31\xDB\xB2\x0B\x7D\x84\xE3\xA6\x42\x7F\x00\x38"
+ "\x44\x02\xC5\xB8\xD9\x58\x29\x9D\x68\x5C\x32\x8B\x76\xAE\xED\x15"
+ "\xF9\x7C\xAE\x7B\xB6\x8E\xD6\x54\x24\xFF\xFA\x87\x05\xEF\x15\x08"
+ "\x5E\x4B\x21\xA2\x2F\x49\xE7\x0F\xC3\xD0\xB9\x49\x22\xEF\xD5\xCA"
+ "\xB2\x11\xF2\x17\xB6\x77\x24\x68\x76\xB2\x07\xF8\x0A\x73\xDD\x65"
+ "\x9C\x75\x64\xF7\xA1\xC6\x23\x08\x84\x72\x3E\x54\x2E\xEB\x9B\x40"
+ "\xA6\x83\x87\xEB\xB5\x00\x40\x4F\xE1\x72\x2A\x59\x3A\x06\x60\x29"
+ "\x7E\x25\x2F\xD8\x80\x40\x8C\x59\xCA\xCF\x8E\x44\xE4\x2D\x84\x7E"
+ "\xCB\xFD\x1E\x3B\xD5\xFF\x9A\xB9\x66\x93\x6D\x5E\xC8\xB7\x13\x26"
+ "\xD6\x38\x1B\x2B\xE1\x87\x96\x05\xD5\xF3\xAB\x68\xF7\x12\x62\x2C"
+ "\x58\xC1\xC9\x85\x3C\x72\xF1\x26\xEE\xC0\x09\x5F\x1D\x4B\xAC\x01"
+ "\x41\xC8\x12\xF8\xF3\x93\x43\x41\xFF\xEC\x0B\x80\xE2\xEE\x20\x85"
+ "\x25\xCD\x6C\x30\x8C\x0D\x24\x2E\xBA\x19\xEA\x28\x7F\xCF\xD5\x10"
+ "\x5C\xE9\xB2\x9D\x5F\x16\xE4\xC0\xF3\xCC\xD9\x68\x4A\x05\x08\x70"
+ "\x17\x26\xC8\x5C\x4A\xBF\x94\x6A\x0E\xD5\xDA\x67\x47\x4B\xAF\x44"
+ "\xE3\x94\xAA\x05\xDB\xA2\x49\x74\xFA\x5C\x69\xAB\x44\xB7\xF7\xBA"
+ "\xAE\x7A\x23\x87\xEB\x54\x7E\x80\xF1\x5B\x60\xA5\x93\xE5\xD4\x24"
+ "\x84\xF7\x0A\x16\x10\xBE\xE9\x4D\xD8\x6B\x15\x40\x5D\x74\xDA\x1B"
+ "\xFF\x2E\x4D\x17\x9D\x35\xF7\x0D\xCF\x66\x38\x0D\x8A\xE4\xDD\x6B"
+ "\xE1\x0F\x1F\xBD\xFD\x4F\x30\x37\x3F\x96\xB4\x92\x54\xD3\x9A\x7A"
+ "\xD1\x5B\x5B\xA9\x54\x16\xE6\x24\xAB\xD4\x23\x39\x7D\xD2\xC7\x09"
+ "\xFA\xD4\x86\x55\x4D\x60\xC2\x87\x67\x6B\xE6"
};
static void
@@ -686,7 +710,7 @@ test_pac_ticket_signature(krb5_context context)
{
krb5_error_code ret;
krb5_ticket *ticket;
- krb5_principal sprinc;
+ krb5_principal cprinc, sprinc;
krb5_authdata **authdata1, **authdata2;
krb5_pac pac, pac2, pac3;
uint32_t *list;
@@ -701,7 +725,13 @@ test_pac_ticket_signature(krb5_context context)
if (ret)
err(context, ret, "while decrypting ticket");
- ret = krb5_parse_name(context, "s1@CDOM.COM", &sprinc);
+ ret = krb5_parse_name(context, "administrator@W2022-L7.BASE", &cprinc);
+ if (ret)
+ err(context, ret, "krb5_parse_name");
+
+ ret = krb5_parse_name(context,
+ "cifs/w2022-118.w2022-l7.base@W2022-L7.BASE",
+ &sprinc);
if (ret)
err(context, ret, "krb5_parse_name");
@@ -713,7 +743,7 @@ test_pac_ticket_signature(krb5_context context)
/* In this test, the server is also the client. */
ret = krb5_pac_verify(context, pac, ticket->enc_part2->times.authtime,
- ticket->server, NULL, NULL);
+ cprinc, NULL, NULL);
if (ret)
err(context, ret, "while verifying PAC client info");
@@ -722,7 +752,7 @@ test_pac_ticket_signature(krb5_context context)
ticket->enc_part2->authorization_data = NULL;
ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac, sprinc,
- sprinc, &ticket_sig_server_key,
+ cprinc, &ticket_sig_server_key,
&ticket_sig_krbtgt_key, FALSE);
if (ret)
err(context, ret, "while signing ticket");
@@ -781,6 +811,7 @@ test_pac_ticket_signature(krb5_context context)
krb5_pac_free(context, pac);
krb5_pac_free(context, pac2);
krb5_pac_free(context, pac3);
+ krb5_free_principal(context, cprinc);
krb5_free_principal(context, sprinc);
krb5_free_ticket(context, ticket);
}
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index 4fbdbec052..b0666c3b81 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -11,7 +11,7 @@ realm = K5Realm(krb5_conf=conf)
# container.
mark('baseline authdata')
out = realm.run(['./adata', realm.host_princ])
-if '?512: ' not in out or '^-42: Hello' not in out:
+if '?128: [6, 7, 10, 16, 19]' not in out or '^-42: Hello' not in out:
fail('expected authdata not seen for basic request')
# Requested authdata is copied into the ticket, with KDC-only types
@@ -239,6 +239,9 @@ realm.run(['./s4u2proxy', usercache, 'service/2'])
out = realm.run(['./adata', '-p', realm.user_princ, 'service/2'])
if '+97: [indcl]' not in out or '[inds1]' in out:
fail('correct auth-indicator not seen for S4U2Proxy req')
+# Make sure a PAC with an S4U_DELEGATION_INFO(11) buffer is included.
+if '?128: [1, 6, 7, 10, 11, 16, 19]' not in out:
+ fail('PAC with delegation info not seen for S4U2Proxy req')
# Get another S4U2Proxy ticket including request-authdata.
realm.run(['./s4u2proxy', usercache, 'service/2', '-2', 'proxy_ad'])
--
2.39.2

View File

@ -1,840 +0,0 @@
From 48be25aaa27487fcbbba76044083de37211b30e7 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Fri, 7 Jan 2022 13:46:24 -0500
Subject: [PATCH] Add PAC ticket signature APIs
Microsoft added a third PAC signature over the ticket to prevent
servers from setting the forwardable flag on evidence tickets. Add
new APIs to generate and verify ticket signatures, as well as defines
for this and other new PAC buffer types. Deprecate the old signing
functions as they cannot generate ticket signatures. Modify several
error returns to better match the protocol errors generated by Active
Directory.
[ghudson@mit.edu: adjusted contracts for KDC requirements; simplified
and commented code changes; wrote commit message. rharwood@redhat.com
also did some work on this commit.]
ticket: 9043 (new)
---
doc/appdev/refs/api/index.rst | 2 +
doc/appdev/refs/macros/index.rst | 6 +
src/include/krb5/krb5.hin | 98 +++++++++++------
src/lib/krb5/krb/deps | 5 +-
src/lib/krb5/krb/int-proto.h | 12 ++
src/lib/krb5/krb/pac.c | 148 ++++++++++++++++++++++++-
src/lib/krb5/krb/pac_sign.c | 121 ++++++++++++++++++++
src/lib/krb5/krb/t_pac.c | 182 +++++++++++++++++++++++++++++++
src/lib/krb5/libkrb5.exports | 2 +
src/lib/krb5_32.def | 3 +
src/plugins/kdb/test/kdb_test.c | 6 +-
11 files changed, 544 insertions(+), 41 deletions(-)
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 9e03fd386f..d12be47c3c 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -223,6 +223,8 @@ Rarely used public interfaces
krb5_init_creds_step.rst
krb5_init_keyblock.rst
krb5_is_referral_realm.rst
+ krb5_kdc_sign_ticket.rst
+ krb5_kdc_verify_ticket.rst
krb5_kt_add_entry.rst
krb5_kt_end_seq_get.rst
krb5_kt_get_entry.rst
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 001fb386a7..c6ea088742 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -234,12 +234,18 @@ Public
KRB5_NT_UNKNOWN.rst
KRB5_NT_WELLKNOWN.rst
KRB5_NT_X500_PRINCIPAL.rst
+ KRB5_PAC_ATTRIBUTES_INFO.rst
KRB5_PAC_CLIENT_INFO.rst
+ KRB5_PAC_CLIENT_CLAIMS.rst
KRB5_PAC_CREDENTIALS_INFO.rst
KRB5_PAC_DELEGATION_INFO.rst
+ KRB5_PAC_DEVICE_CLAIMS.rst
+ KRB5_PAC_DEVICE_INFO.rst
KRB5_PAC_LOGON_INFO.rst
KRB5_PAC_PRIVSVR_CHECKSUM.rst
+ KRB5_PAC_REQUESTOR.rst
KRB5_PAC_SERVER_CHECKSUM.rst
+ KRB5_PAC_TICKET_CHECKSUM.rst
KRB5_PAC_UPN_DNS_INFO.rst
KRB5_PADATA_AFS3_SALT.rst
KRB5_PADATA_AP_REQ.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index a7060aa733..8e59628bd9 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1918,7 +1918,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
#define KRB5_AUTHDATA_CAMMAC 96
#define KRB5_AUTHDATA_WIN2K_PAC 128
#define KRB5_AUTHDATA_ETYPE_NEGOTIATION 129 /**< RFC 4537 */
-#define KRB5_AUTHDATA_SIGNTICKET 512 /**< formerly 142 in krb5 1.8 */
+#define KRB5_AUTHDATA_SIGNTICKET 512 /**< @deprecated use PAC */
#define KRB5_AUTHDATA_FX_ARMOR 71
#define KRB5_AUTHDATA_AUTH_INDICATOR 97
#define KRB5_AUTHDATA_AP_OPTIONS 143
@@ -8181,6 +8181,12 @@ krb5_verify_authdata_kdc_issued(krb5_context context,
#define KRB5_PAC_CLIENT_INFO 10 /**< Client name and ticket info */
#define KRB5_PAC_DELEGATION_INFO 11 /**< Constrained delegation info */
#define KRB5_PAC_UPN_DNS_INFO 12 /**< User principal name and DNS info */
+#define KRB5_PAC_CLIENT_CLAIMS 13 /**< Client claims information */
+#define KRB5_PAC_DEVICE_INFO 14 /**< Device information */
+#define KRB5_PAC_DEVICE_CLAIMS 15 /**< Device claims information */
+#define KRB5_PAC_TICKET_CHECKSUM 16 /**< Ticket checksum */
+#define KRB5_PAC_ATTRIBUTES_INFO 17 /**< PAC attributes */
+#define KRB5_PAC_REQUESTOR 18 /**< PAC requestor SID */
struct krb5_pac_data;
/** PAC data structure to convey authorization information */
@@ -8338,56 +8344,84 @@ krb5_pac_verify_ext(krb5_context context, const krb5_pac pac,
krb5_boolean with_realm);
/**
- * Sign a PAC.
+ * Verify a PAC, possibly including ticket signature
*
- * @param [in] context Library context
- * @param [in] pac PAC handle
- * @param [in] authtime Expected timestamp
- * @param [in] principal Expected principal name (or NULL)
- * @param [in] server_key Key for server checksum
- * @param [in] privsvr_key Key for KDC checksum
- * @param [out] data Signed PAC encoding
+ * @param [in] context Library context
+ * @param [in] enc_tkt Ticket enc-part, possibly containing a PAC
+ * @param [in] server_princ Canonicalized name of ticket server
+ * @param [in] server Key to validate server checksum (or NULL)
+ * @param [in] privsvr Key to validate KDC checksum (or NULL)
+ * @param [out] pac_out Verified PAC (NULL if no PAC included)
*
- * This function signs @a pac using the keys @a server_key and @a privsvr_key
- * and returns the signed encoding in @a data. @a pac is modified to include
- * the server and KDC checksum buffers. Use krb5_free_data_contents() to free
- * @a data when it is no longer needed.
+ * If a PAC is present in @a enc_tkt, verify its signatures. If @a privsvr is
+ * not NULL and @a server_princ is not a krbtgt or kadmin/changepw service,
+ * require a ticket signature over @a enc_tkt in addition to the KDC signature.
+ * Place the verified PAC in @a pac_out. If an invalid PAC signature is found,
+ * return an error matching the Windows KDC protocol code for that condition as
+ * closely as possible.
*
- * @version New in 1.10
+ * If no PAC is present in @a enc_tkt, set @a pac_out to NULL and return
+ * successfully.
+ *
+ * @note This function does not validate the PAC_CLIENT_INFO buffer. If a
+ * specific value is expected, the caller can make a separate call to
+ * krb5_pac_verify_ext() with a principal but no keys.
+ *
+ * @retval 0 Success; otherwise - Kerberos error codes
+ *
+ * @version New in 1.20
*/
krb5_error_code KRB5_CALLCONV
+krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr, krb5_pac *pac_out);
+
+/** @deprecated Use krb5_kdc_sign_ticket() instead. */
+krb5_error_code KRB5_CALLCONV
krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
krb5_const_principal principal, const krb5_keyblock *server_key,
const krb5_keyblock *privsvr_key, krb5_data *data);
+/** @deprecated Use krb5_kdc_sign_ticket() instead. */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+ krb5_data *data);
+
/**
- * Sign a PAC, possibly with a specified realm.
+ * Sign a PAC, possibly including a ticket signature
*
* @param [in] context Library context
+ * @param [in] enc_tkt The ticket for the signature
* @param [in] pac PAC handle
- * @param [in] authtime Expected timestamp
- * @param [in] principal Principal name (or NULL)
- * @param [in] server_key Key for server checksum
- * @param [in] privsvr_key Key for KDC checksum
+ * @param [in] server_princ Canonical ticket server name
+ * @param [in] client_princ PAC_CLIENT_INFO principal (or NULL)
+ * @param [in] server Key for server checksum
+ * @param [in] privsvr Key for KDC and ticket checksum
* @param [in] with_realm If true, include the realm of @a principal
- * @param [out] data Signed PAC encoding
*
- * This function is similar to krb5_pac_sign(), but adds a parameter
- * @a with_realm. If @a with_realm is true, the PAC_CLIENT_INFO field of the
- * signed PAC will include the realm of @a principal as well as the name. This
- * flag is necessary to generate PACs for cross-realm S4U2Self referrals.
+ * Sign @a pac using the keys @a server and @a privsvr. Include a ticket
+ * signature over @a enc_tkt if @a server_princ is not a TGS or kadmin/changepw
+ * principal name. Add the signed PAC's encoding to the authorization data of
+ * @a enc_tkt in the first slot, wrapped in an AD-IF-RELEVANT container. If @a
+ * client_princ is non-null, add a PAC_CLIENT_INFO buffer, including the realm
+ * if @a with_realm is true.
*
- * @version New in 1.17
+ * @retval 0 on success, otherwise - Kerberos error codes
+ *
+ * @version New in 1.20
*/
krb5_error_code KRB5_CALLCONV
-krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
- krb5_const_principal principal,
- const krb5_keyblock *server_key,
- const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
- krb5_data *data);
+krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
+ const krb5_pac pac, krb5_const_principal server_princ,
+ krb5_const_principal client_princ,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr,
+ krb5_boolean with_realm);
-
-/*
+/**
* Read client information from a PAC.
*
* @param [in] context Library context
diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
index 439ca02725..cd842b03cd 100644
--- a/src/lib/krb5/krb/deps
+++ b/src/lib/krb5/krb/deps
@@ -709,7 +709,7 @@ pac.so pac.po $(OUTPRE)pac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/k5-utf8.h $(top_srcdir)/include/krb5.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- authdata.h pac.c
+ authdata.h int-proto.h pac.c
pac_sign.so pac_sign.po $(OUTPRE)pac_sign.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -720,7 +720,8 @@ pac_sign.so pac_sign.po $(OUTPRE)pac_sign.$(OBJEXT): \
$(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-utf8.h \
$(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
$(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
- $(top_srcdir)/include/socket-utils.h authdata.h pac_sign.c
+ $(top_srcdir)/include/socket-utils.h authdata.h int-proto.h \
+ pac_sign.c
padata.so padata.po $(OUTPRE)padata.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index fe61bebf5b..453ed60c6c 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -386,4 +386,16 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds);
+/* Return true if mprinc will match any hostname in a host-based principal name
+ * (possibly due to ignore_acceptor_hostname) with krb5_sname_match(). */
+krb5_boolean
+k5_sname_wildcard_host(krb5_context context, krb5_const_principal mprinc);
+
+/* Guess the appropriate name-type for a principal based on the name. */
+krb5_int32
+k5_infer_principal_type(krb5_principal princ);
+
+krb5_boolean
+k5_pac_should_have_ticket_signature(krb5_const_principal sprinc);
+
#endif /* KRB5_INT_FUNC_PROTO__ */
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 1b9ef12276..6eb23d8090 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "int-proto.h"
#include "authdata.h"
#define MAX_BUFFERS 4096
@@ -552,8 +553,10 @@ k5_pac_verify_server_checksum(krb5_context context,
checksum.checksum_type = load_32_le(p);
checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+ if (checksum.checksum_type == CKSUMTYPE_SHA1)
+ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ return KRB5KRB_ERR_GENERIC;
pac_data.length = pac->data.length;
pac_data.data = k5memdup(pac->data.data, pac->data.length, &ret);
@@ -586,7 +589,7 @@ k5_pac_verify_server_checksum(krb5_context context,
}
if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ ret = KRB5KRB_AP_ERR_MODIFIED;
return ret;
}
@@ -623,7 +626,7 @@ k5_pac_verify_kdc_checksum(krb5_context context,
checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ return KRB5KRB_ERR_GENERIC;
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
@@ -635,11 +638,148 @@ k5_pac_verify_kdc_checksum(krb5_context context,
return ret;
if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ ret = KRB5KRB_AP_ERR_MODIFIED;
return ret;
}
+static krb5_error_code
+verify_ticket_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_data *ticket, const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ krb5_checksum checksum;
+ krb5_data checksum_data;
+ krb5_boolean valid;
+ krb5_octet *p;
+
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ &checksum_data);
+ if (ret != 0)
+ return KRB5KRB_AP_ERR_MODIFIED;
+
+ if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ p = (krb5_octet *)checksum_data.data;
+ checksum.checksum_type = load_32_le(p);
+ checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
+ checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+ if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
+ return KRB5KRB_ERR_GENERIC;
+
+ ret = krb5_c_verify_checksum(context, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, ticket,
+ &checksum, &valid);
+ if (ret != 0)
+ return ret;
+
+ return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+}
+
+/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
+ * should not have ticket signatures. */
+krb5_boolean
+k5_pac_should_have_ticket_signature(krb5_const_principal sprinc)
+{
+ if (IS_TGS_PRINC(sprinc))
+ return FALSE;
+ if (sprinc->length == 2 && data_eq_string(sprinc->data[0], "kadmin") &&
+ data_eq_string(sprinc->data[1], "changepw"))
+ return FALSE;
+ return TRUE;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr, krb5_pac *pac_out)
+{
+ krb5_error_code ret;
+ krb5_pac pac = NULL;
+ krb5_data *recoded_tkt = NULL;
+ krb5_authdata **authdata, *orig, **ifrel = NULL, **recoded_ifrel = NULL;
+ uint8_t z = 0;
+ krb5_authdata zpac = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC, 1, &z };
+ size_t i, j;
+
+ *pac_out = NULL;
+
+ /*
+ * Find the position of the PAC in the ticket authdata. ifrel will be the
+ * decoded AD-IF-RELEVANT container at position i containing a PAC, and j
+ * will be the offset within the container.
+ */
+ authdata = enc_tkt->authorization_data;
+ for (i = 0; authdata != NULL && authdata[i] != NULL; i++) {
+ if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ continue;
+
+ ret = krb5_decode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authdata[i], &ifrel);
+ if (ret)
+ goto cleanup;
+
+ for (j = 0; ifrel[j] != NULL; j++) {
+ if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
+ break;
+ }
+ if (ifrel[j] != NULL)
+ break;
+
+ krb5_free_authdata(context, ifrel);
+ ifrel = NULL;
+ }
+
+ /* Stop and return successfully if we didn't find a PAC. */
+ if (ifrel == NULL) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = krb5_pac_parse(context, ifrel[j]->contents, ifrel[j]->length, &pac);
+ if (ret)
+ goto cleanup;
+
+ if (privsvr != NULL && k5_pac_should_have_ticket_signature(server_princ)) {
+ /* To check the PAC ticket signatures, re-encode the ticket with the
+ * PAC contents replaced by a single zero. */
+ orig = ifrel[j];
+ ifrel[j] = &zpac;
+ ret = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ ifrel, &recoded_ifrel);
+ ifrel[j] = orig;
+ if (ret)
+ goto cleanup;
+ orig = authdata[i];
+ authdata[i] = recoded_ifrel[0];
+ ret = encode_krb5_enc_tkt_part(enc_tkt, &recoded_tkt);
+ authdata[i] = orig;
+ if (ret)
+ goto cleanup;
+
+ ret = verify_ticket_checksum(context, pac, recoded_tkt, privsvr);
+ if (ret)
+ goto cleanup;
+ }
+
+ ret = krb5_pac_verify_ext(context, pac, enc_tkt->times.authtime, NULL,
+ server, privsvr, FALSE);
+
+ *pac_out = pac;
+ pac = NULL;
+
+cleanup:
+ krb5_pac_free(context, pac);
+ krb5_free_data(context, recoded_tkt);
+ krb5_free_authdata(context, ifrel);
+ krb5_free_authdata(context, recoded_ifrel);
+ return ret;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_pac_verify(krb5_context context,
const krb5_pac pac,
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index 12f0259b4f..0f9581abbb 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "int-proto.h"
#include "authdata.h"
/* draft-brezak-win2k-krb-authz-00 */
@@ -286,3 +287,123 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
return 0;
}
+
+/* Add a signature over der_enc_tkt in privsvr to pac. der_enc_tkt should be
+ * encoded with a dummy PAC authdata element containing a single zero byte. */
+static krb5_error_code
+add_ticket_signature(krb5_context context, const krb5_pac pac,
+ krb5_data *der_enc_tkt, const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ krb5_data ticket_cksum;
+ krb5_cksumtype ticket_cksumtype;
+ krb5_crypto_iov iov[2];
+
+ /* Create zeroed buffer for checksum. */
+ ret = k5_insert_checksum(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ privsvr, &ticket_cksumtype);
+ if (ret)
+ return ret;
+
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
+ &ticket_cksum);
+ if (ret)
+ return ret;
+
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = *der_enc_tkt;
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data = make_data(ticket_cksum.data + PAC_SIGNATURE_DATA_LENGTH,
+ ticket_cksum.length - PAC_SIGNATURE_DATA_LENGTH);
+ ret = krb5_c_make_checksum_iov(context, ticket_cksumtype, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, iov, 2);
+ if (ret)
+ return ret;
+
+ store_32_le(ticket_cksumtype, ticket_cksum.data);
+ return 0;
+}
+
+/* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
+ * element with contents pac_data. */
+static krb5_error_code
+encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
+{
+ krb5_error_code ret;
+ krb5_authdata *container[2], **encoded_container = NULL;
+ krb5_authdata pac_ad = { KV5M_AUTHDATA, KRB5_AUTHDATA_WIN2K_PAC };
+ uint8_t z = 0;
+
+ pac_ad.contents = (pac_data != NULL) ? (uint8_t *)pac_data->data : &z;
+ pac_ad.length = (pac_data != NULL) ? pac_data->length : 1;
+ container[0] = &pac_ad;
+ container[1] = NULL;
+
+ ret = krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
+ container, &encoded_container);
+ if (ret)
+ return ret;
+
+ *out = encoded_container[0];
+ free(encoded_container);
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_kdc_sign_ticket(krb5_context context, krb5_enc_tkt_part *enc_tkt,
+ const krb5_pac pac, krb5_const_principal server_princ,
+ krb5_const_principal client_princ,
+ const krb5_keyblock *server, const krb5_keyblock *privsvr,
+ krb5_boolean with_realm)
+{
+ krb5_error_code ret;
+ krb5_data *der_enc_tkt = NULL, pac_data = empty_data();
+ krb5_authdata **list, *pac_ad;
+ size_t count;
+
+ /* Reallocate space for another authdata element in enc_tkt. */
+ list = enc_tkt->authorization_data;
+ for (count = 0; list != NULL && list[count] != NULL; count++);
+ list = realloc(enc_tkt->authorization_data, (count + 2) * sizeof(*list));
+ if (list == NULL)
+ return ENOMEM;
+ list[count] = NULL;
+ enc_tkt->authorization_data = list;
+
+ /* Create a dummy PAC for ticket signing and make it the first element. */
+ ret = encode_pac_ad(context, NULL, &pac_ad);
+ if (ret)
+ goto cleanup;
+ memmove(list + 1, list, (count + 1) * sizeof(*list));
+ list[0] = pac_ad;
+
+ if (k5_pac_should_have_ticket_signature(server_princ)) {
+ ret = encode_krb5_enc_tkt_part(enc_tkt, &der_enc_tkt);
+ if (ret)
+ goto cleanup;
+
+ assert(privsvr != NULL);
+ ret = add_ticket_signature(context, pac, der_enc_tkt, privsvr);
+ if (ret)
+ goto cleanup;
+ }
+
+ ret = krb5_pac_sign_ext(context, pac, enc_tkt->times.authtime,
+ client_princ, server, privsvr, with_realm,
+ &pac_data);
+ if (ret)
+ goto cleanup;
+
+ /* Replace the dummy PAC with the signed real one. */
+ ret = encode_pac_ad(context, &pac_data, &pac_ad);
+ if (ret)
+ goto cleanup;
+ free(list[0]->contents);
+ free(list[0]);
+ list[0] = pac_ad;
+
+cleanup:
+ krb5_free_data(context, der_enc_tkt);
+ krb5_free_data_contents(context, &pac_data);
+ return ret;
+}
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index ccd165380d..173bde7bab 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -605,6 +605,186 @@ check_pac(krb5_context context, int index, const unsigned char *pdata,
krb5_pac_free(context, pac);
}
+static const krb5_keyblock ticket_sig_krbtgt_key = {
+ 0, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ 32, U("\x7a\x58\x98\xd2\xaf\xa6\xaf\xc0\x6a\xce\x06\x04\x4b\xc2\x70\x84"
+ "\x9b\x8e\x0a\x6c\x4c\x07\xdc\x6f\xbb\x48\x43\xe1\xd2\xaa\x97\xf7")
+};
+
+static const krb5_keyblock ticket_sig_server_key = {
+ 0, ENCTYPE_ARCFOUR_HMAC,
+ 16, U("\xed\x23\x11\x20\x7a\x21\x44\x20\xbf\xc0\x8d\x36\xf7\xf6\xb2\x3e")
+};
+
+static const krb5_data ticket_data = {
+ .length = 972, .data =
+ "\x61\x82\x03\xC8\x30\x82\x03\xC4\xA0\x03\x02\x01\x05\xA1\x0A\x1B"
+ "\x08\x43\x44\x4F\x4D\x2E\x43\x4F\x4D\xA2\x0F\x30\x0D\xA0\x03\x02"
+ "\x01\x01\xA1\x06\x30\x04\x1B\x02\x73\x31\xA3\x82\x03\x9E\x30\x82"
+ "\x03\x9A\xA0\x03\x02\x01\x17\xA1\x03\x02\x01\x03\xA2\x82\x03\x8C"
+ "\x04\x82\x03\x88\x44\x31\x61\x20\x17\xC9\xFE\xBC\xAC\x46\xB5\x77"
+ "\xE9\x68\x04\x4C\x9B\x31\x91\x0C\xC1\xD4\xDD\xEF\xC7\x34\x20\x08"
+ "\x90\x91\xE8\x79\xE0\xB5\x03\x26\xA4\x65\xDE\xEC\x47\x03\x2A\x8F"
+ "\x61\xE7\x4D\x38\x5A\x42\x95\x5A\xF9\x2F\x41\x2C\x2A\x6E\x60\xA1"
+ "\xEB\x51\xB3\xBD\x4C\x00\x41\x2A\x44\x76\x08\x37\x1A\x51\xFD\x65"
+ "\x67\x7E\xBF\x3D\x90\x86\xE3\x9A\x54\x6B\x67\xA8\x08\x7A\x73\xCC"
+ "\xC3\xB7\x4B\xD5\x5C\x3A\x14\x6C\xC1\x5F\x54\x4B\x92\x55\xB4\xB7"
+ "\x92\x23\x3F\x53\x89\x47\x8E\x1F\x8B\xB9\xDB\x3B\x93\xE8\x70\xE4"
+ "\x24\xB8\x9D\xF0\x0E\x35\x28\xF8\x7A\x27\x5D\xF7\x25\x97\x9C\xF5"
+ "\x9F\x9F\x64\x04\xF2\xA3\xAB\x11\x15\xB6\xDA\x18\xD6\x46\xD5\xE6"
+ "\xB8\x08\xDE\x0A\x62\xFD\xF8\xAA\x52\x90\xD9\x67\x29\xB2\xCD\x06"
+ "\xB6\xB0\x50\x2B\x3F\x0F\xA3\xA5\xBF\xAA\x6E\x40\x03\xD6\x5F\x02"
+ "\xBC\xD8\x18\x47\x97\x09\xD7\xE4\x96\x3B\xCB\xEB\x92\x2C\x3C\x49"
+ "\xFF\x1F\x71\xE0\x52\x94\x0F\x8B\x9F\xB8\x2A\xBB\x9C\xE2\xA3\xDD"
+ "\x38\x89\xE2\xB1\x0B\x9E\x1F\x7A\xB3\xE3\xD2\xB0\x94\xDC\x87\xBE"
+ "\x37\xA6\xD3\xB3\x29\x35\x9A\x72\xC3\x7A\xF1\xA9\xE6\xC5\xD1\x26"
+ "\x83\x65\x44\x17\xBA\x55\xA8\x5E\x94\x26\xED\xE9\x8A\x93\x11\x5D"
+ "\x7E\x20\x1B\x9C\x15\x9E\x13\x37\x03\x4D\xDD\x99\x51\xD8\x66\x29"
+ "\x6A\xB9\xFB\x49\xFE\x52\x78\xDA\x86\x85\xA9\xA3\xB9\xEF\xEC\xAD"
+ "\x35\xA6\x8D\xAC\x0F\x75\x22\xBB\x0B\x49\x1C\x13\x52\x40\xC9\x52"
+ "\x69\x09\x54\xD1\x0F\x94\x3F\x22\x48\x67\xB0\x96\x28\xAA\xE6\x28"
+ "\xD9\x0C\x08\xEF\x51\xED\x15\x5E\xA2\x53\x59\xA5\x03\xB4\x06\x20"
+ "\x3D\xCC\xB4\xC5\xF8\x8C\x73\x67\xA3\x21\x3D\x19\xCD\xD4\x12\x28"
+ "\xD2\x93\xDE\x0D\xF0\x71\x10\x50\xD6\x33\x35\x04\x11\x64\x43\x39"
+ "\xC3\xDF\x96\xE3\x66\xE3\x85\xCA\xE7\x67\x14\x3A\xF0\x43\xAA\xBB"
+ "\xD4\x1D\xB5\x24\xB5\x74\x90\x25\xA7\x87\x7E\xDB\xD3\x83\x8A\x3A"
+ "\x69\xA8\x2D\xAF\xB7\xB8\xF3\xDC\x13\xAF\x45\x61\x3F\x59\x39\x7E"
+ "\x69\xDE\x0C\x04\xF1\x10\x6B\xB4\x56\xFA\x21\x9F\x72\x2B\x60\x86"
+ "\xE3\x23\x0E\xC4\x51\xF6\xBE\xD8\xE1\x5F\xEE\x73\x4C\x17\x4C\x2C"
+ "\x1B\xFB\x9F\x1F\x7A\x3B\x07\x5B\x8E\xF1\x01\xAC\xD6\x30\x94\x8A"
+ "\x5D\x22\x6F\x08\xCE\xED\x5E\xB6\xDB\x86\x8C\x87\xEB\x8D\x91\xFF"
+ "\x0A\x86\x30\xBD\xC0\xF8\x25\xE7\xAE\x24\x35\xF2\xFC\xE5\xFD\x1B"
+ "\xB0\x05\x4A\xA3\xE5\xEB\x2E\x05\xAD\x99\x67\x49\x87\xE6\xB3\x87"
+ "\x82\xA4\x59\xA7\x6E\xDD\xF2\xB6\x66\xE8\xF7\x70\xF5\xBD\xC9\x0E"
+ "\xFA\x9C\x79\x84\xD4\x9B\x05\x0E\xBB\xF5\xDB\xEF\xFC\xCC\x26\xF2"
+ "\x93\xCF\xD2\x04\x3C\xA9\x2C\x65\x42\x97\x86\xD8\x38\x0A\x1E\xF6"
+ "\xD6\xCA\x30\xB5\x1A\xEC\xFB\xBA\x3B\x84\x57\xB0\xFD\xFB\xE6\xBC"
+ "\xF2\x76\xF6\x4C\xBB\xAB\xB1\x31\xA1\x27\x7C\xE6\xE6\x81\xB6\xCE"
+ "\x84\x86\x40\xB6\x40\x33\xC4\xF8\xB4\x15\xCF\xAA\xA5\x51\x78\xB9"
+ "\x8B\x50\x25\xB2\x88\x86\x96\x72\x8C\x71\x4D\xB5\x3A\x94\x86\x77"
+ "\x0E\x95\x9B\x16\x93\xEF\x3A\x11\x79\xBA\x83\xF7\x74\xD3\x8D\xBA"
+ "\x15\xE1\x2C\x04\x57\xA8\x92\x1E\x9D\x00\x8E\x20\xFD\x30\x70\xE7"
+ "\xF5\x65\x2F\x19\x0C\x94\xBA\x03\x71\x12\x96\xCD\xC8\xB4\x96\xDB"
+ "\xCE\x19\xC2\xDF\x3C\xC2\xF6\x3D\x53\xED\x98\xA5\x41\x72\x2A\x22"
+ "\x7B\xF3\x2B\x17\x6C\xE1\x39\x7D\xAE\x9B\x11\xF9\xC1\xA6\x9E\x9F"
+ "\x89\x3C\x12\xAA\x94\x74\xA7\x4F\x70\xE8\xB9\xDE\x04\xF0\x9D\x39"
+ "\x24\x2D\x92\xE8\x46\x2D\x2E\xF0\x40\x66\x1A\xD9\x27\xF9\x98\xF1"
+ "\x81\x1D\x70\x62\x63\x30\x6D\xCD\x84\x04\x5F\xFA\x83\xD3\xEC\x8D"
+ "\x86\xFB\x40\x61\xC1\x8A\x45\xFF\x7B\xD9\xD4\x18\x61\x7F\x51\xE3"
+ "\xFC\x1E\x18\xF0\xAF\xC6\x18\x2C\xE1\x6D\x5D\xF9\x62\xFC\x20\xA3"
+ "\xB2\x8A\x5F\xE5\xBB\x29\x0F\x99\x63\x07\x88\x38\x3A\x3B\x73\x2A"
+ "\x6D\xDA\x3D\xA8\x0D\x8F\x56\x41\x89\x82\xE5\xB8\x61\x00\x64\x7D"
+ "\x17\x0C\xCE\x03\x55\x8F\xF4\x5B\x0D\x50\xF2\xEB\x05\x67\xBE\xDB"
+ "\x7B\x75\xC5\xEA\xA1\xAB\x1D\xB0\x3C\x6D\x42\x08\x0B\x9A\x45\x20"
+ "\xA8\x8F\xE5\x67\x47\x30\xDE\x93\x5F\x43\x05\xEB\xA8\x2D\x80\xF5"
+ "\x1A\xB8\x4A\x4E\x42\x2D\x0B\x7A\xDC\x46\x20\x2D\x13\x17\xDD\x4B"
+ "\x94\x96\xAA\x1F\x06\x0C\x1F\x62\x07\x9C\x40\xA1"
+};
+
+static void
+test_pac_ticket_signature(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_ticket *ticket;
+ krb5_principal sprinc;
+ krb5_authdata **authdata1, **authdata2;
+ krb5_pac pac, pac2, pac3;
+ uint32_t *list;
+ size_t len, i;
+ krb5_data data;
+
+ ret = krb5_decode_ticket(&ticket_data, &ticket);
+ if (ret)
+ err(context, ret, "while decoding ticket");
+
+ ret = krb5_decrypt_tkt_part(context, &ticket_sig_server_key, ticket);
+ if (ret)
+ err(context, ret, "while decrypting ticket");
+
+ ret = krb5_parse_name(context, "s1@CDOM.COM", &sprinc);
+ if (ret)
+ err(context, ret, "krb5_parse_name");
+
+ ret = krb5_kdc_verify_ticket(context, ticket->enc_part2, sprinc,
+ &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, &pac);
+ if (ret)
+ err(context, ret, "while verifying ticket");
+
+ /* In this test, the server is also the client. */
+ ret = krb5_pac_verify(context, pac, ticket->enc_part2->times.authtime,
+ ticket->server, NULL, NULL);
+ if (ret)
+ err(context, ret, "while verifying PAC client info");
+
+ /* We know there is only a PAC in this test's ticket. */
+ authdata1 = ticket->enc_part2->authorization_data;
+ ticket->enc_part2->authorization_data = NULL;
+
+ ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac, sprinc,
+ sprinc, &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, FALSE);
+ if (ret)
+ err(context, ret, "while signing ticket");
+
+ authdata2 = ticket->enc_part2->authorization_data;
+ assert(authdata2 != NULL);
+ assert(authdata2[1] == NULL);
+
+ assert(authdata1[0]->length == authdata2[0]->length);
+ assert(memcmp(authdata1[0]->contents, authdata2[0]->contents,
+ authdata1[0]->length) == 0);
+
+ /* Test adding signatures to a new PAC. */
+ ret = krb5_pac_init(context, &pac2);
+ if (ret)
+ err(context, ret, "krb5_pac_init");
+
+ ret = krb5_pac_get_types(context, pac, &len, &list);
+ if (ret)
+ err(context, ret, "krb5_pac_get_types");
+
+ for (i = 0; i < len; i++) {
+ /* Skip server_cksum, privsvr_cksum, and ticket_cksum. */
+ if (list[i] == 6 || list[i] == 7 || list[i] == 16)
+ continue;
+
+ ret = krb5_pac_get_buffer(context, pac, list[i], &data);
+ if (ret)
+ err(context, ret, "krb5_pac_get_buffer");
+
+ ret = krb5_pac_add_buffer(context, pac2, list[i], &data);
+ if (ret)
+ err(context, ret, "krb5_pac_add_buffer");
+
+ krb5_free_data_contents(context, &data);
+ }
+ free(list);
+
+ krb5_free_authdata(context, authdata1);
+ krb5_free_authdata(context, ticket->enc_part2->authorization_data);
+ ticket->enc_part2->authorization_data = NULL;
+
+ ret = krb5_kdc_sign_ticket(context, ticket->enc_part2, pac2, sprinc, NULL,
+ &ticket_sig_server_key, &ticket_sig_krbtgt_key,
+ FALSE);
+ if (ret)
+ err(context, ret, "while signing ticket");
+
+ /* We can't compare the data since the order of the buffers may differ. */
+ ret = krb5_kdc_verify_ticket(context, ticket->enc_part2, sprinc,
+ &ticket_sig_server_key,
+ &ticket_sig_krbtgt_key, &pac3);
+ if (ret)
+ err(context, ret, "while verifying ticket");
+
+ krb5_pac_free(context, pac);
+ krb5_pac_free(context, pac2);
+ krb5_pac_free(context, pac3);
+ krb5_free_principal(context, sprinc);
+ krb5_free_ticket(context, ticket);
+}
+
int
main(int argc, char **argv)
{
@@ -618,6 +798,8 @@ main(int argc, char **argv)
if (ret)
err(NULL, 0, "krb5_init_contex");
+ test_pac_ticket_signature(context);
+
ret = krb5_set_default_realm(context, "WIN2K3.THINKER.LOCAL");
if (ret)
err(context, ret, "krb5_set_default_realm");
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 48ae46f5c4..28784ec67c 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -462,6 +462,8 @@ krb5_is_permitted_enctype
krb5_is_referral_realm
krb5_is_thread_safe
krb5_kdc_rep_decrypt_proc
+krb5_kdc_sign_ticket
+krb5_kdc_verify_ticket
krb5_kt_add_entry
krb5_kt_client_default
krb5_kt_close
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index 209c6aaef5..8c3469a96c 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -506,3 +506,6 @@ EXPORTS
; new in 1.20
krb5_marshal_credentials @472
krb5_unmarshal_credentials @473
+ k5_sname_compare @474 ; PRIVATE GSSAPI
+ krb5_kdc_sign_ticket @475 ;
+ krb5_kdc_verify_ticket @476 ;
diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 38d371cb86..7d033acae4 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -675,7 +675,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
int tries;
ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
- if (ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ if (ret != KRB5KRB_AP_ERR_MODIFIED)
return ret;
kvno = tgt->key_data[0].key_data_kvno - 1;
@@ -684,7 +684,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
if (ret)
- return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ return KRB5KRB_AP_ERR_MODIFIED;
ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
if (ret)
return ret;
@@ -697,7 +697,7 @@ verify_kdc_signature(krb5_context context, krb5_pac pac,
kvno = kd->key_data_kvno - 1;
}
- return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ return KRB5KRB_AP_ERR_MODIFIED;
}
static krb5_error_code
--
2.39.2

View File

@ -1,169 +0,0 @@
From 5c2f409c360560c8b99926d6cf1a80419e758b22 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 7 Mar 2023 00:19:33 -0500
Subject: [PATCH] Add a simple DER support header
(cherry picked from commit 548da160b52b25a106e9f6077d6a42c2c049586c)
---
src/include/k5-der.h | 149 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 src/include/k5-der.h
diff --git a/src/include/k5-der.h b/src/include/k5-der.h
new file mode 100644
index 0000000000..b8371d9b4d
--- /dev/null
+++ b/src/include/k5-der.h
@@ -0,0 +1,149 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */
+/*
+ * Copyright (C) 2023 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Most ASN.1 encoding and decoding is done using the table-driven framework in
+ * libkrb5. When that is not an option, these helpers can be used to encode
+ * and decode simple types.
+ */
+
+#ifndef K5_DER_H
+#define K5_DER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "k5-buf.h"
+#include "k5-input.h"
+
+/* Return the number of bytes needed to encode len as a DER encoding length. */
+static inline size_t
+k5_der_len_len(size_t len)
+{
+ size_t llen;
+
+ if (len < 128)
+ return 1;
+ llen = 1;
+ while (len > 0) {
+ len >>= 8;
+ llen++;
+ }
+ return llen;
+}
+
+/* Return the number of bytes needed to encode a DER value (with identifier
+ * byte and length) for a given contents length. */
+static inline size_t
+k5_der_value_len(size_t contents_len)
+{
+ return 1 + k5_der_len_len(contents_len) + contents_len;
+}
+
+/* Add a DER identifier byte (composed by the caller, including the ASN.1
+ * class, tag, and constructed bit) and length. */
+static inline void
+k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len)
+{
+ uint8_t *p;
+ size_t llen = k5_der_len_len(len);
+
+ p = k5_buf_get_space(buf, 1 + llen);
+ if (p == NULL)
+ return;
+ *p++ = idbyte;
+ if (len < 128) {
+ *p = len;
+ } else {
+ *p = 0x80 | (llen - 1);
+ /* Encode the length bytes backwards so the most significant byte is
+ * first. */
+ p += llen;
+ while (len > 0) {
+ *--p = len & 0xFF;
+ len >>= 8;
+ }
+ }
+}
+
+/* Add a DER value (identifier byte, length, and contents). */
+static inline void
+k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,
+ size_t len)
+{
+ k5_der_add_taglen(buf, idbyte, len);
+ k5_buf_add_len(buf, contents, len);
+}
+
+/*
+ * If the next byte in in matches idbyte and the subsequent DER length is
+ * valid, advance in past the value, set *contents_out to the value contents,
+ * and return true. Otherwise return false. Only set an error on in if the
+ * next bytes matches idbyte but the ensuing length is invalid. contents_out
+ * may be aliased to in; it will only be written to on successful decoding of a
+ * value.
+ */
+static inline bool
+k5_der_get_value(struct k5input *in, uint8_t idbyte,
+ struct k5input *contents_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+ const void *bytes;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ bytes = k5_input_get_bytes(in, len);
+ if (bytes == NULL)
+ return false;
+ k5_input_init(contents_out, bytes, len);
+ return true;
+}
+
+#endif /* K5_DER_H */
--
2.45.1

View File

@ -1,226 +0,0 @@
From 433dd85aaf8d9ed0e923c873f107995232b94422 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 14:20:34 -0400
Subject: [PATCH] Add request_timeout configuration parameter
Add a parameter to limit the total amount of time taken for a KDC or
password change request.
ticket: 9106 (new)
(cherry picked from commit 802318cda963456b3ed7856c836e89da891483be)
---
doc/admin/conf_files/krb5_conf.rst | 9 ++++++
src/include/k5-int.h | 2 ++
src/lib/krb5/krb/init_ctx.c | 14 +++++++-
src/lib/krb5/os/sendto_kdc.c | 51 ++++++++++++++++++++----------
4 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 315253e378..557094f6a2 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -357,6 +357,15 @@ The libdefaults section may contain any of the following relations:
(:ref:`duration` string.) Sets the default renewable lifetime
for initial ticket requests. The default value is 0.
+**request_timeout**
+ (:ref:`duration` string.) Sets the maximum total time for KDC or
+ password change requests. This timeout does not affect the
+ intervals between requests, so setting a low timeout may result in
+ fewer requests being attempted and/or some servers not being
+ contacted. A value of 0 indicates no specific maximum, in which
+ case requests will time out if no server responds after several
+ tries. The default value is 0. (New in release 1.22.)
+
**spake_preauth_groups**
A whitespace or comma-separated list of words which specifies the
groups allowed for SPAKE preauthentication. The possible values
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 912aaedac4..9d5e41ca2c 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -293,6 +293,7 @@ typedef unsigned char u_char;
#define KRB5_CONF_SPAKE_PREAUTH_INDICATOR "spake_preauth_indicator"
#define KRB5_CONF_SPAKE_PREAUTH_KDC_CHALLENGE "spake_preauth_kdc_challenge"
#define KRB5_CONF_SPAKE_PREAUTH_GROUPS "spake_preauth_groups"
+#define KRB5_CONF_REQUEST_TIMEOUT "request_timeout"
#define KRB5_CONF_TICKET_LIFETIME "ticket_lifetime"
#define KRB5_CONF_UDP_PREFERENCE_LIMIT "udp_preference_limit"
#define KRB5_CONF_UNLOCKITER "unlockiter"
@@ -1218,6 +1219,7 @@ struct _krb5_context {
kdb5_dal_handle *dal_handle;
/* allowable clock skew */
krb5_deltat clockskew;
+ krb5_deltat req_timeout;
krb5_flags kdc_default_options;
krb5_flags library_options;
krb5_boolean profile_secure;
diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
index 9a4741fa64..1a6e0bf672 100644
--- a/src/lib/krb5/krb/init_ctx.c
+++ b/src/lib/krb5/krb/init_ctx.c
@@ -163,7 +163,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
} seed_data;
krb5_data seed;
int tmp;
- char *plugin_dir = NULL;
+ char *plugin_dir = NULL, *timeout_str = NULL;
/* Verify some assumptions. If the assumptions hold and the
compiler is optimizing, this should result in no code being
@@ -257,6 +257,17 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp);
ctx->clockskew = tmp;
+ retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
+ KRB5_CONF_REQUEST_TIMEOUT, NULL, NULL,
+ &timeout_str);
+ if (retval)
+ goto cleanup;
+ if (timeout_str != NULL) {
+ retval = krb5_string_to_deltat(timeout_str, &ctx->req_timeout);
+ if (retval)
+ goto cleanup;
+ }
+
get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK,
&tmp);
ctx->kdc_default_options = tmp;
@@ -298,6 +309,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
cleanup:
profile_release_string(plugin_dir);
+ profile_release_string(timeout_str);
krb5_free_context(ctx);
return retval;
}
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 8e4fcd2a38..f57117126e 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1390,34 +1390,41 @@ get_endtime(time_ms endtime, struct conn_state *conns)
static krb5_boolean
service_fds(krb5_context context, struct select_state *selstate,
- time_ms interval, struct conn_state *conns,
+ time_ms interval, time_ms timeout, struct conn_state *conns,
struct select_state *seltemp, const krb5_data *realm,
int (*msg_handler)(krb5_context, const krb5_data *, void *),
void *msg_handler_data, struct conn_state **winner_out)
{
int e, selret = 0;
- time_ms endtime;
+ time_ms curtime, interval_end, endtime;
struct conn_state *state;
*winner_out = NULL;
- e = get_curtime_ms(&endtime);
+ e = get_curtime_ms(&curtime);
if (e)
return TRUE;
- endtime += interval;
+ interval_end = curtime + interval;
e = 0;
while (selstate->nfds > 0) {
- e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
- seltemp, &selret);
+ endtime = get_endtime(interval_end, conns);
+ /* Don't wait longer than the whole request should last. */
+ if (timeout && endtime > timeout)
+ endtime = timeout;
+ e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
continue;
if (e != 0)
break;
- if (selret == 0)
- /* Timeout, return to caller. */
+ if (selret == 0) {
+ /* We timed out. Stop if we hit the overall request timeout. */
+ if (timeout && (get_curtime_ms(&curtime) || curtime >= timeout))
+ return TRUE;
+ /* Otherwise return to the caller to send the next request. */
return FALSE;
+ }
/* Got something on a socket, process it. */
for (state = conns; state != NULL; state = state->next) {
@@ -1490,7 +1497,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
void *msg_handler_data)
{
int pass;
- time_ms delay;
+ time_ms delay, timeout = 0;
krb5_error_code retval;
struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
size_t s;
@@ -1500,6 +1507,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
*reply = empty_data();
+ if (context->req_timeout) {
+ retval = get_curtime_ms(&timeout);
+ if (retval)
+ return retval;
+ timeout += 1000 * context->req_timeout;
+ }
+
/* One for use here, listing all our fds in use, and one for
* temporary use in service_fds, for the fds of interest. */
sel_state = malloc(2 * sizeof(*sel_state));
@@ -1527,8 +1541,9 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
}
@@ -1540,13 +1555,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
+ done = service_fds(context, sel_state, 1000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
/* Wait for two seconds at the end of the first pass. */
if (!done) {
- done = service_fds(context, sel_state, 2000, conns, seltemp,
+ done = service_fds(context, sel_state, 2000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
@@ -1557,15 +1572,17 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
if (sel_state->nfds == 0)
break;
}
/* Wait for the delay backoff at the end of this pass. */
if (!done) {
- done = service_fds(context, sel_state, delay, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, delay, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
if (sel_state->nfds == 0)
break;
--
2.44.0

View File

@ -1,34 +0,0 @@
From a68fba22588cc21dcd1dc28550529187dca58331 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 27 Oct 2023 00:44:53 -0400
Subject: [PATCH] End connection on KDC_ERR_SVC_UNAVAILABLE
In sendto_kdc.c:service_fds(), if a message handler indicates that a
message should be discarded, kill the connection so we don't continue
waiting on it for more data.
ticket: 7899
(cherry picked from commit ca80f64c786341d5871ae1de18142e62af64f7b9)
---
src/lib/krb5/os/sendto_kdc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index d76e24ccf0..8e4fcd2a38 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1435,7 +1435,10 @@ service_fds(krb5_context context, struct select_state *selstate,
if (msg_handler != NULL) {
krb5_data reply = make_data(state->in.buf, state->in.pos);
- stop = (msg_handler(context, &reply, msg_handler_data) != 0);
+ if (!msg_handler(context, &reply, msg_handler_data)) {
+ kill_conn(context, state, selstate);
+ stop = 0;
+ }
}
if (stop) {
--
2.44.0

View File

@ -1,258 +0,0 @@
From b0372e31b81321a820204450a35c7633caf1b7dd Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jan 2022 02:05:58 -0500
Subject: [PATCH] Factor out PAC checksum verification
Reduce code repetition in PAC checksum handling by adding a helper
function. Remove the unnecessary prefix on several function names.
---
src/lib/krb5/krb/pac.c | 173 +++++++++++++----------------------------
1 file changed, 55 insertions(+), 118 deletions(-)
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 6eb23d8090..2f6ad4e1df 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -493,10 +493,8 @@ k5_pac_validate_client(krb5_context context,
}
static krb5_error_code
-k5_pac_zero_signature(krb5_context context,
- const krb5_pac pac,
- krb5_ui_4 type,
- krb5_data *data)
+zero_signature(krb5_context context, const krb5_pac pac, krb5_ui_4 type,
+ krb5_data *data)
{
PAC_INFO_BUFFER *buffer = NULL;
size_t i;
@@ -530,151 +528,89 @@ k5_pac_zero_signature(krb5_context context,
}
static krb5_error_code
-k5_pac_verify_server_checksum(krb5_context context,
- const krb5_pac pac,
- const krb5_keyblock *server)
+verify_checksum(krb5_context context, const krb5_pac pac, uint32_t buffer_type,
+ const krb5_keyblock *key, krb5_keyusage usage,
+ const krb5_data *data)
{
krb5_error_code ret;
- krb5_data pac_data; /* PAC with zeroed checksums */
+ krb5_data buffer;
+ krb5_cksumtype cksumtype;
krb5_checksum checksum;
- krb5_data checksum_data;
krb5_boolean valid;
- krb5_octet *p;
+ size_t cksumlen;
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &checksum_data);
+ ret = k5_pac_locate_buffer(context, pac, buffer_type, &buffer);
if (ret != 0)
return ret;
-
- if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+ if (buffer.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
- p = (krb5_octet *)checksum_data.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (checksum.checksum_type == CKSUMTYPE_SHA1)
+ cksumtype = load_32_le(buffer.data);
+ if (buffer_type == KRB5_PAC_SERVER_CHECKSUM && cksumtype == CKSUMTYPE_SHA1)
return KRB5KDC_ERR_SUMTYPE_NOSUPP;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
+ if (!krb5_c_is_keyed_cksum(cksumtype))
return KRB5KRB_ERR_GENERIC;
- pac_data.length = pac->data.length;
- pac_data.data = k5memdup(pac->data.data, pac->data.length, &ret);
- if (pac_data.data == NULL)
- return ret;
-
- /* Zero out both checksum buffers */
- ret = k5_pac_zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM,
- &pac_data);
- if (ret != 0) {
- free(pac_data.data);
- return ret;
- }
-
- ret = k5_pac_zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
- &pac_data);
- if (ret != 0) {
- free(pac_data.data);
+ /* There may be an RODCIdentifier trailer (see [MS-PAC] 2.8), so look up
+ * the length of the checksum by its type. */
+ ret = krb5_c_checksum_length(context, cksumtype, &cksumlen);
+ if (ret)
return ret;
- }
+ if (cksumlen > buffer.length - PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+ checksum.checksum_type = cksumtype;
+ checksum.length = cksumlen;
+ checksum.contents = (uint8_t *)buffer.data + PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, server,
- KRB5_KEYUSAGE_APP_DATA_CKSUM,
- &pac_data, &checksum, &valid);
+ ret = krb5_c_verify_checksum(context, key, usage, data, &checksum, &valid);
+ return ret ? ret : (valid ? 0 : KRB5KRB_AP_ERR_MODIFIED);
+}
- free(pac_data.data);
+static krb5_error_code
+verify_server_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_keyblock *server)
+{
+ krb5_error_code ret;
+ krb5_data copy; /* PAC with zeroed checksums */
- if (ret != 0) {
+ ret = krb5int_copy_data_contents(context, &pac->data, &copy);
+ if (ret)
return ret;
- }
- if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_MODIFIED;
+ /* Zero out both checksum buffers */
+ ret = zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+ ret = zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, &copy);
+ if (ret)
+ goto cleanup;
+
+ ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+cleanup:
+ free(copy.data);
return ret;
}
static krb5_error_code
-k5_pac_verify_kdc_checksum(krb5_context context,
- const krb5_pac pac,
- const krb5_keyblock *privsvr)
+verify_kdc_checksum(krb5_context context, const krb5_pac pac,
+ const krb5_keyblock *privsvr)
{
krb5_error_code ret;
- krb5_data server_checksum, privsvr_checksum;
- krb5_checksum checksum;
- krb5_boolean valid;
- krb5_octet *p;
-
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
- &privsvr_checksum);
- if (ret != 0)
- return ret;
-
- if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
+ krb5_data server_checksum;
ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
&server_checksum);
- if (ret != 0)
+ if (ret)
return ret;
-
if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
-
- p = (krb5_octet *)privsvr_checksum.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_ERR_GENERIC;
-
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM,
- &server_checksum, &checksum, &valid);
- if (ret != 0)
- return ret;
-
- if (valid == FALSE)
- ret = KRB5KRB_AP_ERR_MODIFIED;
-
- return ret;
-}
-
-static krb5_error_code
-verify_ticket_checksum(krb5_context context, const krb5_pac pac,
- const krb5_data *ticket, const krb5_keyblock *privsvr)
-{
- krb5_error_code ret;
- krb5_checksum checksum;
- krb5_data checksum_data;
- krb5_boolean valid;
- krb5_octet *p;
-
- ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_TICKET_CHECKSUM,
- &checksum_data);
- if (ret != 0)
- return KRB5KRB_AP_ERR_MODIFIED;
-
- if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
- return KRB5_BAD_MSIZE;
-
- p = (krb5_octet *)checksum_data.data;
- checksum.checksum_type = load_32_le(p);
- checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
- checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
- if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
- return KRB5KRB_ERR_GENERIC;
-
- ret = krb5_c_verify_checksum(context, privsvr,
- KRB5_KEYUSAGE_APP_DATA_CKSUM, ticket,
- &checksum, &valid);
- if (ret != 0)
- return ret;
-
- return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+ return verify_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, &server_checksum);
}
/* Per MS-PAC 2.8.3, tickets encrypted to TGS and password change principals
@@ -761,7 +697,8 @@ krb5_kdc_verify_ticket(krb5_context context, const krb5_enc_tkt_part *enc_tkt,
if (ret)
goto cleanup;
- ret = verify_ticket_checksum(context, pac, recoded_tkt, privsvr);
+ ret = verify_checksum(context, pac, KRB5_PAC_TICKET_CHECKSUM, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM, recoded_tkt);
if (ret)
goto cleanup;
}
@@ -804,13 +741,13 @@ krb5_pac_verify_ext(krb5_context context,
krb5_error_code ret;
if (server != NULL) {
- ret = k5_pac_verify_server_checksum(context, pac, server);
+ ret = verify_server_checksum(context, pac, server);
if (ret != 0)
return ret;
}
if (privsvr != NULL) {
- ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
+ ret = verify_kdc_checksum(context, pac, privsvr);
if (ret != 0)
return ret;
}
--
2.39.2

View File

@ -1,45 +0,0 @@
From 058dfbaed97c8e09ac4f3f7a1655b64ab3cf0144 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 21 Jul 2021 13:44:30 -0400
Subject: [PATCH] Fix defcred leak in krb5 gss_inquire_cred()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Commit 1cd2821c19b2b95e39d5fc2f451a035585a40fa5 altered the memory
management of krb5_gss_inquire_cred(), introducing defcred to act as
an owner pointer when the function must acquire a default credential.
The commit neglected to update the code to release the default cred
along the successful path. The old code does not trigger because
cred_handle is now reassigned, so the default credential is leaked.
Reported by Pavel Březina.
(a minimal alternative to commit 593e16448e1af23eef74689afe06a7bcc86e79c7)
ticket: 9016
version_fixed: 1.18.4
(cherry picked from commit b92be484630b38e26f5ee4bd67973fbd7627009c)
---
src/lib/gssapi/krb5/inq_cred.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c
index a8f2541102..cd8384d08c 100644
--- a/src/lib/gssapi/krb5/inq_cred.c
+++ b/src/lib/gssapi/krb5/inq_cred.c
@@ -197,9 +197,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
mechs = GSS_C_NO_OID_SET;
}
- if (cred_handle == GSS_C_NO_CREDENTIAL)
- krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred);
-
+ krb5_gss_release_cred(minor_status, &defcred);
krb5_free_context(context);
*minor_status = 0;
return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE);
--
2.44.0

View File

@ -1,205 +0,0 @@
From efb3acd20cbe6330439635a9f297b9dae8a0a5d3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 5 Mar 2024 19:53:07 -0500
Subject: [PATCH] Fix two unlikely memory leaks
In gss_krb5int_make_seal_token_v3(), one of the bounds checks (which
could probably never be triggered) leaks plain.data. Fix this leak
and use current practices for cleanup throughout the function.
In xmt_rmtcallres() (unused within the tree and likely elsewhere),
store port_ptr into crp->port_ptr as soon as it is allocated;
otherwise it could leak if the subsequent xdr_u_int32() operation
fails.
(cherry picked from commit c5f9c816107f70139de11b38aa02db2f1774ee0d)
---
src/lib/gssapi/krb5/k5sealv3.c | 56 +++++++++++++++-------------------
src/lib/rpc/pmap_rmt.c | 9 +++---
2 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index 3b4f8cb837..e881eee835 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -65,7 +65,7 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
int conf_req_flag, int toktype)
{
size_t bufsize = 16;
- unsigned char *outbuf = 0;
+ unsigned char *outbuf = NULL;
krb5_error_code err;
int key_usage;
unsigned char acceptor_flag;
@@ -75,9 +75,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
size_t ec;
unsigned short tok_id;
- krb5_checksum sum;
+ krb5_checksum sum = { 0 };
krb5_key key;
krb5_cksumtype cksumtype;
+ krb5_data plain = empty_data();
+
+ token->value = NULL;
+ token->length = 0;
acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
key_usage = (toktype == KG_TOK_WRAP_MSG
@@ -107,14 +111,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
- krb5_data plain;
krb5_enc_data cipher;
size_t ec_max;
size_t encrypt_size;
/* 300: Adds some slop. */
- if (SIZE_MAX - 300 < message->length)
- return ENOMEM;
+ if (SIZE_MAX - 300 < message->length) {
+ err = ENOMEM;
+ goto cleanup;
+ }
ec_max = SIZE_MAX - message->length - 300;
if (ec_max > 0xffff)
ec_max = 0xffff;
@@ -126,20 +131,20 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
err = alloc_data(&plain, message->length + 16 + ec);
if (err)
- return err;
+ goto cleanup;
/* Get size of ciphertext. */
encrypt_size = krb5_encrypt_size(plain.length, key->keyblock.enctype);
if (encrypt_size > SIZE_MAX / 2) {
err = ENOMEM;
- goto error;
+ goto cleanup;
}
bufsize = 16 + encrypt_size;
/* Allocate space for header plus encrypted data. */
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- return ENOMEM;
+ err = ENOMEM;
+ goto cleanup;
}
/* TOK_ID */
@@ -164,11 +169,8 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
cipher.ciphertext.length = bufsize - 16;
cipher.enctype = key->keyblock.enctype;
err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err)
- goto error;
+ goto cleanup;
/* Now that we know we're returning a valid token.... */
ctx->seq_send++;
@@ -181,7 +183,6 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
/* If the rotate fails, don't worry about it. */
#endif
} else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
- krb5_data plain;
size_t cksumsize;
/* Here, message is the application-supplied data; message2 is
@@ -193,21 +194,19 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
wrap_with_checksum:
err = alloc_data(&plain, message->length + 16);
if (err)
- return err;
+ goto cleanup;
err = krb5_c_checksum_length(context, cksumtype, &cksumsize);
if (err)
- goto error;
+ goto cleanup;
assert(cksumsize <= 0xffff);
bufsize = 16 + message2->length + cksumsize;
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- plain.data = 0;
err = ENOMEM;
- goto error;
+ goto cleanup;
}
/* TOK_ID */
@@ -239,23 +238,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
if (message2->length)
memcpy(outbuf + 16, message2->value, message2->length);
- sum.contents = outbuf + 16 + message2->length;
- sum.length = cksumsize;
-
err = krb5_k_make_checksum(context, cksumtype, key,
key_usage, &plain, &sum);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err) {
zap(outbuf,bufsize);
- goto error;
+ goto cleanup;
}
if (sum.length != cksumsize)
abort();
memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize);
- krb5_free_checksum_contents(context, &sum);
- sum.contents = 0;
/* Now that we know we're actually generating the token... */
ctx->seq_send++;
@@ -285,12 +276,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
token->value = outbuf;
token->length = bufsize;
- return 0;
+ outbuf = NULL;
+ err = 0;
-error:
+cleanup:
+ krb5_free_checksum_contents(context, &sum);
+ zapfree(plain.data, plain.length);
gssalloc_free(outbuf);
- token->value = NULL;
- token->length = 0;
return err;
}
diff --git a/src/lib/rpc/pmap_rmt.c b/src/lib/rpc/pmap_rmt.c
index 8c7e30c21a..0748af34a7 100644
--- a/src/lib/rpc/pmap_rmt.c
+++ b/src/lib/rpc/pmap_rmt.c
@@ -160,11 +160,12 @@ xdr_rmtcallres(
caddr_t port_ptr;
port_ptr = (caddr_t)(void *)crp->port_ptr;
- if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
- xdr_u_int32) && xdr_u_int32(xdrs, &crp->resultslen)) {
- crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (!xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
+ (xdrproc_t)xdr_u_int32))
+ return (FALSE);
+ crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (xdr_u_int32(xdrs, &crp->resultslen))
return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
- }
return (FALSE);
}
--
2.44.0

View File

@ -1,535 +0,0 @@
From c73e8ed9f89fdc709d15656b6431492d43de94ce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jun 2024 10:56:12 -0400
Subject: [PATCH] Fix vulnerabilities in GSS message token handling
In gss_krb5int_unseal_token_v3() and gss_krb5int_unseal_v3_iov(),
verify the Extra Count field of CFX wrap tokens against the encrypted
header. Reported by Jacob Champion.
In gss_krb5int_unseal_token_v3(), check for a decrypted plaintext
length too short to contain the encrypted header and extra count
bytes. Reported by Jacob Champion.
In kg_unseal_iov_token(), separately track the header IOV length and
complete token length when parsing the token's ASN.1 wrapper. This
fix contains modified versions of functions from k5-der.h and
util_token.c; this duplication will be cleaned up in a future commit.
CVE-2024-37370:
In MIT krb5 release 1.3 and later, an attacker can modify the
plaintext Extra Count field of a confidential GSS krb5 wrap token,
causing the unwrapped token to appear truncated to the application.
CVE-2024-37371:
In MIT krb5 release 1.3 and later, an attacker can cause invalid
memory reads by sending message tokens with invalid length fields.
ticket: 9128 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit b0a2f8a5365f2eec3e27d78907de9f9d2c80505a)
---
src/lib/gssapi/krb5/k5sealv3.c | 5 +
src/lib/gssapi/krb5/k5sealv3iov.c | 3 +-
src/lib/gssapi/krb5/k5unsealiov.c | 80 +++++++++-
src/tests/gssapi/t_invalid.c | 233 +++++++++++++++++++++++++-----
4 files changed, 275 insertions(+), 46 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index e881eee835..d3210c1107 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -400,10 +400,15 @@ gss_krb5int_unseal_token_v3(krb5_context *contextptr,
/* Don't use bodysize here! Use the fact that
cipher.ciphertext.length has been adjusted to the
correct length. */
+ if (plain.length < 16 + ec) {
+ free(plain.data);
+ goto defective;
+ }
althdr = (unsigned char *)plain.data + plain.length - 16;
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr+4) != ec
|| memcmp(althdr+8, ptr+8, 8)) {
free(plain.data);
goto defective;
diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c
index 333ee124dd..f8e90c35b4 100644
--- a/src/lib/gssapi/krb5/k5sealv3iov.c
+++ b/src/lib/gssapi/krb5/k5sealv3iov.c
@@ -402,9 +402,10 @@ gss_krb5int_unseal_v3_iov(krb5_context context,
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr + 4) != ec
|| memcmp(althdr + 8, ptr + 8, 8) != 0) {
*minor_status = 0;
- return GSS_S_BAD_SIG;
+ return GSS_S_DEFECTIVE_TOKEN;
}
} else {
/* Verify checksum: note EC is checksum size here, not padding */
diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c
index 3ce2a90ce9..6a6585d9af 100644
--- a/src/lib/gssapi/krb5/k5unsealiov.c
+++ b/src/lib/gssapi/krb5/k5unsealiov.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "k5-der.h"
#include "gssapiP_krb5.h"
static OM_uint32
@@ -247,6 +248,73 @@ cleanup:
return retval;
}
+/* Similar to k5_der_get_value(), but output an unchecked content length
+ * instead of a k5input containing the contents. */
+static inline bool
+get_der_tag(struct k5input *in, uint8_t idbyte, size_t *len_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ if (in->status)
+ return false;
+
+ *len_out = len;
+ return true;
+}
+
+/*
+ * Similar to g_verify_token_header() without toktype or flags, but do not read
+ * more than *header_len bytes of ASN.1 wrapper, and on output set *header_len
+ * to the remaining number of header bytes. Verify the outer DER tag's length
+ * against token_len, which may be larger (but not smaller) than *header_len.
+ */
+static gss_int32
+verify_detached_wrapper(const gss_OID_desc *mech, size_t *header_len,
+ uint8_t **header_in, size_t token_len)
+{
+ struct k5input in, mech_der;
+ gss_OID_desc toid;
+ size_t len;
+
+ k5_input_init(&in, *header_in, *header_len);
+
+ if (get_der_tag(&in, 0x60, &len)) {
+ if (len != token_len - (in.ptr - *header_in))
+ return G_BAD_TOK_HEADER;
+ if (!k5_der_get_value(&in, 0x06, &mech_der))
+ return G_BAD_TOK_HEADER;
+ toid.elements = (uint8_t *)mech_der.ptr;
+ toid.length = mech_der.len;
+ if (!g_OID_equal(&toid, mech))
+ return G_WRONG_MECH;
+ }
+
+ *header_in = (uint8_t *)in.ptr;
+ *header_len = in.len;
+ return 0;
+}
+
/*
* Caller must provide TOKEN | DATA | PADDING | TRAILER, except
* for DCE in which case it can just provide TOKEN | DATA (must
@@ -267,8 +335,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
gss_iov_buffer_t header;
gss_iov_buffer_t padding;
gss_iov_buffer_t trailer;
- size_t input_length;
- unsigned int bodysize;
+ size_t input_length, hlen;
int toktype2;
header = kg_locate_header_iov(iov, iov_count, toktype);
@@ -298,15 +365,14 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
input_length += trailer->buffer.length;
}
- code = g_verify_token_header(ctx->mech_used,
- &bodysize, &ptr, -1,
- input_length, 0);
+ hlen = header->buffer.length;
+ code = verify_detached_wrapper(ctx->mech_used, &hlen, &ptr, input_length);
if (code != 0) {
*minor_status = code;
return GSS_S_DEFECTIVE_TOKEN;
}
- if (bodysize < 2) {
+ if (hlen < 2) {
*minor_status = (OM_uint32)G_BAD_TOK_HEADER;
return GSS_S_DEFECTIVE_TOKEN;
}
@@ -314,7 +380,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
toktype2 = load_16_be(ptr);
ptr += 2;
- bodysize -= 2;
+ hlen -= 2;
switch (toktype2) {
case KG2_TOK_MIC_MSG:
diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c
index fb8fe55111..8192935099 100644
--- a/src/tests/gssapi/t_invalid.c
+++ b/src/tests/gssapi/t_invalid.c
@@ -36,31 +36,41 @@
*
* 1. A pre-CFX wrap or MIC token processed with a CFX-only context causes a
* null pointer dereference. (The token must use SEAL_ALG_NONE or it will
- * be rejected.)
+ * be rejected.) This vulnerability also applies to IOV unwrap.
*
- * 2. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
+ * 2. A CFX wrap token with a different value of EC between the plaintext and
+ * encrypted copies will be erroneously accepted, which allows a message
+ * truncation attack. This vulnerability also applies to IOV unwrap.
+ *
+ * 3. A CFX wrap token with a plaintext length fewer than 16 bytes causes an
+ * access before the beginning of the input buffer, possibly leading to a
+ * crash.
+ *
+ * 4. A CFX wrap token with a plaintext EC value greater than the plaintext
+ * length - 16 causes an integer underflow when computing the result length,
+ * likely causing a crash.
+ *
+ * 5. An IOV unwrap operation will overrun the header buffer if an ASN.1
+ * wrapper longer than the header buffer is present.
+ *
+ * 6. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
* header causes an input buffer overrun, usually leading to either a segv
* or a GSS_S_DEFECTIVE_TOKEN error due to garbage algorithm, filler, or
- * sequence number values.
+ * sequence number values. This vulnerability also applies to IOV unwrap.
*
- * 3. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
+ * 7. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
* header causes an integer underflow when computing the ciphertext length,
* leading to an allocation error on 32-bit platforms or a segv on 64-bit
* platforms. A pre-CFX MIC token of this size causes an input buffer
* overrun when comparing the checksum, perhaps leading to a segv.
*
- * 4. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
+ * 8. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
* ciphertext (where padlen is the last byte of the decrypted ciphertext)
* causes an integer underflow when computing the original message length,
* leading to an allocation error.
*
- * 5. In the mechglue, truncated encapsulation in the initial context token can
+ * 9. In the mechglue, truncated encapsulation in the initial context token can
* cause input buffer overruns in gss_accept_sec_context().
- *
- * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with
- * fewer than 16 bytes after the ASN.1 header will be rejected.
- * Vulnerabilities #2 and #5 can only be robustly detected using a
- * memory-checking environment such as valgrind.
*/
#include "k5-int.h"
@@ -97,17 +107,25 @@ struct test {
}
};
-/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */
+static void *
+ealloc(size_t len)
+{
+ void *ptr = calloc(len, 1);
+
+ if (ptr == NULL)
+ abort();
+ return ptr;
+}
+
+/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key.
+ * The context takes ownership of subkey. */
static gss_ctx_id_t
-make_fake_cfx_context()
+make_fake_cfx_context(krb5_key subkey)
{
gss_union_ctx_id_t uctx;
krb5_gss_ctx_id_t kgctx;
- krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
kgctx->proto = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
@@ -116,15 +134,10 @@ make_fake_cfx_context()
kgctx->sealalg = -1;
kgctx->signalg = -1;
- kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
- kb.length = 16;
- kb.contents = (unsigned char *)"1234567887654321";
- if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0)
- abort();
+ kgctx->subkey = subkey;
+ kgctx->cksumtype = CKSUMTYPE_HMAC_SHA1_96_AES128;
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -138,9 +151,7 @@ make_fake_context(const struct test *test)
krb5_gss_ctx_id_t kgctx;
krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
abort();
@@ -162,9 +173,7 @@ make_fake_context(const struct test *test)
if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0)
abort();
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -194,9 +203,7 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
assert(mech_krb5.length == 9);
assert(len + 11 < 128);
- wrapped = malloc(len + 13);
- if (wrapped == NULL)
- abort();
+ wrapped = ealloc(len + 13);
wrapped[0] = 0x60;
wrapped[1] = len + 11;
wrapped[2] = 0x06;
@@ -207,6 +214,18 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
out->value = wrapped;
}
+/* Create a 16-byte header for a CFX confidential wrap token to be processed by
+ * the fake CFX context. */
+static void
+write_cfx_header(uint16_t ec, uint8_t *out)
+{
+ memset(out, 0, 16);
+ store_16_be(KG2_TOK_WRAP_MSG, out);
+ out[2] = FLAG_WRAP_CONFIDENTIAL;
+ out[3] = 0xFF;
+ store_16_be(ec, out + 4);
+}
+
/* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with
* regular and IOV unwrap. */
static void
@@ -238,6 +257,134 @@ test_bogus_1964_token(gss_ctx_id_t ctx)
free(in.value);
}
+static void
+test_cfx_altered_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], plainbuf[24];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+ gss_iov_buffer_desc iov[2];
+
+ /* Construct a header with a plaintext EC value of 3. */
+ write_cfx_header(3, tokbuf);
+
+ /* Encrypt a plaintext and a copy of the header with the EC value 0. */
+ memcpy(plainbuf, "truncate", 8);
+ memcpy(plainbuf + 8, tokbuf, 16);
+ store_16_be(0, plainbuf + 12);
+ plain = make_data(plainbuf, 24);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Verify that the token is rejected by gss_unwrap_iov(). */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ iov[0].buffer = in;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+}
+
+static void
+test_cfx_short_plaintext(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], zerobyte = 0;
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ write_cfx_header(0, tokbuf);
+
+ /* Encrypt a single byte, with no copy of the header. */
+ plain = make_data(&zerobyte, 1);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_cfx_large_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128] = { 0 }, plainbuf[20];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ /* Construct a header with an EC value of 5. */
+ write_cfx_header(5, tokbuf);
+
+ /* Encrypt a 4-byte plaintext plus the header. */
+ memcpy(plainbuf, "abcd", 4);
+ memcpy(plainbuf + 4, tokbuf, 16);
+ plain = make_data(plainbuf, 20);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_iov_large_asn1_wrapper(gss_ctx_id_t ctx)
+{
+ OM_uint32 minor, major;
+ uint8_t databuf[10] = { 0 };
+ gss_iov_buffer_desc iov[2];
+
+ /*
+ * In this IOV array, the header contains a DER tag with a dangling eight
+ * bytes of length field. The data IOV indicates a total token length
+ * sufficient to contain the length bytes.
+ */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.value = ealloc(2);
+ iov[0].buffer.length = 2;
+ memcpy(iov[0].buffer.value, "\x60\x88", 2);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = databuf;
+ iov[1].buffer.length = 10;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov[0].buffer.value);
+}
+
/* Process wrap and MIC tokens with incomplete headers. */
static void
test_short_header(gss_ctx_id_t ctx)
@@ -387,9 +534,7 @@ try_accept(void *value, size_t len)
gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
/* Copy the provided value to make input overruns more obvious. */
- in.value = malloc(len);
- if (in.value == NULL)
- abort();
+ in.value = ealloc(len);
memcpy(in.value, value, len);
in.length = len;
(void)gss_accept_sec_context(&minor, &ctx, GSS_C_NO_CREDENTIAL, &in,
@@ -424,11 +569,23 @@ test_short_encapsulation()
int
main(int argc, char **argv)
{
+ krb5_keyblock kb;
+ krb5_key cfx_subkey;
gss_ctx_id_t ctx;
size_t i;
- ctx = make_fake_cfx_context();
+ kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+ kb.length = 16;
+ kb.contents = (unsigned char *)"1234567887654321";
+ if (krb5_k_create_key(NULL, &kb, &cfx_subkey) != 0)
+ abort();
+
+ ctx = make_fake_cfx_context(cfx_subkey);
test_bogus_1964_token(ctx);
+ test_cfx_altered_ec(ctx, cfx_subkey);
+ test_cfx_short_plaintext(ctx, cfx_subkey);
+ test_cfx_large_ec(ctx, cfx_subkey);
+ test_iov_large_asn1_wrapper(ctx);
free_fake_context(ctx);
for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
--
2.45.1

View File

@ -1,629 +0,0 @@
From aad00d346e5c7923287fc0016a37b49c4618d78e Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Thu, 22 Aug 2024 17:15:50 +0200
Subject: [PATCH] Generate and verify message MACs in libkrad
Implement some of the measures specified in
draft-ietf-radext-deprecating-radius-03 for mitigating the BlastRADIUS
attack (CVE-2024-3596):
* Include a Message-Authenticator MAC as the first attribute when
generating a packet of type Access-Request, Access-Reject,
Access-Accept, or Access-Challenge (sections 5.2.1 and 5.2.4), if
the secret is non-empty. (An empty secret indicates the use of Unix
domain socket transport.)
* Validate the Message-Authenticator MAC in received packets, if
present.
FreeRADIUS enforces Message-Authenticator as of versions 3.2.5 and
3.0.27. libkrad must generate Message-Authenticator attributes in
order to remain compatible with these implementations.
[ghudson@mit.edu: adjusted style and naming; simplified some
functions; edited commit message]
ticket: 9142 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit 871125fea8ce0370a972bf65f7d1de63f619b06c)
---
src/include/k5-int.h | 5 +
src/lib/crypto/krb/checksum_hmac_md5.c | 28 ++++
src/lib/crypto/libk5crypto.exports | 1 +
src/lib/krad/attr.c | 17 ++
src/lib/krad/attrset.c | 59 +++++--
src/lib/krad/internal.h | 7 +-
src/lib/krad/packet.c | 206 +++++++++++++++++++++++--
src/lib/krad/t_attrset.c | 2 +-
src/lib/krad/t_daemon.py | 3 +-
src/lib/krad/t_packet.c | 11 ++
src/tests/t_otp.py | 3 +
11 files changed, 311 insertions(+), 31 deletions(-)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 9d5e41ca2c..d062617268 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -2415,4 +2415,9 @@ void k5_change_error_message_code(krb5_context ctx, krb5_error_code oldcode,
#define k5_prependmsg krb5_prepend_error_message
#define k5_wrapmsg krb5_wrap_error_message
+/* Generate an HMAC-MD5 keyed checksum as specified by RFC 2104. */
+krb5_error_code
+k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output);
+
#endif /* _KRB5_INT_H */
diff --git a/src/lib/crypto/krb/checksum_hmac_md5.c b/src/lib/crypto/krb/checksum_hmac_md5.c
index ec024f3966..a809388549 100644
--- a/src/lib/crypto/krb/checksum_hmac_md5.c
+++ b/src/lib/crypto/krb/checksum_hmac_md5.c
@@ -92,3 +92,31 @@ cleanup:
free(hash_iov);
return ret;
}
+
+krb5_error_code
+k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
+ krb5_data *output)
+{
+ krb5_error_code ret;
+ const struct krb5_hash_provider *hash = &krb5int_hash_md5;
+ krb5_keyblock keyblock = { 0 };
+ krb5_data hashed_key;
+ uint8_t hkeybuf[16];
+ krb5_crypto_iov iov;
+
+ /* Hash the key if it is longer than the block size. */
+ if (key->length > hash->blocksize) {
+ hashed_key = make_data(hkeybuf, sizeof(hkeybuf));
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ iov.data = *key;
+ ret = hash->hash(&iov, 1, &hashed_key);
+ if (ret)
+ return ret;
+ key = &hashed_key;
+ }
+
+ keyblock.magic = KV5M_KEYBLOCK;
+ keyblock.length = key->length;
+ keyblock.contents = (uint8_t *)key->data;
+ return krb5int_hmac_keyblock(hash, &keyblock, data, num_data, output);
+}
diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports
index 9db1813810..b4dcd29937 100644
--- a/src/lib/crypto/libk5crypto.exports
+++ b/src/lib/crypto/libk5crypto.exports
@@ -107,3 +107,4 @@ krb5_c_prfplus
krb5_c_derive_prfplus
k5_enctype_to_ssf
krb5int_c_deprecated_enctype
+k5_hmac_md5
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
index 42d354a3b5..65ed1d35e7 100644
--- a/src/lib/krad/attr.c
+++ b/src/lib/krad/attr.c
@@ -125,6 +125,23 @@ static const attribute_record attributes[UCHAR_MAX] = {
{"NAS-Port-Type", 4, 4, NULL, NULL},
{"Port-Limit", 4, 4, NULL, NULL},
{"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
+ {NULL, 0, 0, NULL, NULL}, /* Password-Retry */
+ {NULL, 0, 0, NULL, NULL}, /* Prompt */
+ {NULL, 0, 0, NULL, NULL}, /* Connect-Info */
+ {NULL, 0, 0, NULL, NULL}, /* Configuration-Token */
+ {NULL, 0, 0, NULL, NULL}, /* EAP-Message */
+ {"Message-Authenticator", MD5_DIGEST_SIZE, MD5_DIGEST_SIZE, NULL, NULL},
};
/* Encode User-Password attribute. */
diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
index 6ec031e320..e5457ebfd7 100644
--- a/src/lib/krad/attrset.c
+++ b/src/lib/krad/attrset.c
@@ -164,15 +164,44 @@ krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
return 0;
}
+/* Place an encoded attributes into outbuf at position *i. Increment *i by the
+ * length of the encoding. */
+static krb5_error_code
+append_attr(krb5_context ctx, const char *secret,
+ const uint8_t *auth, krad_attr type, const krb5_data *data,
+ uint8_t outbuf[MAX_ATTRSETSIZE], size_t *i, krb5_boolean *is_fips)
+{
+ uint8_t buffer[MAX_ATTRSIZE];
+ size_t attrlen;
+ krb5_error_code retval;
+
+ retval = kr_attr_encode(ctx, secret, auth, type, data, buffer, &attrlen,
+ is_fips);
+ if (retval)
+ return retval;
+
+ if (attrlen > MAX_ATTRSETSIZE - *i - 2)
+ return EMSGSIZE;
+
+ outbuf[(*i)++] = type;
+ outbuf[(*i)++] = attrlen + 2;
+ memcpy(outbuf + *i, buffer, attrlen);
+ *i += attrlen;
+
+ return 0;
+}
+
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
- const unsigned char *auth,
+ const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
krb5_boolean *is_fips)
{
- unsigned char buffer[MAX_ATTRSIZE];
krb5_error_code retval;
- size_t i = 0, attrlen;
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const uint8_t zeroes[MD5_DIGEST_SIZE] = { 0 };
+ krb5_data zerodata;
+ size_t i = 0;
attr *a;
if (set == NULL) {
@@ -180,19 +209,21 @@ kr_attrset_encode(const krad_attrset *set, const char *secret,
return 0;
}
- K5_TAILQ_FOREACH(a, &set->list, list) {
- retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
- buffer, &attrlen, is_fips);
- if (retval != 0)
+ if (add_msgauth) {
+ /* Encode Message-Authenticator as the first attribute, per
+ * draft-ietf-radext-deprecating-radius-03 section 5.2. */
+ zerodata = make_data((uint8_t *)zeroes, MD5_DIGEST_SIZE);
+ retval = append_attr(set->ctx, secret, auth, msgauth_type, &zerodata,
+ outbuf, &i, is_fips);
+ if (retval)
return retval;
+ }
- if (i + attrlen + 2 > MAX_ATTRSETSIZE)
- return EMSGSIZE;
-
- outbuf[i++] = a->type;
- outbuf[i++] = attrlen + 2;
- memcpy(&outbuf[i], buffer, attrlen);
- i += attrlen;
+ K5_TAILQ_FOREACH(a, &set->list, list) {
+ retval = append_attr(set->ctx, secret, auth, a->type, &a->attr,
+ outbuf, &i, is_fips);
+ if (retval)
+ return retval;
}
*outlen = i;
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
index b086598fb2..f3e4a1d8d3 100644
--- a/src/lib/krad/internal.h
+++ b/src/lib/krad/internal.h
@@ -45,6 +45,8 @@
#define UCHAR_MAX 255
#endif
+#define MD5_DIGEST_SIZE 16
+
/* RFC 2865 */
#define MAX_ATTRSIZE (UCHAR_MAX - 2)
#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
@@ -75,10 +77,11 @@ kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
krad_attr type, const krb5_data *in,
unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
-/* Encode the attributes into the buffer. */
+/* Encode set into outbuf. If add_msgauth is true, include a zeroed
+ * Message-Authenticator as the first attribute. */
krb5_error_code
kr_attrset_encode(const krad_attrset *set, const char *secret,
- const unsigned char *auth,
+ const uint8_t *auth, krb5_boolean add_msgauth,
unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen,
krb5_boolean *is_fips);
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
index fc2d248001..257bbc6345 100644
--- a/src/lib/krad/packet.c
+++ b/src/lib/krad/packet.c
@@ -36,6 +36,7 @@
typedef unsigned char uchar;
/* RFC 2865 */
+#define MSGAUTH_SIZE (2 + MD5_DIGEST_SIZE)
#define OFFSET_CODE 0
#define OFFSET_ID 1
#define OFFSET_LENGTH 2
@@ -222,6 +223,106 @@ packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
}
+/* Determine if a packet requires a Message-Authenticator attribute. */
+static inline krb5_boolean
+requires_msgauth(const char *secret, krad_code code)
+{
+ /* If no secret is provided, assume that the transport is a UNIX socket.
+ * Message-Authenticator is required only on UDP and TCP connections. */
+ if (*secret == '\0')
+ return FALSE;
+
+ /*
+ * Per draft-ietf-radext-deprecating-radius-03 sections 5.2.1 and 5.2.4,
+ * Message-Authenticator is required in Access-Request packets and all
+ * potential responses when UDP or TCP transport is used.
+ */
+ return code == krad_code_name2num("Access-Request") ||
+ code == krad_code_name2num("Access-Reject") ||
+ code == krad_code_name2num("Access-Accept") ||
+ code == krad_code_name2num("Access-Challenge");
+}
+
+/* Check if the packet has a Message-Authenticator attribute. */
+static inline krb5_boolean
+has_pkt_msgauth(const krad_packet *pkt)
+{
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+
+ return krad_attrset_get(pkt->attrset, msgauth_type, 0) != NULL;
+}
+
+/* Return the beginning of the Message-Authenticator attribute in pkt, or NULL
+ * if no such attribute is present. */
+static const uint8_t *
+lookup_msgauth_addr(const krad_packet *pkt)
+{
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ size_t i;
+ uint8_t *p;
+
+ i = OFFSET_ATTR;
+ while (i + 2 < pkt->pkt.length) {
+ p = (uint8_t *)offset(&pkt->pkt, i);
+ if (msgauth_type == *p)
+ return p;
+ i += p[1];
+ }
+
+ return NULL;
+}
+
+/*
+ * Calculate the message authenticator MAC for pkt as specified in RFC 2869
+ * section 5.14, placing the result in mac_out. Use the provided authenticator
+ * auth, which may be from pkt or from a corresponding request.
+ */
+static krb5_error_code
+calculate_mac(const char *secret, const krad_packet *pkt,
+ const uint8_t auth[AUTH_FIELD_SIZE],
+ uint8_t mac_out[MD5_DIGEST_SIZE])
+{
+ uint8_t zeroed_msgauth[MSGAUTH_SIZE];
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const uint8_t *msgauth_attr, *msgauth_end, *pkt_end;
+ krb5_crypto_iov input[5];
+ krb5_data ksecr, mac;
+
+ msgauth_attr = lookup_msgauth_addr(pkt);
+ if (msgauth_attr == NULL)
+ return EINVAL;
+ msgauth_end = msgauth_attr + MSGAUTH_SIZE;
+ pkt_end = (const uint8_t *)pkt->pkt.data + pkt->pkt.length;
+
+ /* Read code, id, and length from the packet. */
+ input[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[0].data = make_data(pkt->pkt.data, OFFSET_AUTH);
+
+ /* Read the provided authenticator. */
+ input[1].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[1].data = make_data((uint8_t *)auth, AUTH_FIELD_SIZE);
+
+ /* Read any attributes before Message-Authenticator. */
+ input[2].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[2].data = make_data(pkt_attr(pkt), msgauth_attr - pkt_attr(pkt));
+
+ /* Read Message-Authenticator with the data bytes all set to zero, per RFC
+ * 2869 section 5.14. */
+ zeroed_msgauth[0] = msgauth_type;
+ zeroed_msgauth[1] = MSGAUTH_SIZE;
+ memset(zeroed_msgauth + 2, 0, MD5_DIGEST_SIZE);
+ input[3].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[3].data = make_data(zeroed_msgauth, MSGAUTH_SIZE);
+
+ /* Read any attributes after Message-Authenticator. */
+ input[4].flags = KRB5_CRYPTO_TYPE_DATA;
+ input[4].data = make_data((uint8_t *)msgauth_end, pkt_end - msgauth_end);
+
+ mac = make_data(mac_out, MD5_DIGEST_SIZE);
+ ksecr = string2data((char *)secret);
+ return k5_hmac_md5(&ksecr, input, 5, &mac);
+}
+
ssize_t
krad_packet_bytes_needed(const krb5_data *buffer)
{
@@ -255,6 +356,7 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
krad_packet *pkt;
uchar id;
size_t attrset_len;
+ krb5_boolean msgauth_required;
pkt = packet_new();
if (pkt == NULL) {
@@ -274,9 +376,13 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
if (retval != 0)
goto error;
+ /* Determine if Message-Authenticator is required. */
+ msgauth_required = (*secret != '\0' &&
+ code == krad_code_name2num("Access-Request"));
+
/* Encode the attributes. */
- retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
- &attrset_len, &pkt->is_fips);
+ retval = kr_attrset_encode(set, secret, pkt_auth(pkt), msgauth_required,
+ pkt_attr(pkt), &attrset_len, &pkt->is_fips);
if (retval != 0)
goto error;
@@ -285,6 +391,13 @@ krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
pkt_code_set(pkt, code);
pkt_len_set(pkt, pkt->pkt.length);
+ if (msgauth_required) {
+ /* Calculate and set the Message-Authenticator MAC. */
+ retval = calculate_mac(secret, pkt, pkt_auth(pkt), pkt_attr(pkt) + 2);
+ if (retval != 0)
+ goto error;
+ }
+
/* Copy the attrset for future use. */
retval = packet_set_attrset(ctx, secret, pkt);
if (retval != 0)
@@ -307,14 +420,19 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
krb5_error_code retval;
krad_packet *pkt;
size_t attrset_len;
+ krb5_boolean msgauth_required;
pkt = packet_new();
if (pkt == NULL)
return ENOMEM;
+ /* Determine if Message-Authenticator is required. */
+ msgauth_required = requires_msgauth(secret, code);
+
/* Encode the attributes. */
- retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
- &attrset_len, &pkt->is_fips);
+ retval = kr_attrset_encode(set, secret, pkt_auth(request),
+ msgauth_required, pkt_attr(pkt), &attrset_len,
+ &pkt->is_fips);
if (retval != 0)
goto error;
@@ -330,6 +448,18 @@ krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
if (retval != 0)
goto error;
+ if (msgauth_required) {
+ /*
+ * Calculate and replace the Message-Authenticator MAC. Per RFC 2869
+ * section 5.14, use the authenticator from the request, not from the
+ * response.
+ */
+ retval = calculate_mac(secret, pkt, pkt_auth(request),
+ pkt_attr(pkt) + 2);
+ if (retval != 0)
+ goto error;
+ }
+
/* Copy the attrset for future use. */
retval = packet_set_attrset(ctx, secret, pkt);
if (retval != 0)
@@ -343,6 +473,34 @@ error:
return retval;
}
+/* Verify the Message-Authenticator value in pkt, using the provided
+ * authenticator (which may be from pkt or from a corresponding request). */
+static krb5_error_code
+verify_msgauth(const char *secret, const krad_packet *pkt,
+ const uint8_t auth[AUTH_FIELD_SIZE])
+{
+ uint8_t mac[MD5_DIGEST_SIZE];
+ krad_attr msgauth_type = krad_attr_name2num("Message-Authenticator");
+ const krb5_data *msgauth;
+ krb5_error_code retval;
+
+ msgauth = krad_packet_get_attr(pkt, msgauth_type, 0);
+ if (msgauth == NULL)
+ return ENODATA;
+
+ retval = calculate_mac(secret, pkt, auth, mac);
+ if (retval)
+ return retval;
+
+ if (msgauth->length != MD5_DIGEST_SIZE)
+ return EMSGSIZE;
+
+ if (k5_bcmp(mac, msgauth->data, MD5_DIGEST_SIZE) != 0)
+ return EBADMSG;
+
+ return 0;
+}
+
/* Decode a packet. */
static krb5_error_code
decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
@@ -394,21 +552,35 @@ krad_packet_decode_request(krb5_context ctx, const char *secret,
krad_packet **reqpkt)
{
const krad_packet *tmp = NULL;
+ krad_packet *req;
krb5_error_code retval;
- retval = decode_packet(ctx, secret, buffer, reqpkt);
- if (cb != NULL && retval == 0) {
+ retval = decode_packet(ctx, secret, buffer, &req);
+ if (retval)
+ return retval;
+
+ /* Verify Message-Authenticator if present. */
+ if (has_pkt_msgauth(req)) {
+ retval = verify_msgauth(secret, req, pkt_auth(req));
+ if (retval) {
+ krad_packet_free(req);
+ return retval;
+ }
+ }
+
+ if (cb != NULL) {
for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
break;
}
- }
- if (cb != NULL && (retval != 0 || tmp != NULL))
- (*cb)(data, TRUE);
+ if (tmp != NULL)
+ (*cb)(data, TRUE);
+ }
+ *reqpkt = req;
*duppkt = tmp;
- return retval;
+ return 0;
}
krb5_error_code
@@ -435,9 +607,17 @@ krad_packet_decode_response(krb5_context ctx, const char *secret,
break;
}
- /* If the authenticator matches, then the response is valid. */
- if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0)
- break;
+ /* Verify the response authenticator. */
+ if (k5_bcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) != 0)
+ continue;
+
+ /* Verify Message-Authenticator if present. */
+ if (has_pkt_msgauth(*rsppkt)) {
+ if (verify_msgauth(secret, *rsppkt, pkt_auth(tmp)) != 0)
+ continue;
+ }
+
+ break;
}
}
diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
index 0f95762534..9a70529dc5 100644
--- a/src/lib/krad/t_attrset.c
+++ b/src/lib/krad/t_attrset.c
@@ -63,7 +63,7 @@ main()
noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
/* Encode attrset. */
- noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len,
+ noerror(kr_attrset_encode(set, "foo", auth, FALSE, buffer, &encode_len,
&is_fips));
krad_attrset_free(set);
diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
index 7668cd7f87..7fa0449a3c 100755
--- a/src/lib/krad/t_daemon.py
+++ b/src/lib/krad/t_daemon.py
@@ -40,6 +40,7 @@ DICTIONARY = """
ATTRIBUTE\tUser-Name\t1\tstring
ATTRIBUTE\tUser-Password\t2\toctets
ATTRIBUTE\tNAS-Identifier\t32\tstring
+ATTRIBUTE\tMessage-Authenticator\t80\toctets
"""
class TestServer(server.Server):
@@ -52,7 +53,7 @@ class TestServer(server.Server):
if key == "User-Password":
passwd = map(pkt.PwDecrypt, pkt[key])
- reply = self.CreateReplyPacket(pkt)
+ reply = self.CreateReplyPacket(pkt, message_authenticator=True)
if passwd == ['accept']:
reply.code = packet.AccessAccept
else:
diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
index c22489144f..104b6507a2 100644
--- a/src/lib/krad/t_packet.c
+++ b/src/lib/krad/t_packet.c
@@ -172,6 +172,9 @@ main(int argc, const char **argv)
krb5_data username, password;
krb5_boolean auth = FALSE;
krb5_context ctx;
+ const krad_packet *dupreq;
+ const krb5_data *encpkt;
+ krad_packet *decreq;
username = string2data("testUser");
@@ -184,9 +187,17 @@ main(int argc, const char **argv)
password = string2data("accept");
noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
+ encpkt = krad_packet_encode(packets[ACCEPT_PACKET]);
+ noerror(krad_packet_decode_request(ctx, "foo", encpkt, NULL, NULL,
+ &dupreq, &decreq));
+ krad_packet_free(decreq);
password = string2data("reject");
noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
+ encpkt = krad_packet_encode(packets[REJECT_PACKET]);
+ noerror(krad_packet_decode_request(ctx, "foo", encpkt, NULL, NULL,
+ &dupreq, &decreq));
+ krad_packet_free(decreq);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index cba871a0f2..1ec916598c 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -49,6 +49,7 @@ ATTRIBUTE User-Name 1 string
ATTRIBUTE User-Password 2 octets
ATTRIBUTE Service-Type 6 integer
ATTRIBUTE NAS-Identifier 32 string
+ATTRIBUTE Message-Authenticator 80 octets
'''
class RadiusDaemon(Process):
@@ -97,6 +98,8 @@ class RadiusDaemon(Process):
reply.code = packet.AccessReject
replyq['reply'] = False
+ reply.add_message_authenticator()
+
outq.put(replyq)
if addr is None:
sock.send(reply.ReplyPacket())
--
2.46.0

File diff suppressed because it is too large Load Diff

View File

@ -1,184 +0,0 @@
From b6ada496a285a7b350e28c97b53b6f659a9a94b9 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 11 Dec 2021 01:25:34 -0500
Subject: [PATCH] Use 14 instead of 9 for unkeyed SHA-1 checksum
Although MIT krb5 had been using the value 9 for unkeyed SHA-1 since
its 1.0 release in 1996, RFC 3961 instead assigned this value to
rsa-md5-des3 (likely never used), and assigned the values 10 and 14 to
SHA-1. Heimdal and Microsoft use the value 14. Unkeyed SHA-1 almost
never appears on the wire, but has been seen in PKINIT asChecksum
fields in replies from Windows KDCs (despite the field being specified
as a keyed checksum).
Define a new symbol CKSUMTYPE_SHA1 with the value 14, and use it where
we currently use CKSUMTYPE_NIST_SHA. Continue to allow the value 9
for ABI compatibility. Remove the pkinit_clnt.c workaround as the
value 14 will now work without adjustment.
ticket: 9040 (new)
---
doc/appdev/refs/macros/index.rst | 1 +
src/include/krb5/krb5.hin | 6 ++++++
src/lib/crypto/crypto_tests/t_cksums.c | 2 +-
src/lib/crypto/krb/cksumtypes.c | 6 ++++++
src/lib/gssapi/mechglue/g_saslname.c | 3 +--
src/lib/krb5/os/trace.c | 2 +-
src/plugins/kdb/test/kdb_test.c | 2 +-
src/plugins/preauth/pkinit/pkinit_clnt.c | 11 ++---------
src/plugins/preauth/pkinit/pkinit_srv.c | 4 ++--
9 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 788d094bff..001fb386a7 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -42,6 +42,7 @@ Public
CKSUMTYPE_RSA_MD4_DES.rst
CKSUMTYPE_RSA_MD5.rst
CKSUMTYPE_RSA_MD5_DES.rst
+ CKSUMTYPE_SHA1.rst
ENCTYPE_AES128_CTS_HMAC_SHA1_96.rst
ENCTYPE_AES128_CTS_HMAC_SHA256_128.rst
ENCTYPE_AES256_CTS_HMAC_SHA1_96.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index d2cf1eba2a..a7060aa733 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -449,6 +449,11 @@ typedef struct _krb5_crypto_iov {
#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */
#define ENCTYPE_UNKNOWN 0x01ff
+/*
+ * Historically we used the value 9 for unkeyed SHA-1. RFC 3961 assigns this
+ * value to rsa-md5-des3, which fortunately is unused. For ABI compatibility
+ * we allow either 9 or 14 for SHA-1.
+ */
#define CKSUMTYPE_CRC32 0x0001
#define CKSUMTYPE_RSA_MD4 0x0002
#define CKSUMTYPE_RSA_MD4_DES 0x0003
@@ -459,6 +464,7 @@ typedef struct _krb5_crypto_iov {
#define CKSUMTYPE_RSA_MD5_DES 0x0008
#define CKSUMTYPE_NIST_SHA 0x0009
#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c /* @deprecated removed */
+#define CKSUMTYPE_SHA1 0x000d /**< RFC 3962 */
#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f /**< RFC 3962. Used with
ENCTYPE_AES128_CTS_HMAC_SHA1_96 */
#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 /**< RFC 3962. Used with
diff --git a/src/lib/crypto/crypto_tests/t_cksums.c b/src/lib/crypto/crypto_tests/t_cksums.c
index 84408fb68a..de5ed3a22b 100644
--- a/src/lib/crypto/crypto_tests/t_cksums.c
+++ b/src/lib/crypto/crypto_tests/t_cksums.c
@@ -54,7 +54,7 @@ struct test {
},
{
{ KV5M_DATA, 0, "" },
- CKSUMTYPE_NIST_SHA, 0, 0, { KV5M_DATA, 0, "" },
+ CKSUMTYPE_SHA1, 0, 0, { KV5M_DATA, 0, "" },
{ KV5M_DATA, 20,
"\xDA\x39\xA3\xEE\x5E\x6B\x4B\x0D\x32\x55\xBF\xEF\x95\x60\x18\x90"
"\xAF\xD8\x07\x09" }
diff --git a/src/lib/crypto/krb/cksumtypes.c b/src/lib/crypto/krb/cksumtypes.c
index f5fbe8a2a7..25a3ffd2d2 100644
--- a/src/lib/crypto/krb/cksumtypes.c
+++ b/src/lib/crypto/krb/cksumtypes.c
@@ -46,6 +46,12 @@ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = {
krb5int_unkeyed_checksum, NULL,
20, 20, CKSUM_UNKEYED },
+ { CKSUMTYPE_SHA1,
+ "sha", { 0 }, "SHA-1",
+ NULL, &krb5int_hash_sha1,
+ krb5int_unkeyed_checksum, NULL,
+ 20, 20, CKSUM_UNKEYED },
+
{ CKSUMTYPE_HMAC_MD5_ARCFOUR,
"hmac-md5-rc4", { "hmac-md5-enc", "hmac-md5-earcfour" },
"Microsoft HMAC MD5",
diff --git a/src/lib/gssapi/mechglue/g_saslname.c b/src/lib/gssapi/mechglue/g_saslname.c
index e25f9e0a53..2be0c8a69a 100644
--- a/src/lib/gssapi/mechglue/g_saslname.c
+++ b/src/lib/gssapi/mechglue/g_saslname.c
@@ -58,8 +58,7 @@ oidToSaslName(OM_uint32 *minor, const gss_OID mech,
iov[2].data.length = sizeof(cksumBuf);
iov[2].data.data = (char *)cksumBuf;
- *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_NIST_SHA,
- NULL, 0, iov, 3);
+ *minor = krb5_k_make_checksum_iov(NULL, CKSUMTYPE_SHA1, NULL, 0, iov, 3);
if (*minor != 0)
return GSS_S_FAILURE;
diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
index e9b99f4ca0..abb8a3f21b 100644
--- a/src/lib/krb5/os/trace.c
+++ b/src/lib/krb5/os/trace.c
@@ -93,7 +93,7 @@ hash_bytes(krb5_context context, const void *ptr, size_t len)
krb5_data d = make_data((void *) ptr, len);
char *s = NULL;
- if (krb5_k_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, &d,
+ if (krb5_k_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, &d,
&cksum) != 0)
return NULL;
if (cksum.length >= 2)
diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 95a6062e2a..38d371cb86 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -205,7 +205,7 @@ make_keyblock(krb5_kvno kvno, krb5_enctype etype, int32_t salttype,
(int)salttype, princstr, (int)realm->length, realm->data) < 0)
abort();
d = string2data(hashstr);
- check(krb5_c_make_checksum(NULL, CKSUMTYPE_NIST_SHA, NULL, 0, &d, &cksum));
+ check(krb5_c_make_checksum(NULL, CKSUMTYPE_SHA1, NULL, 0, &d, &cksum));
/* Make the appropriate number of input bytes from the hash result. */
for (pos = 0; pos < keybytes; pos += n) {
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index 9b991ffe05..021e5f0723 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -119,8 +119,8 @@ pa_pkinit_gen_req(krb5_context context,
goto cleanup;
}
- retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
- der_req, &cksum);
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req,
+ &cksum);
if (retval)
goto cleanup;
TRACE_PKINIT_CLIENT_REQ_CHECKSUM(context, &cksum);
@@ -701,13 +701,6 @@ pkinit_as_rep_parse(krb5_context context,
pkiDebug("failed to decode reply_key_pack\n");
goto cleanup;
}
- /*
- * This is hack but Windows sends back SHA1 checksum
- * with checksum type of 14. There is currently no
- * checksum type of 14 defined.
- */
- if (key_pack->asChecksum.checksum_type == 14)
- key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA;
retval = krb5_c_make_checksum(context,
key_pack->asChecksum.checksum_type,
&key_pack->replyKey,
diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c
index 3ae56c0641..3bff456f8f 100644
--- a/src/plugins/preauth/pkinit/pkinit_srv.c
+++ b/src/plugins/preauth/pkinit/pkinit_srv.c
@@ -546,8 +546,8 @@ pkinit_server_verify_padata(krb5_context context,
goto cleanup;
}
der_req = cb->request_body(context, rock);
- retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0,
- der_req, &cksum);
+ retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req,
+ &cksum);
if (retval) {
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
--
2.39.2

View File

@ -1,138 +0,0 @@
From 2e871df888d526a15e9e91807480c15ca4e40618 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 16:26:42 -0400
Subject: [PATCH] Wait indefinitely on KDC TCP connections
When making a KDC or password change request, wait indefinitely
(limited only by request_timeout if set) once a KDC has accepted a TCP
connection.
ticket: 9105 (new)
(cherry picked from commit 6436a3808061da787a43c6810f5f0370cdfb6e36)
---
doc/admin/conf_files/krb5_conf.rst | 2 +-
src/lib/krb5/os/sendto_kdc.c | 50 ++++++++++++++++--------------
2 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 557094f6a2..98fe231813 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -358,7 +358,7 @@ The libdefaults section may contain any of the following relations:
for initial ticket requests. The default value is 0.
**request_timeout**
- (:ref:`duration` string.) Sets the maximum total time for KDC or
+ (:ref:`duration` string.) Sets the maximum total time for KDC and
password change requests. This timeout does not affect the
intervals between requests, so setting a low timeout may result in
fewer requests being attempted and/or some servers not being
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index f57117126e..19b78aba24 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -134,7 +134,6 @@ struct conn_state {
krb5_data callback_buffer;
size_t server_index;
struct conn_state *next;
- time_ms endtime;
krb5_boolean defer;
struct {
const char *uri_path;
@@ -344,15 +343,19 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
struct select_state *out, int *sret)
{
#ifndef USE_POLL
- struct timeval tv;
+ struct timeval tv, *tvp;
#endif
krb5_error_code retval;
time_ms curtime, interval;
- retval = get_curtime_ms(&curtime);
- if (retval != 0)
- return retval;
- interval = (curtime < endtime) ? endtime - curtime : 0;
+ if (endtime != 0) {
+ retval = get_curtime_ms(&curtime);
+ if (retval != 0)
+ return retval;
+ interval = (curtime < endtime) ? endtime - curtime : 0;
+ } else {
+ interval = -1;
+ }
/* We don't need a separate copy of the selstate for poll, but use one for
* consistency with how we use select. */
@@ -361,9 +364,14 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
#ifdef USE_POLL
*sret = poll(out->fds, out->nfds, interval);
#else
- tv.tv_sec = interval / 1000;
- tv.tv_usec = interval % 1000 * 1000;
- *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
+ if (interval != -1) {
+ tv.tv_sec = interval / 1000;
+ tv.tv_usec = interval % 1000 * 1000;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+ *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, tvp);
#endif
return (*sret < 0) ? SOCKET_ERRNO : 0;
@@ -1094,11 +1102,6 @@ service_tcp_connect(krb5_context context, const krb5_data *realm,
}
conn->state = WRITING;
-
- /* Record this connection's timeout for service_fds. */
- if (get_curtime_ms(&conn->endtime) == 0)
- conn->endtime += 10000;
-
return conn->service_write(context, realm, conn, selstate);
}
@@ -1373,19 +1376,18 @@ kill_conn:
return FALSE;
}
-/* Return the maximum of endtime and the endtime fields of all currently active
- * TCP connections. */
-static time_ms
-get_endtime(time_ms endtime, struct conn_state *conns)
+/* Return true if conns contains any states with connected TCP sockets. */
+static krb5_boolean
+any_tcp_connections(struct conn_state *conns)
{
struct conn_state *state;
for (state = conns; state != NULL; state = state->next) {
- if ((state->state == READING || state->state == WRITING) &&
- state->endtime > endtime)
- endtime = state->endtime;
+ if (state->addr.transport != UDP &&
+ (state->state == READING || state->state == WRITING))
+ return TRUE;
}
- return endtime;
+ return FALSE;
}
static krb5_boolean
@@ -1408,9 +1410,9 @@ service_fds(krb5_context context, struct select_state *selstate,
e = 0;
while (selstate->nfds > 0) {
- endtime = get_endtime(interval_end, conns);
+ endtime = any_tcp_connections(conns) ? 0 : interval_end;
/* Don't wait longer than the whole request should last. */
- if (timeout && endtime > timeout)
+ if (timeout && (!endtime || endtime > timeout))
endtime = timeout;
e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
--
2.44.0

View File

@ -1,126 +0,0 @@
From 274464a6faaee694c30ae4d1412a8ab516b1a982 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 20 Sep 2023 16:22:06 +0200
Subject: [PATCH] [downstream] Allow to make AD-SIGNEDPATH optional
MIT krb5 1.20 and newer KDCs do generate a minimal PAC instead of
AD-SIGNEDPATH. As a consequence, an evidence ticket generated by an
older KDC would fail to be processed by a newer KDC for a constrained
delegation request.
This commit modifies this behavior to check the AD-SIGNEDPATH whenever
it is present in a TGS-REQ, but do not require it in case a PAC is
provided AND the KDB plugin supports its verification. This is done
regardless to the fact the constrained delegation request is from a
local realm or a cross-realm.
To enable this mechanism, the KDB plugin must set the
"optional_ab_signedpath" string attribute to "true" for the local TGS
principal.
---
src/include/kdb.h | 1 +
src/kdc/kdc_authdata.c | 65 +++++++++++++++++++++++++++++++++---------
2 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/src/include/kdb.h b/src/include/kdb.h
index c56947ab81..95d07d0195 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -136,6 +136,7 @@
/* String attribute names recognized by krb5 */
#define KRB5_KDB_SK_SESSION_ENCTYPES "session_enctypes"
#define KRB5_KDB_SK_REQUIRE_AUTH "require_auth"
+#define KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH "optional_ad_signedpath"
#if !defined(_WIN32)
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 1ebe872467..c0fcccdf21 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -668,6 +668,13 @@ has_pac(krb5_context context, krb5_authdata **authdata)
return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_WIN2K_PAC);
}
+/* Return true if the AD-SIGNEDPATH is present in authorization data. */
+static krb5_boolean
+has_ad_signedpath(krb5_context context, krb5_authdata **authdata)
+{
+ return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_SIGNTICKET);
+}
+
/* Verify AD-SIGNTICKET authdata if we need to, and insert an AD-SIGNEDPATH
* element if we should. */
static krb5_error_code
@@ -680,24 +687,54 @@ handle_signticket(krb5_context context, unsigned int flags,
{
krb5_error_code ret = 0;
krb5_principal *deleg_path = NULL;
- krb5_boolean signed_path = FALSE;
- krb5_boolean s4u2proxy;
+ krb5_boolean s4u2proxy, adsp_present, adsp_optional, adsp_valid = FALSE;
+ char *str;
s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
- /* For cross-realm the Windows PAC must have been verified, and it
- * fulfills the same role as the signed path. */
- if (req->msg_type == KRB5_TGS_REQ &&
- (!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM) ||
- !has_pac(context, enc_tkt_req->authorization_data))) {
- ret = verify_signedpath(context, local_tgt, local_tgt_key, enc_tkt_req,
- &deleg_path, &signed_path);
- if (ret)
- goto cleanup;
+ if (req->msg_type == KRB5_TGS_REQ) {
+ adsp_present = has_ad_signedpath(context,
+ enc_tkt_req->authorization_data);
+
+ /* In case of constained delegation, based on the value of the
+ * "optional_ad_signedpath" string attribute of the local TGS principal:
+ * - "true": in case AD-SIGNEDPATH is absent, the PAC must be present
+ * - "false" or undefined: AD-SIGNEDPATH must be present
+ */
+ if (s4u2proxy && !adsp_present) {
+ ret = krb5_dbe_get_string(context, local_tgt,
+ KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH,
+ &str);
+ /* TODO: should be using _krb5_conf_boolean(), but os-proto.h is not
+ * available here.
+ */
+ adsp_optional = !ret && str && (strncasecmp(str, "true", 4) == 0
+ || strncasecmp(str, "t", 1) == 0
+ || strncasecmp(str, "yes", 3) == 0
+ || strncasecmp(str, "y", 1) == 0
+ || strncasecmp(str, "1", 1) == 0
+ || strncasecmp(str, "on", 2) == 0);
+
+ if (!adsp_optional ||
+ !has_pac(context, enc_tkt_req->authorization_data)) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
+ }
- if (s4u2proxy && signed_path == FALSE) {
- ret = KRB5KDC_ERR_BADOPTION;
- goto cleanup;
+ /* If AD-SIGNEDPATH is present, verify it */
+ if (adsp_present) {
+ ret = verify_signedpath(context, local_tgt, local_tgt_key,
+ enc_tkt_req, &deleg_path, &adsp_valid);
+ if (ret)
+ goto cleanup;
+
+ /* In case of contrained delegation, if AD-SIGNEDPATH is present, it
+ * has to be valid */
+ if (s4u2proxy && !adsp_valid) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
}
}
--
2.41.0

View File

@ -1,140 +0,0 @@
From 8ded82fb279198f3fa20fb7c836e77290e7bc6f6 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Fri, 24 Mar 2023 16:22:06 +0100
Subject: [PATCH] [downstream] Support PAC full checksum w/o ticket checksum
---
doc/appdev/refs/api/index.rst | 1 +
src/include/krb5/krb5.hin | 38 +++++++++++++++++++++++++++++++++++
src/lib/krb5/krb/pac.c | 10 ++++++++-
src/lib/krb5/krb/pac_sign.c | 17 ++++++++++++++++
src/lib/krb5/libkrb5.exports | 1 +
5 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index d12be47c3c..ff813108bb 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -248,6 +248,7 @@ Rarely used public interfaces
krb5_os_localaddr.rst
krb5_pac_add_buffer.rst
krb5_pac_free.rst
+ krb5_pac_full_sign_compat.rst
krb5_pac_get_buffer.rst
krb5_pac_get_types.rst
krb5_pac_init.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 12a1d441b8..dbe87561c5 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -8392,6 +8392,44 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
krb5_data *data);
+/**
+ * Compatibility function used by IPA if the 1.20 KDB diver API is not
+ * available. It generates PAC signatures, including the extended KDC one when
+ * relevant.
+ *
+ * It is similar to krb5_kdc_sign_ticket(), except it will not generate the
+ * PAC ticket signature, and therefore does not expect encrypted ticket part as
+ * parameter.
+ *
+ * @param [in] context Library context
+ * @param [in] pac PAC handle
+ * @param [in] authtime Expected timestamp
+ * @param [in] client_princ Client principal name (or NULL)
+ * @param [in] server_princ Server principal name
+ * @param [in] server_key Key for server checksum
+ * @param [in] privsvr_key Key for KDC checksum
+ * @param [in] with_realm If true, include the realm of @a client_princ
+ * @param [out] data Signed PAC encoding
+ *
+ * This function signs @a pac using the keys @a server_key and @a privsvr_key
+ * and returns the signed encoding in @a data. @a pac is modified to include
+ * the server and KDC checksum buffers. Use krb5_free_data_contents() to free
+ * @a data when it is no longer needed.
+ *
+ * If @a with_realm is true, the PAC_CLIENT_INFO field of the signed PAC will
+ * include the realm of @a client_princ as well as the name. This flag is
+ * necessary to generate PACs for cross-realm S4U2Self referrals.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_full_sign_compat(krb5_context context, krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key,
+ krb5_boolean with_realm,
+ krb5_data *data);
+
/**
* Sign a PAC, possibly including a ticket signature
*
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 9c00178a28..b7fa064100 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -757,9 +757,17 @@ krb5_pac_verify_ext(krb5_context context,
krb5_boolean with_realm)
{
krb5_error_code ret;
+ krb5_boolean has_full_chksum;
if (server != NULL || privsvr != NULL) {
- ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
+ /* Fail only if full checksum is present and invalid.
+ * Proceed if absent.
+ */
+ ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_FULL_CHECKSUM, NULL);
+ has_full_chksum = ret != ENOENT;
+
+ ret = verify_pac_checksums(context, pac, has_full_chksum, server,
+ privsvr);
if (ret != 0)
return ret;
}
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index 8ea61ac17b..7c3a86d8eb 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -309,6 +309,23 @@ krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
with_realm, FALSE, data);
}
+krb5_error_code KRB5_CALLCONV
+krb5_pac_full_sign_compat(krb5_context context, krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key,
+ krb5_boolean with_realm,
+ krb5_data *data)
+{
+ krb5_boolean is_service_tkt;
+
+ is_service_tkt = k5_pac_should_have_ticket_signature(server_princ);
+ return sign_pac(context, pac, authtime, client_princ, server_key,
+ privsvr_key, with_realm, is_service_tkt, data);
+}
+
/* Add a signature over der_enc_tkt in privsvr to pac. der_enc_tkt should be
* encoded with a dummy PAC authdata element containing a single zero byte. */
static krb5_error_code
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 28784ec67c..ac94e0c236 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -508,6 +508,7 @@ krb5_os_localaddr
krb5_overridekeyname
krb5_pac_add_buffer
krb5_pac_free
+krb5_pac_full_sign_compat
krb5_pac_get_buffer
krb5_pac_get_types
krb5_pac_init
--
2.40.1

View File

@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system
Name: krb5 Name: krb5
Version: 1.18.2 Version: 1.18.2
# for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces) # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
Release: 30%{?dist} Release: 22%{?dist}
# lookaside-cached sources; two downloads and a build artifact # lookaside-cached sources; two downloads and a build artifact
Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz
@ -94,21 +94,6 @@ Patch148: downstream-Fix-dejagnu-unit-tests-directory-name-for-RPC-lib.patch
Patch149: krb5-krad-larger-attrs.patch Patch149: krb5-krad-larger-attrs.patch
Patch150: krb5-krad-remote.patch Patch150: krb5-krad-remote.patch
Patch151: Fix-integer-overflows-in-PAC-parsing.patch Patch151: Fix-integer-overflows-in-PAC-parsing.patch
Patch152: Use-14-instead-of-9-for-unkeyed-SHA-1-checksum.patch
Patch153: Add-PAC-ticket-signature-APIs.patch
Patch154: Factor-out-PAC-checksum-verification.patch
Patch155: Add-PAC-full-checksums.patch
Patch156: downstream-Support-PAC-full-checksum-w-o-ticket-chec.patch
Patch157: downstream-Allow-to-make-AD-SIGNEDPATH-optional.patch
Patch158: End-connection-on-KDC_ERR_SVC_UNAVAILABLE.patch
Patch159: Add-request_timeout-configuration-parameter.patch
Patch160: Wait-indefinitely-on-KDC-TCP-connections.patch
Patch161: Fix-two-unlikely-memory-leaks.patch
Patch162: Fix-defcred-leak-in-krb5-gss_inquire_cred.patch
Patch163: Add-a-simple-DER-support-header.patch
Patch164: Fix-vulnerabilities-in-GSS-message-token-handling.patch
Patch165: Remove-PKINIT-RSA-support.patch
Patch166: Generate-and-verify-message-MACs-in-libkrad.patch
License: MIT License: MIT
URL: http://web.mit.edu/kerberos/www/ URL: http://web.mit.edu/kerberos/www/
@ -719,44 +704,6 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.* %{_libdir}/libkadm5srv_mit.so.*
%changelog %changelog
* Thu Oct 17 2024 Julien Rische <jrische@redhat.com> - 1.18.2-30
- libkrad: implement support for Message-Authenticator (CVE-2024-3596)
Resolves: RHEL-50253
- Remove RSA protocol for PKINIT
Resolves: RHEL-17616
* Mon Jul 01 2024 Julien Rische <jrische@redhat.com> - 1.18.2-29
- CVE-2024-37370 CVE-2024-37371
Fix vulnerabilities in GSS message token handling
Resolves: RHEL-45398 RHEL-45386
* Tue Apr 09 2024 Julien Rische <jrische@redhat.com> - 1.18.2-28
- Fix leak of default credentials in gss_inquire_cred()
Resolves: RHEL-32258
* Thu Mar 21 2024 Julien Rische <jrische@redhat.com> - 1.18.2-27
- Fix memory leak in GSSAPI interface
Resolves: RHEL-27250
- Fix memory leak in PMAP RPC interface
Resolves: RHEL-27244
- Make TCP waiting time configurable
Resolves: RHEL-17131
* Wed Sep 27 2023 Julien Rische <jrische@redhat.com> - 1.18.2-26
- Allow to make AD-SIGNEDPATH optional
Resolves: RHEL-10514
* Thu Jul 06 2023 Julien Rische <jrische@redhat.com> - 1.18.2-25
- Bump release number
* Wed Jul 05 2023 Julien Rische <jrische@redhat.com> - 1.18.2-24
- Remove downloadable source signature file
- Resolves: rhbz#2219654
* Wed May 31 2023 Julien Rische <jrische@redhat.com> - 1.18.2-23
- Support PAC with KDC extended signature and without ticket signature
- Resolves: rhbz#2169477
* Tue Nov 08 2022 Julien Rische <jrische@redhat.com> - 1.18.2-22 * Tue Nov 08 2022 Julien Rische <jrische@redhat.com> - 1.18.2-22
- Fix integer overflows in PAC parsing (CVE-2022-42898) - Fix integer overflows in PAC parsing (CVE-2022-42898)
- Resolves: rhbz#2140968 - Resolves: rhbz#2140968