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