]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4:kdc Support for key trust authentication
authorGary Lockyer <gary@catalyst.net.nz>
Fri, 25 Jul 2025 01:22:27 +0000 (13:22 +1200)
committerDouglas Bagnall <dbagnall@samba.org>
Tue, 29 Jul 2025 05:31:10 +0000 (05:31 +0000)
Extract the public kes from msDS-KeyCredentialLink and populate the sdb
structure.  These values can then be passed to Kergeros to allow key
trust authentication.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
Autobuild-Date(master): Tue Jul 29 05:31:10 UTC 2025 on atb-devel-224

selftest/tests.py
source4/auth/sam.c
source4/kdc/db-glue.c
source4/kdc/tests/db-glue-test.c [new file with mode: 0644]
source4/kdc/wscript_build

index 1f4d8e5cfd6a09f80b2d76f7b01a7f78ad9d8490..49fe5e6426bf16290334cd4ac7a5541647b15453 100644 (file)
@@ -625,6 +625,9 @@ plantestsuite("samba.unittests.claim_conversion", "none",
               [os.path.join(bindir(), "test_claim_conversion")])
 plantestsuite("samba.unittests.cmdline", "none",
               [os.path.join(bindir(), "test_cmdline")])
+if have_heimdal_support:
+    plantestsuite("samba.source4.kdc.tests.db_glue", "none",
+                [os.path.join(bindir(), "default/source4/kdc/test_db_glue")])
 
 # Run the Rust cargo tests
 planpythontestsuite("none", "samba.tests.rust")
index 32475de87809f323da3fa3e7e930b291ada2f445..99c60977d5392d4e30f863e7b8b63e2d502676e0 100644 (file)
@@ -67,7 +67,9 @@
        /* Required for Group Managed Service Accounts. */ \
        "msDS-ManagedPasswordId",               \
        "msDS-ManagedPasswordInterval",         \
-       "whenCreated"
+       "whenCreated",                          \
+       /* Required for Key Trust authentication */\
+       "msDS-CustomKeyInformation"
 
 #define AUTHN_POLICY_ATTRS                     \
        /* Required for authentication policies / silos */ \
index 5bfc41b66811ce7c50642fc490b0881d95d47608..f3e8bd9073c10e321577eab2e9726b98fedeacd1 100644 (file)
@@ -45,6 +45,9 @@
 #include "kdc/pac-glue.h"
 #include "librpc/gen_ndr/ndr_irpc_c.h"
 #include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_keycredlink.h"
+#include "talloc.h"
+#include "util/debug.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_KERBEROS
@@ -1184,6 +1187,237 @@ static krb5_error_code samba_kdc_get_entry_principal(
 
        return code;
 }
