[31654] in CVS-changelog-for-Kerberos-V5

home help back first fref pref prev next nref lref last post

krb5 commit: Check PAC server checksum verification result

daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Mon Nov 9 11:59:53 2026

From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20261109165948.14B11105814@krbdev.mit.edu>
Date: Mon,  9 Nov 2026 11:59:48 -0500 (EST)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu

https://github.com/krb5/krb5/commit/ffb49b20a30c01ac799e2444542fc9dc748ff8db
commit ffb49b20a30c01ac799e2444542fc9dc748ff8db
Author: Alejandro Pacheco <alepacheco99@gmail.com>
Date:   Fri Oct 16 11:48:06 2026 +0200

    Check PAC server checksum verification result
    
    In verify_pac_checksums(), stop if the server checksum does not
    verify.  In krb5_pac_verify_ext(), validate the client principal
    before the checksums, so that a client mismatch does not leave the PAC
    marked as verified.  Add a test case.
    
    CVE-2026-111423:
    
    In MIT krb5 release 1.21 and later, server applications will
    erroneously treat the PAC within a ticket as verified when the server
    checksum is invalid.
    
    [ghudson@mit.edu: edited commit message]
    
    ticket: 9259 (new)
    tags: pullup
    target_version: 1.22-next

 src/lib/krb5/krb/pac.c   | 14 +++++---
 src/lib/krb5/krb/t_pac.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 9b5c9d418..35b55f349 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -538,6 +538,8 @@ verify_pac_checksums(krb5_context context, const krb5_pac pac,
         /* Verify the server checksum over the PAC copy. */
         ret = verify_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM, server,
                               KRB5_KEYUSAGE_APP_DATA_CKSUM, &copy);
+        if (ret)
+            goto cleanup;
     }
 
     if (privsvr != NULL && expect_full_checksum) {
@@ -711,15 +713,17 @@ krb5_pac_verify_ext(krb5_context context,
 {
     krb5_error_code ret;
 
-    if (server != NULL || privsvr != NULL) {
-        ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
+    /* Validate the client first, so that the PAC is only marked as verified
+     * if all of the requested checks pass. */
+    if (principal != NULL) {
+        ret = k5_pac_validate_client(context, pac, authtime,
+                                     principal, with_realm);
         if (ret)
             return ret;
     }
 
-    if (principal != NULL) {
-        ret = k5_pac_validate_client(context, pac, authtime,
-                                     principal, with_realm);
+    if (server != NULL || privsvr != NULL) {
+        ret = verify_pac_checksums(context, pac, FALSE, server, privsvr);
         if (ret)
             return ret;
     }
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index 81f1642ab..98762333e 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -33,6 +33,7 @@
  */
 
 #include "k5-int.h"
+#include "authdata.h"
 
 #define U(x) (uint8_t *)x
 
@@ -816,6 +817,87 @@ test_pac_ticket_signature(krb5_context context)
     krb5_free_ticket(context, ticket);
 }
 
+/* Check that krb5_pac_verify() marks a PAC as verified only if every
+ * requested check passes. */
+static void
+test_pac_verify_marks_verified(krb5_context context, krb5_const_principal p)
+{
+    krb5_error_code ret;
+    krb5_pac pac;
+    krb5_data data;
+    krb5_principal wrong;
+    uint32_t i;
+
+    ret = krb5_parse_name(context, "wrong@WIN2K3.THINKER.LOCAL", &wrong);
+    if (ret)
+        err(context, ret, "krb5_parse_name");
+
+    ret = krb5_pac_init(context, &pac);
+    if (ret)
+        err(context, ret, "krb5_pac_init");
+    ret = krb5_pac_sign(context, pac, authtime, p, &member_keyblock,
+                        &kdc_keyblock, &data);
+    if (ret)
+        err(context, ret, "krb5_pac_sign");
+    krb5_pac_free(context, pac);
+
+    ret = krb5_pac_parse(context, data.data, data.length, &pac);
+    if (ret)
+        err(context, ret, "krb5_pac_parse");
+    ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock,
+                          &kdc_keyblock);
+    if (ret)
+        err(context, ret, "krb5_pac_verify");
+    if (!pac->verified)
+        err(context, 0, "PAC not marked verified");
+    krb5_pac_free(context, pac);
+
+    /* A wrong server key must fail even if the privsvr checksum is good. */
+    ret = krb5_pac_parse(context, data.data, data.length, &pac);
+    if (ret)
+        err(context, ret, "krb5_pac_parse");
+    ret = krb5_pac_verify(context, pac, authtime, p, &kdc_keyblock,
+                          &kdc_keyblock);
+    if (ret != KRB5KRB_AP_ERR_MODIFIED)
+        err(context, ret, "krb5_pac_verify with wrong server key");
+    if (pac->verified)
+        err(context, 0, "PAC marked verified with wrong server key");
+    krb5_pac_free(context, pac);
+
+    /* A client principal mismatch must not mark the PAC as verified. */
+    ret = krb5_pac_parse(context, data.data, data.length, &pac);
+    if (ret)
+        err(context, ret, "krb5_pac_parse");
+    ret = krb5_pac_verify(context, pac, authtime, wrong, &member_keyblock,
+                          NULL);
+    if (ret != KRB5KRB_AP_WRONG_PRINC)
+        err(context, ret, "krb5_pac_verify with wrong principal");
+    if (pac->verified)
+        err(context, 0, "PAC marked verified with wrong principal");
+    krb5_pac_free(context, pac);
+
+    /* Corrupt the server checksum and verify with only the server key. */
+    ret = krb5_pac_parse(context, data.data, data.length, &pac);
+    if (ret)
+        err(context, ret, "krb5_pac_parse");
+    for (i = 0; i < pac->nbuffers; i++) {
+        if (pac->buffers[i].type == KRB5_PAC_SERVER_CHECKSUM)
+            break;
+    }
+    if (i == pac->nbuffers)
+        err(context, 0, "no server checksum buffer");
+    pac->data.data[pac->buffers[i].offset + PAC_SIGNATURE_DATA_LENGTH] ^= 1;
+    ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, NULL);
+    if (ret != KRB5KRB_AP_ERR_MODIFIED)
+        err(context, ret, "krb5_pac_verify with bad server checksum");
+    if (pac->verified)
+        err(context, 0, "PAC marked verified with bad server checksum");
+    krb5_pac_free(context, pac);
+
+    krb5_free_data_contents(context, &data);
+    krb5_free_principal(context, wrong);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1144,6 +1226,8 @@ main(int argc, char **argv)
 
     krb5_pac_free(context, pac);
 
+    test_pac_verify_marks_verified(context, p);
+
     krb5_free_principal(context, p);
     krb5_free_context(context);
 
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5

home help back first fref pref prev next nref lref last post