From: Gary Lockyer Date: Sun, 7 Sep 2025 22:29:36 +0000 (+1200) Subject: s4:kdc:sdb_to_hdb strong/flexible certificate mappings X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9ffcd38c16c0f44950a7ea547eb19a01590617f7;p=thirdparty%2Fsamba.git s4:kdc:sdb_to_hdb strong/flexible certificate mappings Map the content of sdb_certificate_mappings to the hdb extension HDB_Ext_CertificateMapping Signed-off-by: Gary Lockyer Reviewed-by: Jennifer Sutton Autobuild-User(master): Jennifer Sutton Autobuild-Date(master): Fri Oct 10 02:30:06 UTC 2025 on atb-devel-224 --- diff --git a/selftest/knownfail b/selftest/knownfail index 0e42fbd3b9c..ab2d79d7114 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -338,21 +338,3 @@ # We currently don't send referrals for LDAP modify of non-replicated attrs ^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.* - -# Certificate mapping -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_computer_account_no_mapping\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_issuer_subject_before\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_no_mapping\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_rfc822_before\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_subject_name_before\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_subject_name_reversed\(ad_dc_ntvfs\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_computer_account_no_mapping\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_issuer_subject\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_issuer_subject_before\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_no_mapping\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_rfc822\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_rfc822_before\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_subject_name\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_subject_name_before\(ad_dc_smb1\) -^samba.tests.krb5.pkinit_certificate_mapping_tests..*.test_subject_name_reversed\(ad_dc_smb1\) - diff --git a/source4/kdc/sdb_to_hdb.c b/source4/kdc/sdb_to_hdb.c index 83e5547b8a1..3e89adea9d0 100644 --- a/source4/kdc/sdb_to_hdb.c +++ b/source4/kdc/sdb_to_hdb.c @@ -303,12 +303,183 @@ static int sdb_pub_keys_to_hdb_ext(const struct sdb_pub_keys *s, return 0; } +/** + * @brief Does data contain a value? + * + * @param[in] data pointer to a krb5_data structure + * + * @return TRUE data contains data + * FALSE data is NULL, or contains no data1:w + */ +static krb5_boolean krb5_data_not_empty(const krb5_data *data) { + + if (data == NULL) { + return FALSE; + } + + if (data->length == 0 || data->data == NULL) { + return FALSE; + } + return TRUE; +} + +/** + * @brief Allocate a new krb5_data struct and copy the contents of src + * + * @param[in] The source krb5_data structure + * + * @return A pointer the new krb5_data structure + * OR NULL if unable to allocate memory + * + * @note this allocates a new krb5_data structure and space for the + * contents of src, it should be freed by calling free_krb5_data + */ +static krb5_data *copy_krb5_data(const krb5_data *src) { + + krb5_data *dest = malloc(sizeof(*dest)); + + if (dest == NULL) { + return NULL; + } + + dest->data = malloc(src->length); + if (dest->data == NULL) { + SAFE_FREE(dest); + return NULL; + } + + memcpy(dest->data, src->data, src->length); + dest->length = src->length; + return dest; +} + +/** +* @brief Convert a single sdb_certificate_mapping to +* the corresponding HDB_Ext_CertificateMapping +* +* @param s[in] certificate mappings in sdb form +* @param h[out] the HDB_Ext_Certificate mapping to populate +* +* @return 0 no error +* ENOMEM unable to allocate memory +* +* @note memory is allocated on the HDB_Ext_CertificateMapping +* and it needs to be freed by calling free_HDB_Ext_CertificateMapping +* This also needs to be done in the event of an error. +* +*/ +static int sdb_cert_mapping_to_hdb_key_trust_val( + const struct sdb_certificate_mapping *m, + struct HDB_Ext_CertificateMapping *h) +{ + h->strong_mapping = m->strong_mapping; + if (krb5_data_not_empty(&m->subject_name)) { + h->subject_name = copy_krb5_data(&m->subject_name); + if (h->subject_name == NULL) { + return ENOMEM; + } + } + if (krb5_data_not_empty(&m->issuer_name)) { + h->issuer_name = copy_krb5_data(&m->issuer_name); + if (h->issuer_name == NULL) { + return ENOMEM; + } + } + if (krb5_data_not_empty(&m->serial_number)) { + h->serial_number = copy_krb5_data(&m->serial_number); + if (h->serial_number == NULL) { + return ENOMEM; + } + } + if (krb5_data_not_empty(&m->public_key)) { + h->public_key = copy_krb5_data(&m->public_key); + if (h->public_key == NULL) { + return ENOMEM; + } + } + if (krb5_data_not_empty(&m->rfc822)) { + h->rfc822 = copy_krb5_data(&m->rfc822); + if (h->rfc822 == NULL) { + return ENOMEM; + } + } + if (krb5_data_not_empty(&m->ski)) { + h->ski = copy_krb5_data(&m->ski); + if (h->ski == NULL) { + return ENOMEM; + } + } + return 0; +} + +/** +* @brief Convert the sdb certificate mappings to a HDB_Ext_CertificateMapping +* +* @param s[in] The certificate mappings to be used +* @param h[out] The converted mappings +* +* @return 0 if there are no errors +* ENOMEM unable to allocate memory +* +* @note The HDB_Ext_CertificateMappings may not contain any mappings, this +* can happen when: +* - Enforcement mode is none +* - Or the user is not permitted to use PKINIT when the enforcement +* mode is compatibility or strong. +* +* @note Memory is allocated and will need to be freed with a call +* to free_HDB_Ext_CertificateMappings +* +* @note h must be initialised to zero and have no associated allocated +* memory +* +*/ +static int sdb_certificate_mappings_to_hdb_ext( + const struct sdb_certificate_mappings *m, + HDB_Ext_CertificateMappings *h) +{ + int ret = 0; + size_t i = 0; + + h->enforcement_mode = m->enforcement_mode; + h->valid_certificate_start= m->valid_certificate_start; + + if (m->mappings != NULL) { + h->mappings = malloc + (sizeof(struct HDB_Ext_CertificateMappings_mappings)); + if (h->mappings == NULL) { + return ENOMEM; + } + h->mappings->len = 0; + h->mappings->val =calloc( + m->len,sizeof(struct HDB_Ext_CertificateMapping)); + if (h->mappings->val == NULL) { + SAFE_FREE(h->mappings); + return ENOMEM; + } + for (i = 0; i < m->len; i++) { + ret = sdb_cert_mapping_to_hdb_key_trust_val( + &m->mappings[i], &h->mappings->val[i]); + if (ret != 0) { + free_HDB_Ext_CertificateMapping( + &h->mappings->val[i]); + free_HDB_Ext_CertificateMappings(h); + return ret; + } + h->mappings->len++; + } + } + return ret; +} + + int sdb_entry_to_hdb_entry(krb5_context context, const struct sdb_entry *s, hdb_entry *h) { struct samba_kdc_entry *ske = s->skdc_entry; struct HDB_Ext_KeyTrust kt = {}; + struct HDB_Ext_CertificateMappings cm = {}; unsigned int i; int rc; @@ -475,6 +646,22 @@ int sdb_entry_to_hdb_entry(krb5_context context, goto error; } + rc = sdb_certificate_mappings_to_hdb_ext(&s->mappings, &cm); + if (rc != 0) { + goto error; + } + { + HDB_extension ext = {}; + ext.mandatory = FALSE; + ext.data.element = choice_HDB_extension_data_cert_mappings; + ext.data.u.cert_mappings = cm; + rc = hdb_replace_extension(context, h, &ext); + free_HDB_Ext_CertificateMappings(&cm); + if (rc != 0) { + goto error; + } + } + h->context = ske; if (ske != NULL) { ske->kdc_entry = h; diff --git a/source4/kdc/tests/sdb_to_hdb_test.c b/source4/kdc/tests/sdb_to_hdb_test.c index fc6ff0ff7f1..58281d7926c 100644 --- a/source4/kdc/tests/sdb_to_hdb_test.c +++ b/source4/kdc/tests/sdb_to_hdb_test.c @@ -41,6 +41,12 @@ #include "../sdb_to_hdb.c" #include "hdb_asn1.h" +#include "util/data_blob.h" + +#define assert_empty(a)\ + _assert_int_equal((0), (a)->length, __FILE__, __LINE__);\ + _assert_true(!(cast_ptr_to_largest_integral_type(a)), #a, \ + __FILE__, __LINE__) /* * Test that an empty sdb_pub_key is handled without error and that @@ -163,12 +169,415 @@ static void test_no_leading_bit(void **state) free(out.pub_key.data); } +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val handles an + * empty sdb_certificate_mapping + */ +static void cert_map_empty_sdb_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + assert_false(h.strong_mapping); + assert_null(h.serial_number); + assert_null(h.rfc822); + assert_null(h.ski); + assert_null(h.issuer_name); + assert_null(h.subject_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * subject_name + */ +static void cert_map_subject_name_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB subject_name = data_blob_string_const("DN=SubjectName"); + + m.subject_name.data = subject_name.data; + m.subject_name.length = subject_name.length; + m.strong_mapping = FALSE; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.subject_name); + assert_memory_equal( + subject_name.data, h.subject_name->data, subject_name.length); + assert_int_equal(subject_name.length, h.subject_name->length); + + assert_false(h.strong_mapping); + + assert_null(h.serial_number); + assert_null(h.rfc822); + assert_null(h.ski); + assert_null(h.issuer_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * issuer_name + */ +static void cert_map_issuer_name_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB issuer_name = + data_blob_string_const("DC=local,DC=samba,CN=Things"); + + m.issuer_name.data = issuer_name.data; + m.issuer_name.length = issuer_name.length; + m.strong_mapping = TRUE; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.issuer_name); + assert_memory_equal( + issuer_name.data, h.issuer_name->data, issuer_name.length); + assert_int_equal(issuer_name.length, h.issuer_name->length); + + assert_true(h.strong_mapping); + + assert_null(h.serial_number); + assert_null(h.rfc822); + assert_null(h.ski); + assert_null(h.subject_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * serial_number + */ +static void cert_map_serial_number_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB serial_number = + data_blob_string_const("1234BACXXXXXX"); + + m.serial_number.data = serial_number.data; + m.serial_number.length = serial_number.length; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.serial_number); + assert_memory_equal( + serial_number.data, h.serial_number->data, serial_number.length); + assert_int_equal(serial_number.length, h.serial_number->length); + + assert_false(h.strong_mapping); + + assert_null(h.rfc822); + assert_null(h.ski); + assert_null(h.issuer_name); + assert_null(h.subject_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * public_key + */ +static void cert_map_public_key_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB public_key = + data_blob_string_const("abcdefghij"); + + m.public_key.data = public_key.data; + m.public_key.length = public_key.length; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.public_key); + assert_memory_equal( + public_key.data, h.public_key->data, public_key.length); + assert_int_equal(public_key.length, h.public_key->length); + + assert_false(h.strong_mapping); + + assert_null(h.serial_number); + assert_null(h.rfc822); + assert_null(h.ski); + assert_null(h.issuer_name); + assert_null(h.subject_name); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * RFC822 (email address) + */ +static void cert_map_RFC822_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB rfc822 = + data_blob_string_const("test@test.org"); + + m.rfc822.data = rfc822.data; + m.rfc822.length = rfc822.length; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.rfc822); + assert_memory_equal( + rfc822.data, h.rfc822->data, rfc822.length); + assert_int_equal(rfc822.length, h.rfc822->length); + + assert_false(h.strong_mapping); + + assert_null(h.serial_number); + assert_null(h.ski); + assert_null(h.issuer_name); + assert_null(h.subject_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps the + * SKI (Subject Key Identifier) + */ +static void cert_map_ski_mapping(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB ski = + data_blob_string_const("cdef123455"); + + m.ski.data = ski.data; + m.ski.length = ski.length; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_non_null(h.ski); + assert_memory_equal( + ski.data, h.ski->data, ski.length); + assert_int_equal(ski.length, h.ski->length); + + assert_false(h.strong_mapping); + + assert_null(h.serial_number); + assert_null(h.rfc822); + assert_null(h.issuer_name); + assert_null(h.subject_name); + assert_null(h.public_key); + + free_HDB_Ext_CertificateMapping(&h); +} + +/* + * Ensure that sdb_cert_mapping_to_hdb_key_trust_val correctly maps + * all values if provided + */ +static void cert_map_all(void **state) +{ + struct sdb_certificate_mapping m = {}; + struct HDB_Ext_CertificateMapping h = {}; + int ret = 0; + DATA_BLOB issuer_name = + data_blob_string_const("DC=local,DC=samba,CN=Things"); + DATA_BLOB subject_name = data_blob_string_const("DN=SubjectName"); + DATA_BLOB serial_number = + data_blob_string_const("1234BACXXXXXX"); + DATA_BLOB public_key = + data_blob_string_const("abcdefghij"); + DATA_BLOB rfc822 = + data_blob_string_const("test@test.org"); + DATA_BLOB ski = + data_blob_string_const("cdef123455"); + + m.ski.data = ski.data; + m.ski.length = ski.length; + + m.rfc822.data = rfc822.data; + m.rfc822.length = rfc822.length; + + m.public_key.data = public_key.data; + m.public_key.length = public_key.length; + + m.serial_number.data = serial_number.data; + m.serial_number.length = serial_number.length; + + m.subject_name.data = subject_name.data; + m.subject_name.length = subject_name.length; + + m.issuer_name.data = issuer_name.data; + m.issuer_name.length = issuer_name.length; + + ret = sdb_cert_mapping_to_hdb_key_trust_val(&m, &h); + + assert_int_equal(0, ret); + + assert_memory_equal( + issuer_name.data, h.issuer_name->data, issuer_name.length); + assert_int_equal(issuer_name.length, h.issuer_name->length); + + assert_memory_equal( + serial_number.data, h.serial_number->data, serial_number.length); + assert_int_equal(serial_number.length, h.serial_number->length); + + assert_memory_equal( + rfc822.data, h.rfc822->data, rfc822.length); + assert_int_equal(rfc822.length, h.rfc822->length); + + assert_memory_equal( + ski.data, h.ski->data, ski.length); + assert_int_equal(ski.length, h.ski->length); + + assert_memory_equal( + subject_name.data, h.subject_name->data, subject_name.length); + assert_int_equal(subject_name.length, h.subject_name->length); + + assert_memory_equal( + public_key.data, h.public_key->data, public_key.length); + assert_int_equal(public_key.length, h.public_key->length); + + free_HDB_Ext_CertificateMapping(&h); +} + +static void cert_mappings_empty_sdb(void **state) +{ + struct sdb_certificate_mappings m = {}; + HDB_Ext_CertificateMappings h = {}; + int ret = 0; + + ret = sdb_certificate_mappings_to_hdb_ext(&m, &h); + assert_int_equal(0, ret); + + assert_null(h.mappings); + assert_int_equal(0, h.valid_certificate_start); + assert_int_equal(0, h.enforcement_mode); + + free_HDB_Ext_CertificateMappings(&h); +} + + +static void cert_mappings_one_mapping(void **state) +{ + struct sdb_certificate_mappings m = {}; + struct sdb_certificate_mapping cm = {}; + HDB_Ext_CertificateMappings h = {}; + int ret = 0; + + DATA_BLOB ski = + data_blob_string_const("cdef123455"); + + cm.ski.data = ski.data; + cm.ski.length = ski.length; + cm.strong_mapping = TRUE; + + m.enforcement_mode = 2; + m.valid_certificate_start = 100; + + m.len = 1; + m.mappings = &cm; + + + ret = sdb_certificate_mappings_to_hdb_ext(&m, &h); + assert_int_equal(0, ret); + + assert_non_null(h.mappings); + assert_int_equal(100, h.valid_certificate_start); + assert_int_equal(2, h.enforcement_mode); + + free_HDB_Ext_CertificateMappings(&h); +} + +static void cert_mappings_two_mappings(void **state) +{ + struct sdb_certificate_mappings m = {}; + struct sdb_certificate_mapping cm1 = {}; + struct sdb_certificate_mapping cm2 = {}; + HDB_Ext_CertificateMappings h = {}; + struct sdb_certificate_mapping mappings[] = {cm1, cm2}; + int ret = 0; + + DATA_BLOB ski = + data_blob_string_const("cdef123455"); + DATA_BLOB rfc822 = + data_blob_string_const("test@test.org"); + + cm1.ski.data = ski.data; + cm1.ski.length = ski.length; + cm1.strong_mapping = TRUE; + + cm2.rfc822.data = rfc822.data; + cm2.rfc822.length = rfc822.length; + cm2.strong_mapping = FALSE; + + m.enforcement_mode = 2; + m.valid_certificate_start = 100; + + m.len = 2; + m.mappings = mappings; + + + ret = sdb_certificate_mappings_to_hdb_ext(&m, &h); + assert_int_equal(0, ret); + + assert_non_null(h.mappings); + assert_int_equal(100, h.valid_certificate_start); + assert_int_equal(2, h.enforcement_mode); + + free_HDB_Ext_CertificateMappings(&h); +} int main(int argc, const char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(empty_key), cmocka_unit_test(test_leading_bit_handling), cmocka_unit_test(test_no_leading_bit), + cmocka_unit_test(cert_map_empty_sdb_mapping), + cmocka_unit_test(cert_map_subject_name_mapping), + cmocka_unit_test(cert_map_issuer_name_mapping), + cmocka_unit_test(cert_map_serial_number_mapping), + cmocka_unit_test(cert_map_public_key_mapping), + cmocka_unit_test(cert_map_RFC822_mapping), + cmocka_unit_test(cert_map_ski_mapping), + cmocka_unit_test(cert_map_all), + cmocka_unit_test(cert_mappings_empty_sdb), + cmocka_unit_test(cert_mappings_one_mapping), + cmocka_unit_test(cert_mappings_two_mappings), }; cmocka_set_message_output(CM_OUTPUT_SUBUNIT);