The branch, master has been updated
       via  3d4d7f3cb88 third_party:heimdal: import 
lorikeet-heimdal-202602190236
       via  76bf9214239 test:heimdal:pkinit fixes for SHA1-PUKEY calculation
       via  fb16086ba44 s4:kdc:db-glue:tests free principal
       via  580051e5686 s4:kdc:db-glue altSecurityIdentities DN and serial 
reversed
      from  437436832fd selftest: mark "smb2.lease.rename_dir_openfile" as 
flapping

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 3d4d7f3cb88180b205c67a6ca66373fe1fb6851e
Author: Gary Lockyer <[email protected]>
Date:   Thu Feb 19 15:55:36 2026 +1300

    third_party:heimdal: import lorikeet-heimdal-202602190236
    
    commits: 4223f36655031fd13ad3b0bedbc937dd9ba40c8d
         1a9371036d4baa7da50e9260e411eefa6cee0811
         53f5f685a474413d009249ecd7750399737dcd39
    
    Action upstream feedback on KB5014754 changes.
    pkinit:match_name Implement 3.1.5.2.1.3 Explicit Mapping
    
    Calculate hash for KB5014754 SHA1-PUKEY over entire certificate
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=16001
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Jennifer Sutton <[email protected]>
    
    Autobuild-User(master): Jennifer Sutton <[email protected]>
    Autobuild-Date(master): Mon Feb 23 21:18:57 UTC 2026 on atb-devel-224

commit 76bf9214239759169ff4688b035c3f531e0db1bc
Author: Gary Lockyer <[email protected]>
Date:   Fri Feb 20 11:55:59 2026 +1300

    test:heimdal:pkinit fixes for SHA1-PUKEY calculation
    
    The SHA1 hash for KB5014754 SHA1-PUKEY is calculate over the entire
    certificate not just the public key.
    
    BUG https://bugzilla.samba.org/show_bug.cgi?id=16001
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Jennifer Sutton <[email protected]>

commit fb16086ba44ad1943ec6796c8d607ed4c37eb064
Author: Gary Lockyer <[email protected]>
Date:   Thu Feb 19 12:19:35 2026 +1300

    s4:kdc:db-glue:tests free principal
    
    Call krb5_free_principal to quiet valgrind leak reports
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Jennifer Sutton <[email protected]>

commit 580051e5686d9a26d2502eb969f7a80e13519afb
Author: Gary Lockyer <[email protected]>
Date:   Thu Feb 19 12:18:38 2026 +1300

    s4:kdc:db-glue altSecurityIdentities DN and serial reversed
    
    When altSecurityIdentities is set by RSAT / ADUC they store the
    Issuer and Subject DN in last to first order i.e.
           CN=Common Name, O=Organization, C=Country
    Need to reverse that to first to last order, i.e.
           C=Country, O=Organization, CN=Common name
    Which is how they're stored on the X509 certificates.
    
    Also the serial number is stored in reverse order.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=16001
    
    Signed-off-by: Gary Lockyer <[email protected]>
    Reviewed-by: Jennifer Sutton <[email protected]>

-----------------------------------------------------------------------

Summary of changes:
 .../tests/krb5/pkinit_certificate_mapping_tests.py |  66 +++--
 source4/kdc/db-glue.c                              | 150 +++++++++-
 source4/kdc/tests/db-glue-test.c                   | 322 ++++++++++++++++++++-
 third_party/heimdal/kdc/kerberos5.c                |  15 +-
 third_party/heimdal/kdc/pkinit.c                   |  32 +-
 third_party/heimdal/lib/hx509/cert.c               |  25 +-
 6 files changed, 544 insertions(+), 66 deletions(-)


Changeset truncated at 500 lines:

diff --git a/python/samba/tests/krb5/pkinit_certificate_mapping_tests.py 
b/python/samba/tests/krb5/pkinit_certificate_mapping_tests.py
index 7802a0cf4ac..7004478fb68 100755
--- a/python/samba/tests/krb5/pkinit_certificate_mapping_tests.py
+++ b/python/samba/tests/krb5/pkinit_certificate_mapping_tests.py
@@ -178,7 +178,8 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, []
         )
 
-        identity = f"X509:<S>{self._rfc4514_string(certificate.subject)}"
+        subject = self._reverse_dn(self._rfc4514_string(certificate.subject))
+        identity = f"X509:<S>{subject}"
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -206,7 +207,8 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, [], not_before
         )
 
