From 33b55227db888acf70db9ff44c385a294e07ce36 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 25 Jul 2025 13:22:27 +1200 Subject: [PATCH] s4:kdc Support for key trust authentication 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 Reviewed-by: Douglas Bagnall Autobuild-User(master): Douglas Bagnall Autobuild-Date(master): Tue Jul 29 05:31:10 UTC 2025 on atb-devel-224 --- selftest/tests.py | 3 + source4/auth/sam.c | 4 +- source4/kdc/db-glue.c | 243 +++++++ source4/kdc/tests/db-glue-test.c | 1054 ++++++++++++++++++++++++++++++ source4/kdc/wscript_build | 12 + 5 files changed, 1315 insertions(+), 1 deletion(-) create mode 100644 source4/kdc/tests/db-glue-test.c diff --git a/selftest/tests.py b/selftest/tests.py index 1f4d8e5cfd6..49fe5e6426b 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -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") diff --git a/source4/auth/sam.c b/source4/auth/sam.c index 32475de8780..99c60977d53 100644 --- a/source4/auth/sam.c +++ b/source4/auth/sam.c @@ -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 */ \ diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 5bfc41b6681..f3e8bd9073c 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -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 index 00000000000..4d5f7191414 --- /dev/null +++ b/source4/kdc/tests/db-glue-test.c @@ -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 . + * + */ + +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include + * #include + * #include + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + * + */ + +#include +#include +#include + +#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); +} diff --git a/source4/kdc/wscript_build b/source4/kdc/wscript_build index 9b73adfbe0f..f3b3ae79dfa 100644 --- a/source4/kdc/wscript_build +++ b/source4/kdc/wscript_build @@ -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', -- 2.47.2