]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4:kdc:sdb_to_hdb key trust support
authorGary Lockyer <gary@catalyst.net.nz>
Mon, 11 Aug 2025 00:00:03 +0000 (12:00 +1200)
committerDouglas Bagnall <dbagnall@samba.org>
Tue, 16 Sep 2025 23:23:42 +0000 (23:23 +0000)
Convert key trust public keys contained in the clients sdb records, and add
to the HDB_Ext_KeyTrust extension on the clients HDB record

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
Autobuild-Date(master): Tue Sep 16 23:23:42 UTC 2025 on atb-devel-224

selftest/knownfail
selftest/tests.py
source4/kdc/sdb_to_hdb.c
source4/kdc/tests/sdb_to_hdb_test.c [new file with mode: 0644]
source4/kdc/wscript_build

index 63637c774a261e208b9f5e68102de7e4abd4d211..ab2d79d7114edb6ee29eafd4ef5e4ce84921471b 100644 (file)
 
 # We currently don't send referrals for LDAP modify of non-replicated attrs
 ^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
-^samba.tests.krb5.key_trust_tests.*
index c00acb434e38b6f563a4a7261818e9f4de278bc6..f466dbb9feeff22756643e04d33d41543c743e81 100644 (file)
@@ -636,6 +636,8 @@ plantestsuite("samba.unittests.cmdline", "none",
 if have_heimdal_support:
     plantestsuite("samba.source4.kdc.tests.db_glue", "none",
                 [os.path.join(bindir(), "default/source4/kdc/test_db_glue")])
+    plantestsuite("samba.source4.kdc.tests.sdb_to_hdb", "none",
+                [os.path.join(bindir(), "default/source4/kdc/test_sdb_to_hdb")])
 
 # Run the Rust cargo tests
 planpythontestsuite("none", "samba.tests.rust")
index 4c4f2a576266560465a774f23d24005555e391ce..ef02ce5be2a8e299a796802865d22a6f165e5952 100644 (file)
 */
 
 #include "includes.h"
+#include "hdb_asn1.h"
 #include <hdb.h>
+#include <krb5.h>
+#include <hx_locl.h>
+#include "rfc2459_asn1.h"
 #include "sdb.h"
 #include "sdb_hdb.h"
 #include "lib/krb5_wrap/krb5_samba.h"
@@ -193,11 +197,118 @@ static int sdb_event_to_Event(krb5_context context,
        return 0;
 }
 
+
+/**
+* @brief Convert a sdb_pub_key to a HDB_Ext_KeyTrust_val
+*
+* @param s[in]     A public key in sdb
+* @param h[in,out] The HDb_Ext_KeyTrust_val to populate
+*
+* @return 0 no error
+*         >0 an error occurred
+*/
+static int sdb_pub_key_to_hdb_key_trust_val(const struct sdb_pub_key *s,
+                                           struct HDB_Ext_KeyTrust_val *h)
+{
+       krb5_error_code ret;
+       SubjectPublicKeyInfo spki = {};
+       RSAPublicKey rsa = {};
+       krb5_data buf = {};
+       AlgorithmIdentifier alg = {};
+       HEIM_ANY parameters = {};
+       uint8_t none[] = {0x05, 0x00};
+       size_t size = 0;
+
+       rsa.publicExponent.length = s->exponent.length;
+       rsa.publicExponent.data = s->exponent.data;
+       rsa.publicExponent.negative = 0;
+
+       rsa.modulus.length = s->modulus.length;
+       rsa.modulus.data = s->modulus.data;
+       rsa.modulus.negative = 0;
+
+       ASN1_MALLOC_ENCODE(
+               RSAPublicKey, buf.data , buf.length, &rsa, &size, ret);
+       if (ret != 0) {
+               goto out;
+       }
+
+       spki.subjectPublicKey.data = buf.data;
+       /*
+        * The public key length is in bits, but the buffer len is in bytes
+        * so need to convert it to bits.
+        */
+       spki.subjectPublicKey.length = buf.length * 8;
+
+       ret = der_copy_oid(ASN1_OID_ID_PKCS1_RSAENCRYPTION, &alg.algorithm);
+       if (ret != 0) {
+               goto out1;
+       }
+       parameters.data = &none;
+       parameters.length = sizeof(none);
+       alg.parameters = &parameters;
+       spki.algorithm = alg;
+
+       /*
+        * This will be freed when sdb_pub_keys_to_hdb calls
+        * free_HDB_Ext_KeyTrust
+        */
+       ASN1_MALLOC_ENCODE(SubjectPublicKeyInfo,
+                   h->pub_key.data,
+                   h->pub_key.length,
+                   &spki,
+                   &size,
+                   ret);
+       if (ret != 0) {
+               goto out2;
+       }
+
+out2:
+       der_free_oid(&alg.algorithm);
+out1:
+        der_free_octet_string(&buf);
+out:
+       return ret;
+}
+
+/**
+* @brief Convert any public keys for key trust authentication.
+*
+* @param s[in]  The public keys that can be used for authentication
+* @param h[out] The converted public keys
+* @return  0 if there are no errors
+*         >0 an error occurred
+*/
+static int sdb_pub_keys_to_hdb_ext(const struct sdb_pub_keys *s,
+                                  HDB_Ext_KeyTrust *h)
+{
+       int ret, i;
+       *h = (struct HDB_Ext_KeyTrust) {};
+
+       if (s->keys != NULL) {
+               h->val = malloc(s->len * sizeof(heim_octet_string));
+               if (h->val == NULL) {
+                       return ENOMEM;
+               }
+               for (i = 0; i < s->len; i++) {
+                       ret = sdb_pub_key_to_hdb_key_trust_val(
+                               &s->keys[i], &h->val[i]);
+                       if (ret != 0) {
+                               free_HDB_Ext_KeyTrust(h);
+                               return ret;
+                       }
+                       h->len++;
+               }
+       }
+       return 0;
+}
+
 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 = {};
        unsigned int i;
        int rc;
 
@@ -348,6 +459,22 @@ int sdb_entry_to_hdb_entry(krb5_context context,
                }
        }
 