-        identity = f"X509:<S>{self._rfc4514_string(certificate.subject)}"
+        subject = self._reverse_dn(self._rfc4514_string(certificate.subject))
+        identity = f"X509:<S>{subject}"
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -216,12 +218,12 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             expect_error=self.WEAK_EXPECTED_RESULT_BEFORE,
         )
 
-    def test_subject_name_reversed(self):
+    def test_subject_name_not_reversed(self):
         """
         Test PKINIT logon with a user account
         and the weak mapping subject name
         certificate created after the start of the compensation window
-        however the subject name has been reversed.
+        however the subject name has been not reversed.
 
         NOTE:This currently fails, as normalization/canonicalization of
              the subject and issuer name is not currently implemented
@@ -236,10 +238,7 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, []
         )
 
-        # Reverse the order of the subject name components
-        components = self._rfc4514_string(certificate.subject).split(",")
-        components.reverse()
-        subject = ",".join(components)
+        subject = self._rfc4514_string(certificate.subject)
         identity = f"X509:<S>{subject}"
         self._add_altSecurityIdentities(client_creds, identity)
 
@@ -266,11 +265,9 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, []
         )
 
-        identity = (
-            "X509:"
-            f"<I>{self._rfc4514_string(certificate.issuer)}"
-            f"<S>{self._rfc4514_string(certificate.subject)}"
-        )
+        issuer = self._reverse_dn(self._rfc4514_string(certificate.issuer))
+        subject = self._reverse_dn(self._rfc4514_string(certificate.subject))
+        identity = ( f"X509:<I>{issuer}<S>{subject}")
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -298,11 +295,9 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, [], not_before
         )
 
-        identity = (
-            "X509:"
-            f"<I>{self._rfc4514_string(certificate.issuer)}"
-            f"<S>{self._rfc4514_string(certificate.subject)}"
-        )
+        issuer = self._reverse_dn(self._rfc4514_string(certificate.issuer))
+        subject = self._reverse_dn(self._rfc4514_string(certificate.subject))
+        identity = f"X509:<I>{issuer}" f"<S>{subject}"
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -385,11 +380,9 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, []
         )
 
-        serial = hex(certificate.serial_number)[2:]
-        if len(serial) % 2:
-            # Add a leading 0 if needed
-            serial = '0' + serial
-        identity = 
f"X509:<I>{self._rfc4514_string(certificate.issuer)}<SR>{serial}"
+        issuer = self._reverse_dn(self._rfc4514_string(certificate.issuer))
+        serial = self._convert_serial(certificate.serial_number)
+        identity = f"X509:<I>{issuer}<SR>{serial}"
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -441,8 +434,8 @@ class PkInitCertificateMappingTests(KDCBaseTest):
             client_creds, ca_cert, ca_private_key, None, []
         )
 
-        hash = 
x509.SubjectKeyIdentifier.from_public_key(certificate.public_key())
-        identity = f"X509:<SHA1-PUKEY>{hash.digest.hex()}"
+        fingerprint = certificate.fingerprint(hashes.SHA1())
+        identity = f"X509:<SHA1-PUKEY>{fingerprint.hex()}"
         self._add_altSecurityIdentities(client_creds, identity)
 
         self._pkinit_req(
@@ -518,6 +511,29 @@ class PkInitCertificateMappingTests(KDCBaseTest):
         ns = ns.replace("ST=", "S=")
         return ns
 
+    def _reverse_dn(self, dn):
+        """
+        Reverse the components of a DN, as they're stored in reverse
+        order on altSecurityIdentities
+        """
+        components = dn.split(',')
+        components.reverse()
+        return ','.join(components)
+
+    def _convert_serial(self, serial):
+        """
+        convert a certificate serial number into a format suitable
+        for  storage in altSecurityIdentities
+        a string of hex byte values, in reverse order
+        """
+        hex_str = hex(serial)[2:]
+        if len(hex_str) % 2:
+            # Add a leading 0 if needed
+            hex_str = '0' + hex_str
+        hex_bytes = [hex_str[i:i+2] for i in range(0, len(hex_str), 2)]
+        hex_bytes.reverse()
+        return "".join(hex_bytes)
+
     def _add_altSecurityIdentities(self, creds, identity):
         """
         Update the altSecurityIdentities attribute of the account under test
diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
index bdd4509de49..761a27f2ee8 100644
--- a/source4/kdc/db-glue.c
+++ b/source4/kdc/db-glue.c
@@ -1213,6 +1213,31 @@ static krb5_error_code data_blob_to_krb5_data( DATA_BLOB 
*blob, krb5_data *krb5)
        return 0;
 }
 
+/**
+ * @brief reverse the contents of a krb5_data element
+ *
+ * @param[in,out] krb5  krb5_data element to reverse
+ *
+ */
+static void reverse_krb5_data(krb5_data *krb5)
+{
+       size_t start = 0;
+       size_t end = 0;
+       char temp;
+       char *data = NULL;
+
+       if (krb5->length == 0 || krb5->data == NULL) {
+               return;
+       }
+       start = 0;
+       end = krb5->length - 1;
+       data = krb5->data;
+       while (start < end) {
+               temp = data[start];
+               data[start++] = data[end];
+               data[end--] = temp;
+       }
+}
 
 /**
  * @brief Copy the contents of a hex string data blob to a binary
@@ -1261,6 +1286,95 @@ static krb5_error_code db_hex_str_to_krb5_data(
        return 0;
 }
 
+/**
+ * @brief reverse a DN
+ *
+ * Microsoft stores DN's (i.e. issuer, subject) in last to first order, i.e.
+ *     CN=Common Name, O=Organization, C=Country
+ * Need to reverse that to first to last order, i.e.
+ *      C=Country, O=Organization, CN=Common name
+ * Which is how they're stored on the X509 certificates.
+ *
+ * NOTE: Any empty components are removed from the result
+ *
+ * @param[in]  mem_ctx   talloc memory context
+ * @param[in]  name      the distinguished name, in last to first order
+ * @param[out] reversed  the issuer name, in first to last order
+ *                       allocated on mem_ctx
+ *
+ * @return      0 No error
+ *         ENOMEM memory allocation error
+ */
+static krb5_error_code reverse_dn(TALLOC_CTX *mem_ctx,
+                                 DATA_BLOB *name,
+                                 DATA_BLOB *reversed)
+{
+       uint8_t *buffer = NULL;
+       size_t buffer_len = 0;
+       int name_idx = 0;
+       int buffer_idx = 0;
+       int buffer_end = 0;
+       int reversed_idx = 0;
+
+       if (name->length == 0) {
+               return 0;
+       }
+
+       buffer = talloc_zero_size(mem_ctx, name->length);
+       if (buffer == NULL) {
+               return ENOMEM;
+       }
+       *reversed = data_blob_talloc_zero(mem_ctx, name->length);
+       if (reversed->data == NULL) {
+               TALLOC_FREE(buffer);
+               return ENOMEM;
+       }
+
+       buffer_end = name->length - 1;
+       buffer_idx = buffer_end;
+       buffer_len = 0;
+       /*
+        * Iterate backwards through the name,
+        * components are delimited by ',', '\n' and '\r'
+        */
+       for (name_idx = name->length - 1; name_idx >= 0; name_idx--) {
+               uint8_t b = name->data[name_idx];
+               bool is_delimiter = (b == ',' || b == '\n' || b == '\r');
+               if (!is_delimiter) {
+                       /* Add b to the buffer in reverse order */
+                       buffer[buffer_idx--] = name->data[name_idx];
+                       buffer_len++;
+               }
+               if (is_delimiter || name_idx == 0) {
+                       /*
+                        * Reached the start of a component
+                        * copy it into the buffer
+                        */
+                       if (buffer_len > 0 && reversed_idx != 0) {
+                               /*
+                                * if component not empty, and it's not the
+                                * first output a ','
+                                */
+                               reversed->data[reversed_idx] = ',';
+                               reversed_idx++;
+                       }
+                       if (buffer_len > 0) {
+                               /* Copy the buffer contents to the result */
+                               buffer_idx++;
+                               memcpy(&reversed->data[reversed_idx],
+                                      &buffer[buffer_idx],
+                                      buffer_len);
+                               reversed_idx += buffer_len;
+                       }
+                       buffer_idx = buffer_end;
+                       buffer_len = 0;
+               }
+       }
+       reversed->length = reversed_idx;
+       TALLOC_FREE(buffer);
+       return 0;
+}
+
 /*
  * Helper macro to populate the data blob constants used by
  * populate_certificate_mapping and parse_certificate_mapping
@@ -1281,9 +1395,10 @@ static const DATA_BLOB X509_HEADER = 
DATA_BLOB_STRING("X509:");
 /**
  * @brief Populate the certificate mapping from the tag and value
  *
- * @param[in]     tag      the tag i.e. I, S, SKI, .....
- * @param[in]     value    the value associated with the tag
- * @param[in,out] mapping  the mapping to be updated
+ * @param[in] mem_ctx talloc memory context
+ * @param[in] tag     the tag i.e. I, S, SKI, .....
+ * @param[in] value   the value associated with the tag
+ * @param[in,out]     mapping the mapping to be updated
  *
  * @return      0 No error
  *         EINVAL tag or value are invalid
@@ -1293,6 +1408,7 @@ static const DATA_BLOB X509_HEADER = 
DATA_BLOB_STRING("X509:");
  *       sdb_certificate_mapping_free
  */
 static krb5_error_code populate_certificate_mapping(
+       TALLOC_CTX *mem_ctx,
        DATA_BLOB *tag,
        DATA_BLOB *value,
        struct sdb_certificate_mapping *mapping)