+/**
+ * @brief Extract the KeyMaterial from a KEYCREDENTIALLINK_BLOB
+ *        as a KeyMaterialInternal structure.
+ *
+ * The following validation is performed on the KEYCREDENTIALLINK_BLOB:
+ *   1) can be unpacked
+ *   2) has one and only one KeyUsage
+ *   3) that KeyUsage is KEY_USAGE_NGC
+ *   4) has one and only one KeyMaterial
+ *   5) that KeyMaterial can be unpacked
+ *
+ * @param[in] mem_ctx  talloc memory context, that will own pub_key
+ * @param[in] value    ldb value containing the raw KEYCREDENTIALLINK_BLOB
+ * @param[out] pub_key the extracted public key
+ *
+ * @return 0 No error pub_key will be valid
+ *         EINVAL KeyMaterial was invalid, pub_key will be NULL
+ *         ENOMEM memory allocation error pub_key will be NULL
+ */
+static krb5_error_code unpack_key_credential_link_blob(
+       TALLOC_CTX *mem_ctx,
+       struct ldb_val *value,
+       struct KeyMaterialInternal **pub_key)
+{
+
+       krb5_error_code ret = 0;
+       enum ndr_err_code ndr_err = NDR_ERR_SUCCESS;
+       struct KEYCREDENTIALLINK_BLOB blob = {};
+       DATA_BLOB key_material = data_blob_null;
+       int key_usage = 0;
+
+       size_t i = 0;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               *pub_key = NULL;
+               return ENOMEM;
+       }
+       *pub_key = NULL;
+
+       /* Unpack the KEYCREDENTIALLINK_BLOB */
+       ndr_err = ndr_pull_struct_blob_all(
+               value,
+               tmp_ctx,
+               &blob,
+               (ndr_pull_flags_fn_t)ndr_pull_KEYCREDENTIALLINK_BLOB);
+       if (ndr_err != NDR_ERR_SUCCESS) {
+               DBG_WARNING("Unable to unpack KEYCREDENTIALLINK_BLOB, "
+                           "ndr_err_code (%d)",
+                           ndr_err);
+               ret = EINVAL;
+               goto out;
+       }
+
+       /* No need to check the version as that's checked in the ndr_pull */
+
+       /* get the KeyMaterial and KeyUsage */
+       for (i = 0; i < blob.count; i++) {
+               struct KEYCREDENTIALLINK_ENTRY *e = NULL;
+               e = &blob.entries[i];
+               switch (e->identifier) {
+               case KeyMaterial:
+                       if (key_material.data != NULL) {
+                               DBG_WARNING("Duplicate KeyMaterial");
+                               ret = EINVAL;
+                               goto out;
+                       }
+                       key_material = e->value.keyMaterial;
+                       break;
+               case KeyUsage:
+                       if (key_usage != 0) {
+                               DBG_WARNING("Duplicate KeyUsage");
+                               ret = EINVAL;
+                               goto out;
+                       }
+                       if (e->value.keyUsage != KEY_USAGE_NGC) {
+                               DBG_WARNING("Invalid KeyUsage (%d)",
+                                           e->value.keyUsage);
+                               ret = EINVAL;
+                               goto out;
+                       }
+                       key_usage = e->value.keyUsage;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (key_usage == 0) {
+               DBG_WARNING("No KeyUsage");
+               ret = EINVAL;
+               goto out;
+       }
+       if (key_material.data == NULL) {
+               DBG_WARNING("No KeyMaterial");
+               ret = EINVAL;
+               goto out;
+       }
+
+       /* Unpack the KeyMaterial */
+       *pub_key = talloc(mem_ctx, struct KeyMaterialInternal);
+       if (*pub_key == NULL) {
+               DBG_WARNING("Unable to allocate KeyMaterialInternal");
+               ret = ENOMEM;
+               goto out;
+       }
+       ndr_err = ndr_pull_struct_blob_all(
+               &key_material,
+               mem_ctx,
+               *pub_key,
+               (ndr_pull_flags_fn_t)ndr_pull_KeyMaterialInternal);
+       if (ndr_err != NDR_ERR_SUCCESS) {
+               DBG_WARNING("Unable to unpack KeyMaterialInternal, "
+                           "ndr_err_code (%d)",
+                           ndr_err);
+               ret = EINVAL;
+               TALLOC_FREE(*pub_key);
+               goto out;
+       }
+
+out:
+       TALLOC_FREE(tmp_ctx);
+       return ret;
+}
+
+/**
+ * @brief extract the public keys for key trust authentication from the
+ *        ldb message.
+ *
+ * Processes the KEYCREDENTIALLINK_BLOBs in the msDS-KeyCredentialLink
+ * attribute, extracting all the valid public keys.
+ *
+ * @param mem_ctx[in] talloc memory context
+ * @param msg[in]     ldb message containing the public keys
+ * @param entry[out]  entry will be updated with the keys
+ *
+ * @note Invalid KEYCREDENTIALLINK_BLOB's will be ignored
+ * @note There may be no public keys, indicating that key trust logon is
+ *       not enabled for this object.
+ *
+ * @return 0  No error, and there are zero or more valid keys in entry
+ *         >0 Errors detected
+ */
+static krb5_error_code get_key_trust_public_keys(TALLOC_CTX *mem_ctx,
+                                                struct ldb_message *msg,
+                                                struct sdb_entry *entry)
+{
+       krb5_error_code ret = 0;
+       struct ldb_message_element *el = NULL;
+       size_t i = 0;
+       struct sdb_pub_keys pub_keys = {};
+
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       el = ldb_msg_find_element(msg, "msDS-KeyCredentialLink");
+       if (el == NULL || el->num_values == 0) {
+               /* No msDS-KeyCredentialLink nothing to do */
+               goto out;
+       }
+
+       for (i = 0; i < el->num_values; i++) {
+               krb5_error_code r = 0;
+               struct KeyMaterialInternal *kmi = NULL;
+               struct sdb_pub_key pub_key = {};
+               r = unpack_key_credential_link_blob(tmp_ctx,
+                                                   &el->values[i],
+                                                   &kmi);
+               if (r == 0) {
+                       /* Get bit size*/
+                       pub_key.bit_size = kmi->bit_size;
+
+                       /* get Exponent */
+                       pub_key.exponent.length = kmi->exponent.length;
+                       pub_key.exponent.data = malloc(kmi->exponent.length);
+                       if (pub_key.exponent.data == NULL) {
+                               goto pub_keys_oom;
+                       }
+                       memcpy(pub_key.exponent.data,
+                              kmi->exponent.data,
+                              kmi->exponent.length);
+
+                       /* get Modulus */
+                       pub_key.modulus.length = kmi->modulus.length;
+                       pub_key.modulus.data = malloc(kmi->modulus.length);
+                       if (pub_key.modulus.data == NULL) {
+                                       SAFE_FREE(pub_key.exponent.data);
+                                       goto pub_keys_oom;
+                       }
+                       memcpy(pub_key.modulus.data,
+                              kmi->modulus.data,
+                              kmi->modulus.length);
+
+                       /* Add public key to the list of public keys */
+                       if (pub_keys.keys == NULL) {
+                               pub_keys.len = 0;
+                               pub_keys.keys = calloc(1, sizeof(pub_key));
+                               if (pub_keys.keys == NULL) {
+                                       SAFE_FREE(pub_key.exponent.data);
+                                       SAFE_FREE(pub_key.modulus.data);
+                                       goto pub_keys_oom;
+                               }
+                       } else {
+                               pub_keys.keys = reallocarray(pub_keys.keys,
+                                                       pub_keys.len + 1,
+                                                       sizeof(pub_key));
+                               if (pub_keys.keys == NULL) {
+                                       SAFE_FREE(pub_key.exponent.data);
+                                       SAFE_FREE(pub_key.modulus.data);
+                                       goto pub_keys_oom;
+                               }
+                       }
+                       pub_keys.keys[pub_keys.len] = pub_key;
+                       pub_keys.len++;
+               }
+               TALLOC_FREE(kmi);
+       }
+       if (pub_keys.len != 0) {
+               entry->pub_keys = pub_keys;
+       }
+       goto out;
+
+pub_keys_oom:
+       sdb_pub_keys_free(&pub_keys);
+       ret = ENOMEM;
+
+out:
+       TALLOC_FREE(tmp_ctx);
+       return ret;
+}
 
 /*
  * Construct an hdb_entry from a directory entry.
@@ -1924,6 +2158,15 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
        p->client_policy = talloc_steal(p, authn_client_policy);
        p->server_policy = talloc_steal(p, authn_server_policy);
 
+       /*
+        * get_key_trust_public_keys will return ENOENT if there are no
+        * public keys loaded for an object, this is not an error condition
+        */
+       ret = get_key_trust_public_keys(tmp_ctx, msg, entry);
+       if (ret != 0 && ret != ENOENT) {
+               goto out;
+       }
+
        talloc_steal(kdc_db_ctx, p);
 
 out:
diff --git a/source4/kdc/tests/db-glue-test.c b/source4/kdc/tests/db-glue-test.c
new file mode 100644 (file)
index 0000000..4d5f719
--- /dev/null
@@ -0,0 +1,1054 @@
+/*
+ * Unit tests for source4/kdc/db-glue.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 "../db-glue.c"
+#include "krb5-protos.h"
+#include "ldb.h"
+#include "samdb/samdb.h"
+#include "talloc.h"
+#include "util/data_blob.h"
+#include "util/debug.h"
+
+/******************************************************************************
+ * Over ridden functions
+ ******************************************************************************
+ */
+int dsdb_functional_level(struct ldb_context *ldb)
+{
+       return 1;
+}
+
+/******************************************************************************
+ * Test helper functions
+ *****************************************************************************/
+static void add_msDS_KeyCredentialLink(struct ldb_message *msg,
+                                      size_t size,
+                                      uint8_t *data)
+{
+       struct ldb_message_element *el = NULL;
+       DATA_BLOB key_cred_val = {.length = size, .data = data};
+
+       /* Add the data to msDS-KeyCredentialLink */
+       ldb_msg_add_value(msg, "msDS-KeyCredentialLink", &key_cred_val, &el);
+}
+
+static struct ldb_message *create_ldb_message(TALLOC_CTX *mem_ctx)
+{
+       DATA_BLOB sid_val = data_blob_null;
+       struct dom_sid sid = {};
+       struct ldb_message_element *el = NULL;
+       unsigned int flags = UF_NORMAL_ACCOUNT;
+       DATA_BLOB flags_val = data_blob_null;
+
+       struct ldb_message *msg = ldb_msg_new(mem_ctx);
+       ldb_msg_add_string(msg, "sAMAccountName", "testUser");
+
+       string_to_sid(&sid, "S-1-5-21-4231626423-2410014848-2360679739-513");
+       ndr_push_struct_blob(&sid_val,
+                            mem_ctx,
+                            &sid,
+                            (ndr_push_flags_fn_t)ndr_push_dom_sid);
+       ldb_msg_add_value(msg, "objectSid", &sid_val, &el);
+
+       flags_val = data_blob_talloc_zero(mem_ctx, sizeof(flags));
+       memcpy(flags_val.data, &flags, sizeof(flags));
+       ldb_msg_add_value(msg,
+                         "msDS-User-Account-Control-Computed",
+                         &flags_val,
+                         &el);
+       return msg;
+}
+
+static struct samba_kdc_db_context *create_kdc_db_ctx(TALLOC_CTX *mem_ctx)
+{
+
+       struct samba_kdc_db_context *kdc_db_ctx = NULL;
+
+       /* set up an lp_ctx */
+       struct loadparm_context *lp_ctx = loadparm_init(mem_ctx);
+       assert_non_null(lp_ctx);
+
+       /* Set up the kdc_db_context */
+       kdc_db_ctx = talloc_zero(mem_ctx, struct samba_kdc_db_context);
+       kdc_db_ctx->lp_ctx = lp_ctx;
+
+       kdc_db_ctx->current_nttime_ull = talloc_zero(kdc_db_ctx,
+                                                    unsigned long long);
+
+       return kdc_db_ctx;
+}
+
+static krb5_principal get_principal(TALLOC_CTX *mem_ctx,
+                                   krb5_context krb5_ctx,
+                                   const char *name)
+{
+       krb5_principal principal = NULL;
+       char *principle_name = NULL;
+
+       principle_name = talloc_strdup(mem_ctx, "atestuser@test.samba.org");
+       assert_int_equal(
+               0, smb_krb5_parse_name(krb5_ctx, principle_name, &principal));
+       assert_non_null(principal);
+       return principal;
+}
+
+/******************************************************************************
+ * Test Data
+ *****************************************************************************/
+/* clang-format off */
+/* clang format tends to mangle the layout so turn it of for the test data   */
+
+static uint8_t BCRYPT_KEY_CREDENTIAL_LINK[] = {
+       0x00, 0x02, 0x00, 0x00,         /* version 2                         */
+       0x1C, 0x01, 0x03,               /* Key Material                      */
+       'R', 'S', 'A', '1',             /* RSA public key                    */
+       0x00, 0x08, 0x00, 0x00,         /* bit length, 2048                  */
+       0x04, 0x00, 0x00, 0x00,         /* public exponent length            */
+       0x00, 0x01, 0x00, 0x00,         /* modulus length, 256               */
+       0x00, 0x00, 0x00, 0x00,         /* prime one length                  */
+       0x00, 0x00, 0x00, 0x00,         /* prime two length                  */
+       0x01, 0x02, 0x03, 0x04,         /* public exponent                   */
+                                       /* modulus                           */
+       0x9A, 0x9E, 0xF6, 0x5D, 0xE2, 0x92, 0xD6, 0xD0,
+       0xE5, 0xB3, 0xC4, 0x35, 0xB1, 0x5B, 0x36, 0xF3,
+       0x9E, 0x83, 0x7B, 0xA9, 0x34, 0xAB, 0xD9, 0x67,
+       0xE1, 0x1C, 0x75, 0x43, 0xE5, 0xB6, 0x48, 0x9B,
+       0x6E, 0xCD, 0x8D, 0xFC, 0x30, 0x5F, 0x4C, 0xB6,
+       0x8E, 0xA0, 0x69, 0xA4, 0x07, 0x21, 0xE7, 0xD7,
+       0xA1, 0x74, 0x4A, 0x29, 0xBC, 0xC9, 0x5D, 0x78,
+       0x70, 0xC4, 0x3B, 0xE4, 0x20, 0x54, 0xBC, 0xD0,
+       0xAA, 0xFF, 0x21, 0x44, 0x54, 0xFC, 0x09, 0x08,
+       0x2A, 0xCC, 0xDE, 0x44, 0x68, 0xED, 0x9F, 0xB2,
+       0x3E, 0xF7, 0xED, 0x82, 0xD7, 0x2D, 0x28, 0x74,
+       0x42, 0x2A, 0x2F, 0x55, 0xA2, 0xE0, 0xDA, 0x45,
+       0xF1, 0x08, 0xC0, 0x83, 0x8C, 0x95, 0x81, 0x6D,
+       0x92, 0xCC, 0xA8, 0x5D, 0xA4, 0xB8, 0x06, 0x8C,
+       0x76, 0xF5, 0x68, 0x94, 0xE7, 0x60, 0xE6, 0xF4,
+       0xEE, 0x40, 0x50, 0x28, 0x6C, 0x82, 0x47, 0x89,
+       0x07, 0xE7, 0xBC, 0x0D, 0x56, 0x5D, 0xDA, 0x86,
+       0x57, 0xE2, 0xCE, 0xD3, 0x19, 0xA1, 0xA2, 0x7F,
+       0x56, 0xF8, 0x99, 0x8B, 0x4A, 0x71, 0x32, 0x6A,
+       0x57, 0x3B, 0xF9, 0xE5, 0x2D, 0x39, 0x35, 0x6E,
+       0x13, 0x3E, 0x84, 0xDC, 0x5C, 0x96, 0xE1, 0x75,
+       0x38, 0xC3, 0xAA, 0x23, 0x5B, 0x68, 0xBE, 0x41,
+       0x52, 0x49, 0x72, 0x7A, 0xF6, 0x2A, 0x8F, 0xC5,
+       0xC5, 0xE0, 0x6C, 0xDB, 0x99, 0xD1, 0xA8, 0x84,
+       0x5F, 0x70, 0x21, 0x87, 0x2E, 0xA0, 0xD2, 0x68,
+       0xD3, 0x76, 0x5C, 0x9E, 0xD4, 0x9C, 0xB5, 0xE1,
+       0x72, 0x9D, 0x17, 0x8B, 0xDC, 0x11, 0x55, 0x09,
+       0x90, 0x8D, 0x96, 0xF3, 0x68, 0x34, 0xDD, 0x50,
+       0x63, 0xAC, 0x4A, 0x74, 0xA7, 0xAF, 0x0D, 0xDC,
+       0x15, 0x06, 0x07, 0xD7, 0x5A, 0xB3, 0x86, 0x1A,
+       0x54, 0x96, 0xE0, 0xFA, 0x66, 0x25, 0x31, 0xF5,
+       0xB4, 0xC7, 0x97, 0xC7, 0x7C, 0x70, 0x94, 0xE3,
+       0x01, 0x00, 0x04, 0x01,         /* key usage                         */
+       0x01, 0x00, 0x05, 0x00,         /* key source                        */
+};
+
+static uint8_t BCRYPT_MODULUS[] = {
+       0x9A, 0x9E, 0xF6, 0x5D, 0xE2, 0x92, 0xD6, 0xD0,
+       0xE5, 0xB3, 0xC4, 0x35, 0xB1, 0x5B, 0x36, 0xF3,
+       0x9E, 0x83, 0x7B, 0xA9, 0x34, 0xAB, 0xD9, 0x67,
+       0xE1, 0x1C, 0x75, 0x43, 0xE5, 0xB6, 0x48, 0x9B,
+       0x6E, 0xCD, 0x8D, 0xFC, 0x30, 0x5F, 0x4C, 0xB6,
+       0x8E, 0xA0, 0x69, 0xA4, 0x07, 0x21, 0xE7, 0xD7,
+       0xA1, 0x74, 0x4A, 0x29, 0xBC, 0xC9, 0x5D, 0x78,
+       0x70, 0xC4, 0x3B, 0xE4, 0x20, 0x54, 0xBC, 0xD0,
+       0xAA, 0xFF, 0x21, 0x44, 0x54, 0xFC, 0x09, 0x08,
+       0x2A, 0xCC, 0xDE, 0x44, 0x68, 0xED, 0x9F, 0xB2,
+       0x3E, 0xF7, 0xED, 0x82, 0xD7, 0x2D, 0x28, 0x74,
+       0x42, 0x2A, 0x2F, 0x55, 0xA2, 0xE0, 0xDA, 0x45,
+       0xF1, 0x08, 0xC0, 0x83, 0x8C, 0x95, 0x81, 0x6D,
+       0x92, 0xCC, 0xA8, 0x5D, 0xA4, 0xB8, 0x06, 0x8C,
+       0x76, 0xF5, 0x68, 0x94, 0xE7, 0x60, 0xE6, 0xF4,
+       0xEE, 0x40, 0x50, 0x28, 0x6C, 0x82, 0x47, 0x89,
+       0x07, 0xE7, 0xBC, 0x0D, 0x56, 0x5D, 0xDA, 0x86,
+       0x57, 0xE2, 0xCE, 0xD3, 0x19, 0xA1, 0xA2, 0x7F,
+       0x56, 0xF8, 0x99, 0x8B, 0x4A, 0x71, 0x32, 0x6A,
+       0x57, 0x3B, 0xF9, 0xE5, 0x2D, 0x39, 0x35, 0x6E,
+       0x13, 0x3E, 0x84, 0xDC, 0x5C, 0x96, 0xE1, 0x75,
+       0x38, 0xC3, 0xAA, 0x23, 0x5B, 0x68, 0xBE, 0x41,
+       0x52, 0x49, 0x72, 0x7A, 0xF6, 0x2A, 0x8F, 0xC5,
+       0xC5, 0xE0, 0x6C, 0xDB, 0x99, 0xD1, 0xA8, 0x84,
+       0x5F, 0x70, 0x21, 0x87, 0x2E, 0xA0, 0xD2, 0x68,
+       0xD3, 0x76, 0x5C, 0x9E, 0xD4, 0x9C, 0xB5, 0xE1,
+       0x72, 0x9D, 0x17, 0x8B, 0xDC, 0x11, 0x55, 0x09,
+       0x90, 0x8D, 0x96, 0xF3, 0x68, 0x34, 0xDD, 0x50,
+       0x63, 0xAC, 0x4A, 0x74, 0xA7, 0xAF, 0x0D, 0xDC,
+       0x15, 0x06, 0x07, 0xD7, 0x5A, 0xB3, 0x86, 0x1A,
+       0x54, 0x96, 0xE0, 0xFA, 0x66, 0x25, 0x31, 0xF5,
+       0xB4, 0xC7, 0x97, 0xC7, 0x7C, 0x70, 0x94, 0xE3,
+};
+
+static uint8_t BCRYPT_EXPONENT[] = {
+       0x01, 0x02, 0x03, 0x04,
+};
+
+static uint8_t TPM_KEY_CREDENTIAL_LINK[] = {
+       0x00, 0x02, 0x00, 0x00,         /* version 2                         */
+       0x50, 0x01, 0x03,               /* Key Material                      */
+       0x50, 0x43, 0x50, 0x4D,         /* Magic value PCPM                  */
+       0x2E, 0x00, 0x00, 0x00,         /* header length                     */
+       0x02, 0x00, 0x00, 0x00,         /* type TPM 2.0                      */
+       0x00, 0x00, 0x00, 0x00,         /* flags                             */
+       0x00, 0x00, 0x00, 0x00,         /* public_length                     */
+       0x00, 0x00, 0x00, 0x00,         /* private length                    */
+       0x00, 0x00, 0x00, 0x00,         /* migration public length           */
+       0x00, 0x00, 0x00, 0x00,         /* migration private length          */
+       0x00, 0x00, 0x00, 0x00,         /* policy digest list length         */
+       0x00, 0x00, 0x00, 0x00,         /* PCR binding length                */
+       0x00, 0x00, 0x00, 0x00,         /* PCR digest length                 */
+       0x00, 0x00, 0x00, 0x00,         /* Encrypted secret length           */
+       0x00, 0x00, 0x00, 0x00,         /* TPM 1.2 hostage blob length       */
+       0x00, 0x00,                     /* PCRA Algorithm Id                 */
+       0x18, 0x01,                     /* size 280 bytes                    */
+       0x00, 0x01,                     /* type                              */
+       0x00, 0x0B,                     /* hash algorithm                    */
+       0x00, 0x05, 0x24, 0x72,         /* attributes                        */
+       0x00, 0x00,                     /* auth policy                       */
+       0x00, 0x10,                     /* algorithm                         */
+       0x00, 0x14,                     /* scheme                            */
+       0x00, 0x0B,                     /*hash algorithm                     */
+       0x08, 0x00,                     /* key bits                          */
+       0x01, 0x02, 0x03, 0x04,         /* exponent                          */
+       0x01, 0x00,                     /* modulus size 256 bytes            */
+       0x9A, 0x9E, 0xF6, 0x5D, 0xE2, 0x92, 0xD6, 0xD0,
+       0xE5, 0xB3, 0xC4, 0x35, 0xB1, 0x5B, 0x36, 0xF3,
+       0x9E, 0x83, 0x7B, 0xA9, 0x34, 0xAB, 0xD9, 0x67,
+       0xE1, 0x1C, 0x75, 0x43, 0xE5, 0xB6, 0x48, 0x9B,
+       0x6E, 0xCD, 0x8D, 0xFC, 0x30, 0x5F, 0x4C, 0xB6,
+       0x8E, 0xA0, 0x69, 0xA4, 0x07, 0x21, 0xE7, 0xD7,
+       0xA1, 0x74, 0x4A, 0x29, 0xBC, 0xC9, 0x5D, 0x78,
+       0x70, 0xC4, 0x3B, 0xE4, 0x20, 0x54, 0xBC, 0xD0,
+       0xAA, 0xFF, 0x21, 0x44, 0x54, 0xFC, 0x09, 0x08,
+       0x2A, 0xCC, 0xDE, 0x44, 0x68, 0xED, 0x9F, 0xB2,
+       0x3E, 0xF7, 0xED, 0x82, 0xD7, 0x2D, 0x28, 0x74,
+       0x42, 0x2A, 0x2F, 0x55, 0xA2, 0xE0, 0xDA, 0x45,
+       0xF1, 0x08, 0xC0, 0x83, 0x8C, 0x95, 0x81, 0x6D,
+       0x92, 0xCC, 0xA8, 0x5D, 0xA4, 0xB8, 0x06, 0x8C,
+       0x76, 0xF5, 0x68, 0x94, 0xE7, 0x60, 0xE6, 0xF4,
+       0xEE, 0x40, 0x50, 0x28, 0x6C, 0x82, 0x47, 0x89,
+       0x07, 0xE7, 0xBC, 0x0D, 0x56, 0x5D, 0xDA, 0x86,
+       0x57, 0xE2, 0xCE, 0xD3, 0x19, 0xA1, 0xA2, 0x7F,
+       0x56, 0xF8, 0x99, 0x8B, 0x4A, 0x71, 0x32, 0x6A,
+       0x57, 0x3B, 0xF9, 0xE5, 0x2D, 0x39, 0x35, 0x6E,
+       0x13, 0x3E, 0x84, 0xDC, 0x5C, 0x96, 0xE1, 0x75,
+       0x38, 0xC3, 0xAA, 0x23, 0x5B, 0x68, 0xBE, 0x41,
+       0x52, 0x49, 0x72, 0x7A, 0xF6, 0x2A, 0x8F, 0xC5,
+       0xC5, 0xE0, 0x6C, 0xDB, 0x99, 0xD1, 0xA8, 0x84,
+       0x5F, 0x70, 0x21, 0x87, 0x2E, 0xA0, 0xD2, 0x68,
+       0xD3, 0x76, 0x5C, 0x9E, 0xD4, 0x9C, 0xB5, 0xE1,
+       0x72, 0x9D, 0x17, 0x8B, 0xDC, 0x11, 0x55, 0x09,
+       0x90, 0x8D, 0x96, 0xF3, 0x68, 0x34, 0xDD, 0x50,
+       0x63, 0xAC, 0x4A, 0x74, 0xA7, 0xAF, 0x0D, 0xDC,
+       0x15, 0x06, 0x07, 0xD7, 0x5A, 0xB3, 0x86, 0x1A,
+       0x54, 0x96, 0xE0, 0xFA, 0x66, 0x25, 0x31, 0xF5,
+       0xB4, 0xC7, 0x97, 0xC7, 0x7C, 0x70, 0x94, 0xE3,
+       0x01, 0x00, 0x04, 0x01,         /* key usage                         */
+       0x01, 0x00, 0x05, 0x00,         /* key source                        */
+};
+
+static uint8_t TPM_MODULUS[] = {
+       0x9A, 0x9E, 0xF6, 0x5D, 0xE2, 0x92, 0xD6, 0xD0,
+       0xE5, 0xB3, 0xC4, 0x35, 0xB1, 0x5B, 0x36, 0xF3,
+       0x9E, 0x83, 0x7B, 0xA9, 0x34, 0xAB, 0xD9, 0x67,
+       0xE1, 0x1C, 0x75, 0x43, 0xE5, 0xB6, 0x48, 0x9B,
+       0x6E, 0xCD, 0x8D, 0xFC, 0x30, 0x5F, 0x4C, 0xB6,
+       0x8E, 0xA0, 0x69, 0xA4, 0x07, 0x21, 0xE7, 0xD7,
+       0xA1, 0x74, 0x4A, 0x29, 0xBC, 0xC9, 0x5D, 0x78,
+       0x70, 0xC4, 0x3B, 0xE4, 0x20, 0x54, 0xBC, 0xD0,
+       0xAA, 0xFF, 0x21, 0x44, 0x54, 0xFC, 0x09, 0x08,
+       0x2A, 0xCC, 0xDE, 0x44, 0x68, 0xED, 0x9F, 0xB2,
+       0x3E, 0xF7, 0xED, 0x82, 0xD7, 0x2D, 0x28, 0x74,
+       0x42, 0x2A, 0x2F, 0x55, 0xA2, 0xE0, 0xDA, 0x45,
+       0xF1, 0x08, 0xC0, 0x83, 0x8C, 0x95, 0x81, 0x6D,
+       0x92, 0xCC, 0xA8, 0x5D, 0xA4, 0xB8, 0x06, 0x8C,
+       0x76, 0xF5, 0x68, 0x94, 0xE7, 0x60, 0xE6, 0xF4,
+       0xEE, 0x40, 0x50, 0x28, 0x6C, 0x82, 0x47, 0x89,
+       0x07, 0xE7, 0xBC, 0x0D, 0x56, 0x5D, 0xDA, 0x86,
+       0x57, 0xE2, 0xCE, 0xD3, 0x19, 0xA1, 0xA2, 0x7F,
+       0x56, 0xF8, 0x99, 0x8B, 0x4A, 0x71, 0x32, 0x6A,
+       0x57, 0x3B, 0xF9, 0xE5, 0x2D, 0x39, 0x35, 0x6E,
+       0x13, 0x3E, 0x84, 0xDC, 0x5C, 0x96, 0xE1, 0x75,
+       0x38, 0xC3, 0xAA, 0x23, 0x5B, 0x68, 0xBE, 0x41,
+       0x52, 0x49, 0x72, 0x7A, 0xF6, 0x2A, 0x8F, 0xC5,
+       0xC5, 0xE0, 0x6C, 0xDB, 0x99, 0xD1, 0xA8, 0x84,
+       0x5F, 0x70, 0x21, 0x87, 0x2E, 0xA0, 0xD2, 0x68,
+       0xD3, 0x76, 0x5C, 0x9E, 0xD4, 0x9C, 0xB5, 0xE1,
+       0x72, 0x9D, 0x17, 0x8B, 0xDC, 0x11, 0x55, 0x09,
+       0x90, 0x8D, 0x96, 0xF3, 0x68, 0x34, 0xDD, 0x50,
+       0x63, 0xAC, 0x4A, 0x74, 0xA7, 0xAF, 0x0D, 0xDC,
+       0x15, 0x06, 0x07, 0xD7, 0x5A, 0xB3, 0x86, 0x1A,
+       0x54, 0x96, 0xE0, 0xFA, 0x66, 0x25, 0x31, 0xF5,
+       0xB4, 0xC7, 0x97, 0xC7, 0x7C, 0x70, 0x94, 0xE3,
+};
+
+static uint8_t TPM_EXPONENT[] = {
+       0x01, 0x02, 0x03, 0x04,
+};
+
+static uint8_t DER_KEY_CREDENTIAL_LINK[] = {
+       0x00, 0x02, 0x00, 0x00,         /* version 2                         */
+       0x26, 0x01, 0x03,               /* Key Material                      */
+       0x30, 0x82, 0x01, 0x22,         /* Sequence 290 bytes, 2 elements    */
+       0x30, 0x0d,                     /* Sequence 13 bytes, 2 elem         */
+       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, 0x82, 0x01, 0x0f, 0x00,   /* Bit string, 2160 bits, 0 unused   */
+       0x30, 0x82, 0x01, 0x0a,         /* Sequence 266 bytes, 2 elements    */
+       0x02, 0x82, 0x01, 0x01, 0x00,   /* Integer 2048 bit, 257 bytes       */
+                                       /* MODULUS is 257 bytes as it's most */
+                                       /* significant byte is 0b10111101    */
+                                       /* which has bit 8 set, which        */
+                                       /* DER Integer encoding uses as the  */
+                                       /* sign bit, so need the leading 00  */
+                                       /* byte to prevent the value being   */
+                                       /* interpreted as a negative integer */
+       0xbd, 0xae, 0x45, 0x8b, 0x17, 0xcd, 0x3e, 0x62,
+       0x71, 0x66, 0x67, 0x7f, 0xa2, 0x46, 0xc4, 0x47,
+       0x78, 0x79, 0xf2, 0x8c, 0xd4, 0x2e, 0x0c, 0xa0,
+       0x90, 0x1c, 0xf6, 0x33, 0xe1, 0x94, 0x89, 0xb9,
+       0x44, 0x15, 0xe3, 0x29, 0xe7, 0xb6, 0x91, 0xca,
+       0xab, 0x7e, 0xc6, 0x25, 0x60, 0xe3, 0x7a, 0xc4,
+       0x09, 0x97, 0x8a, 0x4e, 0x79, 0xcb, 0xa6, 0x1f,
+       0xf8, 0x29, 0x3f, 0x8a, 0x0d, 0x45, 0x58, 0x9b,
+       0x0e, 0xbf, 0xa5, 0xfa, 0x1c, 0xa2, 0x5e, 0x31,
+       0xa1, 0xe7, 0xba, 0x7e, 0x17, 0x62, 0x03, 0x79,
+       0xc0, 0x07, 0x48, 0x11, 0x8b, 0xfa, 0x58, 0x17,
+       0x56, 0x1a, 0xa1, 0x62, 0xd2, 0x02, 0x02, 0x2a,
+       0x64, 0x8d, 0x8c, 0x53, 0xfa, 0x28, 0x7c, 0x89,
+       0x18, 0x34, 0x70, 0x64, 0xa7, 0x08, 0x10, 0xc9,
+       0x3b, 0x1b, 0x2c, 0x23, 0x88, 0x9c, 0x35, 0x50,
+       0x78, 0xd1, 0x89, 0x33, 0xce, 0x82, 0xb2, 0x84,
+       0xf4, 0x99, 0xd8, 0x3e, 0x67, 0x11, 0xa1, 0x5c,
+       0x1a, 0x64, 0xb8, 0x6a, 0x3e, 0xe6, 0x95, 0x2e,
+       0x47, 0x33, 0x51, 0x7e, 0xb7, 0x62, 0xb4, 0x08,
+       0x2c, 0xc4, 0x87, 0x52, 0x00, 0x9e, 0x28, 0xf2,
+       0x16, 0x9f, 0x1b, 0xc1, 0x3a, 0x93, 0x6d, 0xa3,
+       0x38, 0x9b, 0x34, 0x39, 0x88, 0x85, 0xea, 0x38,
+       0xad, 0xc2, 0x2b, 0xc3, 0x7c, 0x15, 0xcb, 0x8f,
+       0x15, 0x37, 0xed, 0x88, 0x62, 0x5c, 0x34, 0x75,
+       0x6f, 0xb0, 0xeb, 0x5c, 0x42, 0x6a, 0xcd, 0x03,
+       0xcc, 0x49, 0xbc, 0xb4, 0x78, 0x14, 0xe1, 0x5e,
+       0x98, 0x83, 0x6f, 0xe7, 0x19, 0xa8, 0x43, 0xcb,
+       0xca, 0x07, 0xb2, 0x4e, 0xa4, 0x36, 0x60, 0x95,
+       0xac, 0x6f, 0xe2, 0x1d, 0x3a, 0x33, 0xf6, 0x0e,
+       0x94, 0xae, 0xfb, 0xd2, 0xac, 0x9f, 0xc2, 0x9f,
+       0x5b, 0x77, 0x8f, 0x46, 0x3c, 0xee, 0x13, 0x27,
+       0x19, 0x8e, 0x68, 0x71, 0x27, 0x3f, 0x50, 0x59,
+       0x02, 0x03, 0x01, 0x00, 0x01,   /* INTEGER, 3 bytes EXPONENT         */
+       0x01, 0x00, 0x04, 0x01,         /* key usage                         */
+       0x01, 0x00, 0x05, 0x00,         /* key source                        */
+};
+
+static uint8_t DER_MODULUS[] = {
+       0xbd, 0xae, 0x45, 0x8b, 0x17, 0xcd, 0x3e, 0x62,
+       0x71, 0x66, 0x67, 0x7f, 0xa2, 0x46, 0xc4, 0x47,
+       0x78, 0x79, 0xf2, 0x8c, 0xd4, 0x2e, 0x0c, 0xa0,
+       0x90, 0x1c, 0xf6, 0x33, 0xe1, 0x94, 0x89, 0xb9,
+       0x44, 0x15, 0xe3, 0x29, 0xe7, 0xb6, 0x91, 0xca,
+       0xab, 0x7e, 0xc6, 0x25, 0x60, 0xe3, 0x7a, 0xc4,
+       0x09, 0x97, 0x8a, 0x4e, 0x79, 0xcb, 0xa6, 0x1f,
+       0xf8, 0x29, 0x3f, 0x8a, 0x0d, 0x45, 0x58, 0x9b,
+       0x0e, 0xbf, 0xa5, 0xfa, 0x1c, 0xa2, 0x5e, 0x31,
+       0xa1, 0xe7, 0xba, 0x7e, 0x17, 0x62, 0x03, 0x79,
+       0xc0, 0x07, 0x48, 0x11, 0x8b, 0xfa, 0x58, 0x17,
+       0x56, 0x1a, 0xa1, 0x62, 0xd2, 0x02, 0x02, 0x2a,
+       0x64, 0x8d, 0x8c, 0x53, 0xfa, 0x28, 0x7c, 0x89,
+       0x18, 0x34, 0x70, 0x64, 0xa7, 0x08, 0x10, 0xc9,
+       0x3b, 0x1b, 0x2c, 0x23, 0x88, 0x9c, 0x35, 0x50,
+       0x78, 0xd1, 0x89, 0x33, 0xce, 0x82, 0xb2, 0x84,
+       0xf4, 0x99, 0xd8, 0x3e, 0x67, 0x11, 0xa1, 0x5c,
+       0x1a, 0x64, 0xb8, 0x6a, 0x3e, 0xe6, 0x95, 0x2e,
+       0x47, 0x33, 0x51, 0x7e, 0xb7, 0x62, 0xb4, 0x08,
+       0x2c, 0xc4, 0x87, 0x52, 0x00, 0x9e, 0x28, 0xf2,
+       0x16, 0x9f, 0x1b, 0xc1, 0x3a, 0x93, 0x6d, 0xa3,
+       0x38, 0x9b, 0x34, 0x39, 0x88, 0x85, 0xea, 0x38,
+       0xad, 0xc2, 0x2b, 0xc3, 0x7c, 0x15, 0xcb, 0x8f,
+       0x15, 0x37, 0xed, 0x88, 0x62, 0x5c, 0x34, 0x75,
+       0x6f, 0xb0, 0xeb, 0x5c, 0x42, 0x6a, 0xcd, 0x03,
+       0xcc, 0x49, 0xbc, 0xb4, 0x78, 0x14, 0xe1, 0x5e,
+       0x98, 0x83, 0x6f, 0xe7, 0x19, 0xa8, 0x43, 0xcb,
+       0xca, 0x07, 0xb2, 0x4e, 0xa4, 0x36, 0x60, 0x95,
+       0xac, 0x6f, 0xe2, 0x1d, 0x3a, 0x33, 0xf6, 0x0e,
+       0x94, 0xae, 0xfb, 0xd2, 0xac, 0x9f, 0xc2, 0x9f,
+       0x5b, 0x77, 0x8f, 0x46, 0x3c, 0xee, 0x13, 0x27,
+       0x19, 0x8e, 0x68, 0x71, 0x27, 0x3f, 0x50, 0x59,
+};
+
+static uint8_t DER_EXPONENT[] = {
+       0x01, 0x00, 0x01,
+};
+
+/* clang-format on */
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+
+/*
+ * Test samba_kdc_message2entry behaviour when passed an empty message.
+ */
+static void empty_message2entry(void **state)
+{
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       krb5_context krb5_ctx = NULL;
+       struct samba_kdc_db_context *kdc_db_ctx = NULL;
+       struct ldb_context *ldb_ctx = ldb_init(mem_ctx, NULL);
+
+       krb5_principal principal = NULL;
+       struct ldb_dn *realm_dn = NULL;
+       struct ldb_message *msg = NULL;
+
+       krb5_kvno kvno = 0;
+       enum samba_kdc_ent_type ent_type = SAMBA_KDC_ENT_TYPE_CLIENT;
+       unsigned int flags = 0;
+
+       struct sdb_entry entry = {};
+       krb5_error_code err = 0;
+
+       /* Set up */
+       kdc_db_ctx = create_kdc_db_ctx(mem_ctx);
+       ;
+       realm_dn = ldb_dn_new(mem_ctx, ldb_ctx, "TEST.SAMBA.ORG");
+
+       smb_krb5_init_context_common(&krb5_ctx);
+       assert_non_null(krb5_ctx);
+
+       principal = get_principal(mem_ctx,
+                                 krb5_ctx,
+                                 "atestuser@test.samba.org");
+
+       msg = ldb_msg_new(mem_ctx);
+       err = samba_kdc_message2entry(krb5_ctx,
+                                     kdc_db_ctx,
+                                     mem_ctx,
+                                     principal,
+                                     ent_type,
+                                     flags,
+                                     kvno,
+                                     realm_dn,
+                                     msg,
+                                     &entry);
+
+       /* Expect ENOENT as there is no SAMAccountName, among others */
+       assert_int_equal(ENOENT, err);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test samba_kdc_message2entry behaviour with minimum required elements.
+ */
+static void minimal_message2entry(void **state)
+{
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       krb5_context krb5_ctx = NULL;
+       struct samba_kdc_db_context *kdc_db_ctx = create_kdc_db_ctx(mem_ctx);
+       struct ldb_context *ldb_ctx = ldb_init(mem_ctx, NULL);
+       struct ldb_dn *realm_dn = ldb_dn_new(mem_ctx,
+                                            ldb_ctx,
+                                            "TEST.SAMBA.ORG");
+       struct ldb_message *msg = NULL;
+
+       krb5_principal principal = NULL;
+
+       enum samba_kdc_ent_type ent_type = SAMBA_KDC_ENT_TYPE_CLIENT;
+       unsigned int flags = 0;
+       krb5_kvno kvno = 0;
+
+       struct sdb_entry entry = {};
+       krb5_error_code err = 0;
+
+       /* Set up */
+       smb_krb5_init_context_common(&krb5_ctx);
+       assert_non_null(krb5_ctx);
+       principal = get_principal(mem_ctx,
+                                 krb5_ctx,
+                                 "atestuser@test.samba.org");
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+
+       err = samba_kdc_message2entry(krb5_ctx,
+                                     kdc_db_ctx,
+                                     mem_ctx,
+                                     principal,
+                                     ent_type,
+                                     flags,
+                                     kvno,
+                                     realm_dn,
+                                     msg,
+                                     &entry);
+
+       /* Expect the ldb message to be loaded */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       krb5_free_principal(krb5_ctx, principal);
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test samba_kdc_message2entry mapping of msDS-KeyCredentialLink.
+ */
+static void msDS_KeyCredentialLink_message2entry(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       krb5_context krb5_ctx = NULL;
+       struct samba_kdc_db_context *kdc_db_ctx = create_kdc_db_ctx(mem_ctx);
+       struct ldb_context *ldb_ctx = ldb_init(mem_ctx, NULL);
+       struct ldb_dn *realm_dn = ldb_dn_new(mem_ctx,
+                                            ldb_ctx,
+                                            "TEST.SAMBA.ORG");
+       struct ldb_message *msg = NULL;
+
+       krb5_principal principal = NULL;
+
+       enum samba_kdc_ent_type ent_type = SAMBA_KDC_ENT_TYPE_CLIENT;
+       unsigned int flags = 0;
+       krb5_kvno kvno = 0;
+
+       struct sdb_entry entry = {};
+       krb5_error_code err = 0;
+
+       /* Set up */
+       smb_krb5_init_context_common(&krb5_ctx);
+       assert_non_null(krb5_ctx);
+       principal = get_principal(mem_ctx,
+                                 krb5_ctx,
+                                 "atestuser@test.samba.org");
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(BCRYPT_KEY_CREDENTIAL_LINK),
+                                  BCRYPT_KEY_CREDENTIAL_LINK);
+
+       err = samba_kdc_message2entry(krb5_ctx,
+                                     kdc_db_ctx,
+                                     mem_ctx,
+                                     principal,
+                                     ent_type,
+                                     flags,
+                                     kvno,
+                                     realm_dn,
+                                     msg,
+                                     &entry);
+
+       /* Expect the ldb message to be loaded */
+       assert_int_equal(0, err);
+       assert_non_null(entry.pub_keys.keys);
+       assert_int_equal(1, entry.pub_keys.len);
+
+       assert_int_equal(2048, entry.pub_keys.keys[0].bit_size);
+
+       assert_int_equal(sizeof(BCRYPT_MODULUS),
+                        entry.pub_keys.keys[0].modulus.length);
+       assert_memory_equal(BCRYPT_MODULUS,
+                           entry.pub_keys.keys[0].modulus.data,
+                           sizeof(BCRYPT_MODULUS));
+
+       assert_int_equal(sizeof(BCRYPT_EXPONENT),
+                        entry.pub_keys.keys[0].exponent.length);
+       assert_memory_equal(BCRYPT_EXPONENT,
+                           entry.pub_keys.keys[0].exponent.data,
+                           sizeof(BCRYPT_EXPONENT));
+
+       krb5_free_principal(krb5_ctx, principal);
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys
+ * handling of an invalid version number
+ */
+static void invalid_version_keycredlink(void **state)
+{
+       /* clang-format off */
+       uint8_t KEY_CREDENTIAL_LINK[] = {
+               0x01, 0x02, 0x00, 0x00, /* Invalid version                   */
+               0x18, 0x00, 0x03,       /* Key Material                      */
+               'R',  'S',  'A',  '1',  /* RSA public key                    */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x01, 0x00, 0x04, 0x01, /* key usage (KEY_USAGE_NGC)         */
+               0x01, 0x00, 0x05, 0x00, /* key source (KEY_SOURCE_AD)        */
+       };
+       /* clang-format on */
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(KEY_CREDENTIAL_LINK),
+                                  KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       /* Expect the key credential link to be ignored */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys
+ * handling of duplicate key material
+ */
+static void duplicate_key_material_keycredlink(void **state)
+{
+       /* clang-format off */
+       uint8_t KEY_CREDENTIAL_LINK[] = {
+               0x00, 0x02, 0x00, 0x00, /* Invalid version                   */
+               0x18, 0x00, 0x03,       /* Key Material                      */
+               'R',  'S',  'A',  '1',  /* RSA public key                    */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x18, 0x00, 0x03,       /* Key Material, a duplicate         */
+               'R',  'S',  'A',  '1',  /* RSA public key                    */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x01, 0x00, 0x04, 0x01, /* key usage (KEY_USAGE_NGC)         */
+               0x01, 0x00, 0x05, 0x00, /* key source (KEY_SOURCE_AD)        */
+       };
+       /* clang-format on */
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(KEY_CREDENTIAL_LINK),
+                                  KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       /* Expect the key credential link to be ignored */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys
+ * handling of duplicate key usage entries
+ */
+static void duplicate_key_usage_keycredlink(void **state)
+{
+       /* clang-format off */
+       uint8_t KEY_CREDENTIAL_LINK[] = {
+               0x00, 0x02, 0x00, 0x00, /* Invalid version                   */
+               0x18, 0x00, 0x03,       /* Key Material                      */
+               'R',  'S',  'A',  '1',  /* RSA public key                    */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x01, 0x00, 0x04, 0x01, /* key usage (KEY_USAGE_NGC)         */
+               0x01, 0x00, 0x04, 0x01, /* key usage duplicate               */
+               0x01, 0x00, 0x05, 0x00, /* key source (KEY_SOURCE_AD)        */
+       };
+       /* clang-format on */
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(KEY_CREDENTIAL_LINK),
+                                  KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       /* Expect the key credential link to be ignored */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys
+ * handling of invalid key usage entries
+ */
+static void invalid_key_usage_keycredlink(void **state)
+{
+       /* clang-format off */
+       uint8_t KEY_CREDENTIAL_LINK[] = {
+               0x00, 0x02, 0x00, 0x00, /* Invalid version                   */
+               0x18, 0x00, 0x03,       /* Key Material                      */
+               'R',  'S',  'A',  '1',  /* RSA public key                    */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x01, 0x00, 0x04, 0xFE, /* key usage invalid                 */
+               0x01, 0x00, 0x05, 0x00, /* key source (KEY_SOURCE_AD)        */
+       };
+       /* clang-format on */
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(KEY_CREDENTIAL_LINK),
+                                  KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       /* Expect the key credential link to be ignored */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys
+ * handling of invalid key usage entries
+ */
+static void invalid_key_material_keycredlink(void **state)
+{
+       /* clang-format off */
+       uint8_t KEY_CREDENTIAL_LINK[] = {
+               0x00, 0x02, 0x00, 0x00, /* version 2                         */
+               0x18, 0x00, 0x03,       /* Key Material                      */
+               'R',  'S',  'A',  '2',  /* RSA private key                   */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x01, 0x00, 0x04, 0x01, /* key usage                         */
+               0x01, 0x00, 0x05, 0x00, /* key source                        */
+       };
+       /* clang-format on */
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = ldb_msg_new(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(KEY_CREDENTIAL_LINK),
+                                  KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       /* Expect the key credential link to be ignored */
+       assert_int_equal(0, err);
+       assert_null(entry.pub_keys.keys);
+       assert_int_equal(0, entry.pub_keys.len);
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys can unpack BCRYPT
+ * key material.
+ *
+ */
+static void keycred_bcrypt_key_material(void **state)
+{
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(BCRYPT_KEY_CREDENTIAL_LINK),
+                                  BCRYPT_KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       assert_int_equal(0, err);
+       assert_non_null(entry.pub_keys.keys);
+       assert_int_equal(1, entry.pub_keys.len);
+
+       assert_int_equal(2048, entry.pub_keys.keys[0].bit_size);
+
+       assert_int_equal(sizeof(BCRYPT_MODULUS),
+                        entry.pub_keys.keys[0].modulus.length);
+       assert_memory_equal(BCRYPT_MODULUS,
+                           entry.pub_keys.keys[0].modulus.data,
+                           sizeof(BCRYPT_MODULUS));
+
+       assert_int_equal(sizeof(BCRYPT_EXPONENT),
+                        entry.pub_keys.keys[0].exponent.length);
+       assert_memory_equal(BCRYPT_EXPONENT,
+                           entry.pub_keys.keys[0].exponent.data,
+                           sizeof(BCRYPT_EXPONENT));
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys can unpack TPM 2.0
+ * key material.
+ *
+ */
+static void keycred_tpm_key_material(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(TPM_KEY_CREDENTIAL_LINK),
+                                  TPM_KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       assert_int_equal(0, err);
+       assert_non_null(entry.pub_keys.keys);
+       assert_int_equal(1, entry.pub_keys.len);
+
+       assert_int_equal(2048, entry.pub_keys.keys[0].bit_size);
+
+       assert_int_equal(sizeof(TPM_MODULUS),
+                        entry.pub_keys.keys[0].modulus.length);
+       assert_memory_equal(TPM_MODULUS,
+                           entry.pub_keys.keys[0].modulus.data,
+                           sizeof(TPM_MODULUS));
+
+       assert_int_equal(sizeof(TPM_EXPONENT),
+                        entry.pub_keys.keys[0].exponent.length);
+       assert_memory_equal(TPM_EXPONENT,
+                           entry.pub_keys.keys[0].exponent.data,
+                           sizeof(TPM_EXPONENT));
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys can unpack DER
+ * key material.
+ *
+ */
+static void keycred_der_key_material(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(DER_KEY_CREDENTIAL_LINK),
+                                  DER_KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       assert_int_equal(0, err);
+       assert_non_null(entry.pub_keys.keys);
+       assert_int_equal(1, entry.pub_keys.len);
+
+       assert_int_equal(2048, entry.pub_keys.keys[0].bit_size);
+
+       assert_int_equal(sizeof(DER_MODULUS),
+                        entry.pub_keys.keys[0].modulus.length);
+       assert_memory_equal(DER_MODULUS,
+                           entry.pub_keys.keys[0].modulus.data,
+                           sizeof(DER_MODULUS));
+
+       assert_int_equal(sizeof(DER_EXPONENT),
+                        entry.pub_keys.keys[0].exponent.length);
+       assert_memory_equal(DER_EXPONENT,
+                           entry.pub_keys.keys[0].exponent.data,
+                           sizeof(DER_EXPONENT));
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * Test get_key_trust_public_keys can unpack multiple
+ * key material values.
+ *
+ */
+static void keycred_multiple(void **state)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+       struct ldb_message *msg = NULL;
+       krb5_error_code err = 0;
+       struct sdb_entry entry = {};
+
+       /* Create the ldb_message */
+       msg = create_ldb_message(mem_ctx);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(DER_KEY_CREDENTIAL_LINK),
+                                  DER_KEY_CREDENTIAL_LINK);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(TPM_KEY_CREDENTIAL_LINK),
+                                  TPM_KEY_CREDENTIAL_LINK);
+       add_msDS_KeyCredentialLink(msg,
+                                  sizeof(BCRYPT_KEY_CREDENTIAL_LINK),
+                                  BCRYPT_KEY_CREDENTIAL_LINK);
+
+       err = get_key_trust_public_keys(mem_ctx, msg, &entry);
+
+       assert_int_equal(0, err);
+       assert_non_null(entry.pub_keys.keys);
+       assert_int_equal(3, entry.pub_keys.len);
+
+       /* Check DER entry */
+       assert_int_equal(2048, entry.pub_keys.keys[0].bit_size);
+
+       assert_int_equal(sizeof(DER_MODULUS),
+                        entry.pub_keys.keys[0].modulus.length);
+       assert_memory_equal(DER_MODULUS,
+                           entry.pub_keys.keys[0].modulus.data,
+                           sizeof(DER_MODULUS));
+
+       assert_int_equal(sizeof(DER_EXPONENT),
+                        entry.pub_keys.keys[0].exponent.length);
+       assert_memory_equal(DER_EXPONENT,
+                           entry.pub_keys.keys[0].exponent.data,
+                           sizeof(DER_EXPONENT));
+
+       /* Check TPM entry */
+       assert_int_equal(2048, entry.pub_keys.keys[1].bit_size);
+
+       assert_int_equal(sizeof(TPM_MODULUS),
+                        entry.pub_keys.keys[1].modulus.length);
+       assert_memory_equal(TPM_MODULUS,
+                           entry.pub_keys.keys[1].modulus.data,
+                           sizeof(TPM_MODULUS));
+
+       assert_int_equal(sizeof(TPM_EXPONENT),
+                        entry.pub_keys.keys[1].exponent.length);
+       assert_memory_equal(TPM_EXPONENT,
+                           entry.pub_keys.keys[1].exponent.data,
+                           sizeof(TPM_EXPONENT));
+
+       /* Check BCRYPT entry */
+       assert_int_equal(2048, entry.pub_keys.keys[2].bit_size);
+
+       assert_int_equal(sizeof(BCRYPT_MODULUS),
+                        entry.pub_keys.keys[2].modulus.length);
+       assert_memory_equal(BCRYPT_MODULUS,
+                           entry.pub_keys.keys[2].modulus.data,
+                           sizeof(BCRYPT_MODULUS));
+
+       assert_int_equal(sizeof(BCRYPT_EXPONENT),
+                        entry.pub_keys.keys[2].exponent.length);
+       assert_memory_equal(BCRYPT_EXPONENT,
+                           entry.pub_keys.keys[2].exponent.data,
+                           sizeof(BCRYPT_EXPONENT));
+
+       sdb_entry_free(&entry);
+       TALLOC_FREE(mem_ctx);
+}
+
+int main(int argc, const char **argv)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(empty_message2entry),
+               cmocka_unit_test(minimal_message2entry),
+               cmocka_unit_test(msDS_KeyCredentialLink_message2entry),
+               cmocka_unit_test(invalid_version_keycredlink),
+               cmocka_unit_test(duplicate_key_material_keycredlink),
+               cmocka_unit_test(duplicate_key_usage_keycredlink),
+               cmocka_unit_test(invalid_key_usage_keycredlink),
+               cmocka_unit_test(invalid_key_material_keycredlink),
+               cmocka_unit_test(keycred_bcrypt_key_material),
+               cmocka_unit_test(keycred_tpm_key_material),
+               cmocka_unit_test(keycred_der_key_material),
+               cmocka_unit_test(keycred_multiple),
+       };
+
+       cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
index 9b73adfbe0f39fefc0a45875d60df1196e113769..f3b3ae79dfa4ae97043d17ab463d2a9e3a7cfc78 100644 (file)
@@ -26,6 +26,17 @@ if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'):
                      ''',
                      internal_module=False)
 
+    bld.SAMBA_BINARY('test_db_glue',
+                    source='tests/db-glue-test.c',
+                    deps='''
+                        db-glue
+                        HDB_SAMBA4
+                        cmocka
+                        talloc
+                        ''',
+                    for_selftest=True,
+                    )
+
 if bld.CONFIG_GET('SAMBA_USES_MITKDC'):
     bld.SAMBA_MODULE('service_kdc',
                      source='kdc-service-mit.c',
@@ -54,6 +65,7 @@ bld.SAMBA_LIBRARY('HDB_SAMBA4',
                   enabled=bld.CONFIG_SET('SAMBA4_USES_HEIMDAL')
                   )
 
+
 # A plugin for Heimdal's kadmin for users who need to operate that tool
 bld.SAMBA_LIBRARY('HDB_SAMBA4_PLUGIN',
                   source='hdb-samba4-plugin.c',