+       rc = sdb_pub_keys_to_hdb_ext(&s->pub_keys, &kt);
+       if (rc != 0) {
+               goto error;
+       }
+       if (kt.len > 0) {
+               HDB_extension ext = {};
+               ext.mandatory = FALSE;
+               ext.data.element = choice_HDB_extension_data_key_trust;
+               ext.data.u.key_trust = kt;
+               rc = hdb_replace_extension(context, h, &ext);
+               free_HDB_Ext_KeyTrust(&kt);
+               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
new file mode 100644 (file)
index 0000000..fc6ff0f
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Unit tests for source4/kdc/sdb_to_hdb.c
+ *
+ * Copyright (C) Gary Lockyer 2025
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "../../../third_party/cmocka/cmocka.h"
+
+#include "../sdb_to_hdb.c"
+#include "hdb_asn1.h"
+
+/*
+ * Test that an empty sdb_pub_key is handled without error and that
+ * the expected value is generated
+ */
+static void empty_key(void **state)
+{
+       uint8_t empty_key[] = {
+               0x30, 0x1a, /* Sequence 26 bytes, 2 elements */
+               0x30, 0x0d, /* Sequence 13 bytes, 2 elements */
+               0x06, 0x09, /* OID 9 bytes, 1.2.840.113549.1.1.1 */
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+               0x05, 0x00, /* Null */
+               0x03, 0x09, 0x00, /* Bit string 9 bytes, zero unused bits */
+               0x30, 0x06, /* Sequence 6 bytes, 2 elements */
+               0x02, 0x01, 0x00, /* Integer 1 byte, value 0, Modulus */
+               0x02, 0x01, 0x00, /* Integer 1 byte, value 0, Exponent */
+       };
+       struct sdb_pub_key in = {};
+       struct HDB_Ext_KeyTrust_val out = {};
+       int ret = 0;
+
+       ret = sdb_pub_key_to_hdb_key_trust_val(&in, &out);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(sizeof(empty_key), out.pub_key.length);
+       assert_memory_equal(empty_key,
+                           out.pub_key.data,
+                           sizeof(empty_key));
+
+       free(out.pub_key.data);
+}
+
+/*
+ * Test that modulus and exponent with the leading bit set,
+ * are handled correctly
+ */
+static void test_leading_bit_handling(void **state)
+{
+       uint8_t expected_key[] = {
+               0x30, 0x1c, /* Sequence 281 bytes, 2 elements */
+               0x30, 0x0d, /* Sequence 13 bytes, 2 elements */
+               0x06, 0x09, /* OID 9 bytes, 1.2.840.113549.1.1.1 */
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+               0x05, 0x00, /* Null */
+               0x03, 0x0b, 0x00, /* Bit string 11 bytes, zero unused bits */
+               0x30, 0x08, /* Sequence 8 bytes, 2 elements */
+               0x02, 0x02, 0x00, 0x80, /* Integer 2 byte, value 0, Modulus */
+               0x02, 0x02, 0x00, 0x81, /* Integer 2 byte, value 0, Exponent */
+               /*
+                * As the modulus and exponent have the leading bit set
+                * They should get encoded with a leading 0 byte
+                */
+       };
+       uint8_t modulus[] = {0x80};
+       uint8_t exponent[] = {0x81};
+       struct sdb_pub_key in = {};
+       struct HDB_Ext_KeyTrust_val out = {};
+
+       int ret = 0;
+       in.bit_size = 8;
+
+       in.modulus.data = &modulus;
+       in.modulus.length = sizeof(modulus);
+
+       in.exponent.data = &exponent;
+       in.exponent.length = sizeof(exponent);
+
+
+       ret = sdb_pub_key_to_hdb_key_trust_val(&in, &out);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(sizeof(expected_key), out.pub_key.length);
+       assert_memory_equal(expected_key,
+                           out.pub_key.data,
+                           sizeof(expected_key));
+
+       free(out.pub_key.data);
+}
+/*
+ * Test that modulus and exponent with the leading bit not set,
+ * are handled correctly
+ */
+static void test_no_leading_bit(void **state)
+{
+       uint8_t expected_key[] = {
+               0x30, 0x1c, /* Sequence 281 bytes, 2 elements */
+               0x30, 0x0d, /* Sequence 13 bytes, 2 elements */
+               0x06, 0x09, /* OID 9 bytes, 1.2.840.113549.1.1.1 */
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+               0x05, 0x00, /* Null */
+               0x03, 0x0b, 0x00, /* Bit string 11 bytes, zero unused bits */
+               0x30, 0x08, /* Sequence 8 bytes, 2 elements */
+               0x02, 0x02, 0x78, 0x9a, /* Integer 2 byte, value 0, Modulus */
+               0x02, 0x02, 0x65, 0x43, /* Integer 2 byte, value 0, Exponent */
+       };
+       uint8_t modulus[] = {0x78, 0x9a};
+       uint8_t exponent[] = {0x65, 0x43};
+       struct sdb_pub_key in = {};
+       struct HDB_Ext_KeyTrust_val out = {};
+
+       int ret = 0;
+       in.bit_size = 8;
+
+       in.modulus.data = &modulus;
+       in.modulus.length = sizeof(modulus);
+
+       in.exponent.data = &exponent;
+       in.exponent.length = sizeof(exponent);
+
+
+       ret = sdb_pub_key_to_hdb_key_trust_val(&in, &out);
+
+       assert_int_equal(0, ret);
+       assert_int_equal(sizeof(expected_key), out.pub_key.length);
+       assert_memory_equal(expected_key,
+                           out.pub_key.data,
+                           sizeof(expected_key));
+
+       free(out.pub_key.data);
+}
+
+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_set_message_output(CM_OUTPUT_SUBUNIT);
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
index f3b3ae79dfa4ae97043d17ab463d2a9e3a7cfc78..3017070e6eea0dc9b34f6d15d2b050d72bcec54f 100644 (file)
@@ -37,6 +37,17 @@ if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'):
                     for_selftest=True,
                     )
 
+    bld.SAMBA_BINARY('test_sdb_to_hdb',
+                    source='tests/sdb_to_hdb_test.c',
+                    deps='''
+                        sdb_hdb
+                        HDB_SAMBA4
+                        cmocka
+                        talloc
+                        ''',
+                    for_selftest=True,
+                    )
+
 if bld.CONFIG_GET('SAMBA_USES_MITKDC'):
     bld.SAMBA_MODULE('service_kdc',
                      source='kdc-service-mit.c',