@@ -1312,20 +1428,32 @@ static krb5_error_code populate_certificate_mapping(
        }
 
        if (data_blob_cmp(&ISSUER_NAME, tag) == 0) {
+               DATA_BLOB reversed = data_blob_null;
                /* discard any previous value */
                if (mapping->issuer_name.data != NULL) {
                        SAFE_FREE(mapping->issuer_name.data);
                        mapping->issuer_name.length = 0;
                }
-               ret = data_blob_to_krb5_data(value, &mapping->issuer_name);
+               ret = reverse_dn(mem_ctx, value, &reversed);
+               if (ret == 0) {
+                       ret = data_blob_to_krb5_data(&reversed,
+                                                    &mapping->issuer_name);
+               }
+               data_blob_free(&reversed);
 
        } else if (data_blob_cmp(&SUBJECT_NAME, tag) == 0) {
+               DATA_BLOB reversed = data_blob_null;
                /* discard any previous value */
                if (mapping->subject_name.data != NULL) {
                        SAFE_FREE(mapping->subject_name.data);
                        mapping->subject_name.length = 0;
                }
-               ret = data_blob_to_krb5_data(value, &mapping->subject_name);
+               ret = reverse_dn(mem_ctx, value, &reversed);
+               if (ret == 0) {
+                       ret = data_blob_to_krb5_data(&reversed,
+                                                    &mapping->subject_name);
+               }
+               data_blob_free(&reversed);
 
        } else if (data_blob_cmp(&RFC822, tag) == 0) {
                /* discard any previous value */
@@ -1342,6 +1470,9 @@ static krb5_error_code populate_certificate_mapping(
                        mapping->serial_number.length = 0;
                }
                ret = db_hex_str_to_krb5_data(value, &mapping->serial_number);
+               if (ret == 0) {
+                       reverse_krb5_data(&mapping->serial_number);
+               }
 
        } else if (data_blob_cmp(&SUBJECT_KEY_IDENTIFIER, tag) == 0) {
                /* discard any previous value */
@@ -1515,8 +1646,10 @@ static krb5_error_code parse_certificate_mapping(
                case value_state:
                        if (c == '<') {
                                value.data[value.length] = '\0';
-                               ret = populate_certificate_mapping(
-                                       &tag, &value, mapping);
+                               ret = populate_certificate_mapping(tmp_ctx,
+                                                                  &tag,
+                                                                  &value,
+                                                                  mapping);
                                if (ret != 0) {
                                        goto out;
                                }
@@ -1536,8 +1669,7 @@ static krb5_error_code parse_certificate_mapping(
                goto out;
        }
        value.data[value.length] = '\0';
-       ret = populate_certificate_mapping(
-               &tag, &value, mapping);
+       ret = populate_certificate_mapping(tmp_ctx, &tag, &value, mapping);
        if (ret != 0) {
                goto out;
        }
diff --git a/source4/kdc/tests/db-glue-test.c b/source4/kdc/tests/db-glue-test.c
index 326a96314aa..b46281cd0bf 100644
--- a/source4/kdc/tests/db-glue-test.c
+++ b/source4/kdc/tests/db-glue-test.c
@@ -539,6 +539,7 @@ static void empty_message2entry(void **state)
        /* Expect ENOENT as there is no SAMAccountName, among others */
        assert_int_equal(ENOENT, err);
 
+       krb5_free_principal(krb5_ctx, principal);
        sdb_entry_free(&entry);
        TALLOC_FREE(mem_ctx);
 }
@@ -1319,13 +1320,16 @@ static void issuer_name_parse_certificate_mapping(void 
**state)
 
        krb5_error_code err = 0;
        struct sdb_certificate_mapping mapping = {};
-       struct ldb_val *value = get_ldb_string(mem_ctx, "X509:<I>Issuer");
+       struct ldb_val *value = get_ldb_string(
+               mem_ctx, "X509:<I>CN=Intermediate CA SAMBA,C=FR,O=SAMBA");
 
        err = parse_certificate_mapping(value, &mapping);
 
        assert_int_equal(0, err);
-       assert_int_equal(6, mapping.issuer_name.length);
-       assert_memory_equal("Issuer", mapping.issuer_name.data, 6);
+       assert_int_equal(37, mapping.issuer_name.length);
+       assert_memory_equal("O=SAMBA,C=FR,CN=Intermediate CA SAMBA",
+                           mapping.issuer_name.data,
+                           37);
        assert_false(mapping.strong_mapping);
 
        sdb_certificate_mapping_free(&mapping);
@@ -1448,7 +1452,7 @@ static void serial_number_parse_certificate_mapping(void 
**state)
 
        krb5_error_code err = 0;
        struct sdb_certificate_mapping mapping = {};
-       uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
+       uint8_t sn[] = {0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01};
        struct ldb_val *value
                = get_ldb_string(mem_ctx, "X509:<SR>0123456789abcdef");
 
@@ -1475,7 +1479,7 @@ static void 
duplicate_serial_number_parse_certificate_mapping(void **state)
 
        krb5_error_code err = 0;
        struct sdb_certificate_mapping mapping = {};
-       uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
+       uint8_t sn[] = {0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01};
        struct ldb_val *value
                = get_ldb_string(
                        mem_ctx,
@@ -1505,7 +1509,7 @@ static void 
serial_number_and_issuer_name_parse_certificate_mapping(void **state
 
        krb5_error_code err = 0;
        struct sdb_certificate_mapping mapping = {};
-       uint8_t sn[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
+       uint8_t sn[] = {0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01};
        struct ldb_val *value = get_ldb_string(
                mem_ctx, "X509:<SR>0123456789abcdef<I>TheIssuer");
 
@@ -1978,6 +1982,292 @@ static void 
cert_mapping_empty_altSecurityIdentities(void **state)
        TALLOC_FREE(mem_ctx);
 }
 
+/*
+ * Test reverse DN, DN has no comma's
+ *
+ */
+static void reverse_dn_no_comma(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       DATA_BLOB input = data_blob_string_const("No comma");
+       DATA_BLOB expected = data_blob_string_const("No comma");
+       DATA_BLOB result = data_blob_null;
+
+       int ret = reverse_dn(mem_ctx, &input, &result);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(0, data_blob_cmp(&expected, &result));
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test reverse DN, two components
+ *
+ */
+static void reverse_dn_two_components(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       DATA_BLOB input = data_blob_string_const("one,two");
+       DATA_BLOB expected = data_blob_string_const("two,one");
+       DATA_BLOB result = data_blob_null;
+
+       int ret = reverse_dn(mem_ctx, &input, &result);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(0, data_blob_cmp(&expected, &result));
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test reverse DN, three components
+ *
+ */
+static void reverse_dn_three_components(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       DATA_BLOB input = data_blob_string_const("one,two,three");
+       DATA_BLOB expected = data_blob_string_const("three,two,one");
+       DATA_BLOB result = data_blob_null;
+
+       int ret = reverse_dn(mem_ctx, &input, &result);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(0, data_blob_cmp(&expected, &result));
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test reverse DN, three components and trailing comma
+ *
+ */
+static void reverse_dn_trailing_comma(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       DATA_BLOB input = data_blob_string_const("one,two,three,");
+       DATA_BLOB expected = data_blob_string_const("three,two,one");
+       DATA_BLOB result = data_blob_null;
+
+       int ret = reverse_dn(mem_ctx, &input, &result);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(0, data_blob_cmp(&expected, &result));
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test reverse DN, with embedded comma's
+ *
+ */
+static void reverse_dn_embeded_comma(void **state)
+{


-- 
Samba Shared Repository

Reply via email to