The branch, master has been updated
via da985349114 librpc/idl: Add idl for msDS-KeyCredentialLink
from e7054efb97d s3:selftest: run smb2.{bench,connect,credits,ioctl,rw}
over bsd-tstream
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit da98534911435daf2ec9a098e8a5120dfc94fa53
Author: Gary Lockyer <[email protected]>
Date: Wed Jun 11 09:04:01 2025 +1200
librpc/idl: Add idl for msDS-KeyCredentialLink
Idl and supporting helpers for msDS-KeyCredentialLinks.
See [MS-ADTS] 2.2.20 Key Credential Link Structures
Currently the KeyMaterial is treated as a binary blob
The naming and casing of the variable names is close as is possible to
those in the specification.
Signed-off-by: Gary Lockyer <[email protected]>
Reviewed-by: Douglas Bagnall <[email protected]>
Autobuild-User(master): Douglas Bagnall <[email protected]>
Autobuild-Date(master): Thu Jun 19 00:08:31 UTC 2025 on atb-devel-224
-----------------------------------------------------------------------
Summary of changes:
librpc/idl/keycredlink.idl | 167 +++++++++
librpc/idl/wscript_build | 1 +
librpc/ndr/ndr_keycredlink.c | 441 ++++++++++++++++++++++
librpc/ndr/{ndr_ODJ.h => ndr_keycredlink.h} | 10 +-
librpc/wscript_build | 8 +-
python/samba/tests/key_credential_link.py | 555 ++++++++++++++++++++++++++++
selftest/tests.py | 1 +
source4/librpc/wscript_build | 8 +
8 files changed, 1186 insertions(+), 5 deletions(-)
create mode 100644 librpc/idl/keycredlink.idl
create mode 100644 librpc/ndr/ndr_keycredlink.c
copy librpc/ndr/{ndr_ODJ.h => ndr_keycredlink.h} (79%)
create mode 100755 python/samba/tests/key_credential_link.py
Changeset truncated at 500 lines:
diff --git a/librpc/idl/keycredlink.idl b/librpc/idl/keycredlink.idl
new file mode 100644
index 00000000000..6492df522a1
--- /dev/null
+++ b/librpc/idl/keycredlink.idl
@@ -0,0 +1,167 @@
+/*
+ Definitions for packing and unpacking of msDS-KeyCredentialLink
+ structures, derived from [MS-ADTS] 2.2.20 Key Credential Link Structures
+
+ Note: - KeyMaterial is treated as a binary blob
+ - KEYCREDENTIALLINK_ENTRY ordering by identifier not enforced
+ - Presence of the mandatory KEYCREDENTIALLINK_ENTRYs Key_ID,
+ KeyMaterial and KeyUsage is not enforced
+*/
+
+#include "idl_types.h"
+
+[
+ pointer_default(unique),
+ helper("../librpc/ndr/ndr_keycredlink.h")
+]
+interface keycredlink
+{
+ /* Public structures. */
+
+ typedef [enum8bit, public] enum {
+ KeyID = 0x01,
+ KeyHash = 0x02,
+ KeyMaterial = 0x03,
+ KeyUsage = 0x04,
+ KeySource = 0x05,
+ DeviceId = 0x06,
+ CustomKeyInformation = 0x07,
+ KeyApproximateLastLogonTimeStamp = 0x08,
+ KeyCreationTime = 0x09
+ } KEYCREDENTIALLINK_ENTRY_Identifier;
+
+ typedef [enum8bit, public] enum {
+ KEY_USAGE_NGC = 0x01,
+ KEY_USAGE_FIDO = 0x02,
+ KEY_USAGE_FEK = 0x03
+ } KEYCREDENTIALLINK_ENTRY_KeyUsage;
+
+ typedef [enum8bit, public] enum {
+ KEY_SOURCE_AD = 0x00
+ } KEYCREDENTIALLINK_ENTRY_KeySource;
+
+ typedef [bitmap8bit, public] bitmap {
+ CUSTOM_KEY_INFO_FLAGS_ATTESTATION = 0x01,
+ /* Reserved for future use */
+ CUSTOM_KEY_INFO_FLAGS_MFA_NOT_USED = 0x02
+ /*
+ * During creation of this key, the requesting client
+ * authenticated using only a single credential.
+ */
+ } CUSTOM_KEY_INFO_Flags;
+
+ typedef [enum8bit, public] enum {
+ Unspecified = 0x00, // No volume specified
+ // defined as None in the docs but this
+ // causes issues in the python bindings
+ OSV = 0x01, // Operating system volume
+ FDV = 0x02, // Fixed data volume
+ RDV = 0x03 // Removable data volume
+ } CUSTOM_KEY_INFO_VolType;
+
+ typedef [enum8bit, public] enum {
+ Unsupported = 0x00, // Notification is not supported
+ // defined as None in the docs but this
+ // causes issues in the python bindings
+ Supported = 0x01 // Notification is supported
+ } CUSTOM_KEY_INFO_SupportsNotification;
+
+ typedef [enum8bit, public] enum {
+ Unknown = 0x00,
+ Weak = 0x01,
+ Normal = 0x02
+ } CUSTOM_KEY_INFO_KeyStrength;
+
+ /*
+ * Extended custom key information
+ */
+ typedef [public, flag(NDR_NOALIGN)] struct {
+ [value(0)] uint8 version;
+ uint8 size;
+ uint8 data[size];
+ /*
+ * A Concise Binary Object Representation (CBOR)-encoded blob
+ * whose length is specified by the Size field.
+ * CBOR is a binary data serialization format defined in
+ * [RFC7049]. The contents of this field are opaque and
+ * have no behavioural impact on the protocol.
+ */
+ } EncodedExtendedCKI;
+
+ /*
+ * This structure has two possible representations which are
+ * differentiated by the sized of the encoded data.
+ *
+ * a) only the Version and Flags fields are present;
+ * and the structure has a size of 2 bytes.
+ * b) all additional are also present
+ * - the structure's total size is variable but not 2
+ *
+ * The boolean isExtended attribute is used to indicate which version
+ * was unpacked or should be packed.
+ *
+ * Note: isExtended and count are not present in the packed binary
+ * representation
+ */
+
+ typedef [nopush, nopull] struct {
+ [value(1)] uint8 version;
+ CUSTOM_KEY_INFO_Flags flags;
+ boolean8 isExtended;
+ /*
+ * Not present in packed representation indicates
+ * that the following fields are present
+ */
+ CUSTOM_KEY_INFO_VolType volType;
+ CUSTOM_KEY_INFO_SupportsNotification supportsNotification;
+ [value(1)] uint8 fekKeyVersion;
+ CUSTOM_KEY_INFO_KeyStrength keyStrength;
+ uint8 reserved[10]; /* Reserved bytes not currently used */
+ uint32 count;
+ /* Not present in packed representation size cki array */
+ EncodedExtendedCKI cki[count];
+ } CUSTOM_KEY_INFORMATION;
+
+ typedef [switch_type(KEYCREDENTIALLINK_ENTRY_Identifier),
+ public,
+ nopull,
+ nodiscriminant,
+ gensize,
+ flag(NDR_NOALIGN)]
+ union {
+ [case(KeyID)]
+ uint8 keyId[32];
+ [case(KeyHash)]
+ uint8 keyHash[32];
+ [case(KeyUsage)]
+ KEYCREDENTIALLINK_ENTRY_KeyUsage keyUsage;
+ [case(KeySource), value(KEY_SOURCE_AD)]
+ KEYCREDENTIALLINK_ENTRY_KeySource keySource;
+ [case(KeyMaterial)] [flag(NDR_REMAINING)]
+ DATA_BLOB keyMaterial;
+ /* Currently treating Key Material as an opaque binary blob */
+ [case(DeviceId)]
+ uint8 deviceId[16];
+ [case(CustomKeyInformation)]
+ CUSTOM_KEY_INFORMATION customKeyInformation;
+ [case(KeyApproximateLastLogonTimeStamp)]
+ NTTIME lastLogon;
+ [case(KeyCreationTime)]
+ NTTIME created;
+ } KEYCREDENTIALLINK_ENTRY_Value;
+
+ typedef [public, nopull, flag(NDR_NOALIGN)] struct {
+ [value(
+ ndr_size_KEYCREDENTIALLINK_ENTRY_Value(
+ &value,identifier,ndr->flags))]
+ uint16 length;
+ KEYCREDENTIALLINK_ENTRY_Identifier identifier;
+ [switch_is(identifier)] KEYCREDENTIALLINK_ENTRY_Value value;
+ } KEYCREDENTIALLINK_ENTRY;
+
+ typedef [public, nopull, nopush, flag(NDR_NOALIGN)] struct {
+ [value(0x0200)] uint32 version;
+ uint32 count;
+ KEYCREDENTIALLINK_ENTRY entries[count];
+ } KEYCREDENTIALLINK_BLOB;
+}
diff --git a/librpc/idl/wscript_build b/librpc/idl/wscript_build
index c7d6413b47f..8a8f97d8592 100644
--- a/librpc/idl/wscript_build
+++ b/librpc/idl/wscript_build
@@ -132,6 +132,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
drsblobs.idl
gmsa.idl
idmap.idl
+ keycredlink.idl
krb5pac.idl
krb5ccache.idl
schannel.idl
diff --git a/librpc/ndr/ndr_keycredlink.c b/librpc/ndr/ndr_keycredlink.c
new file mode 100644
index 00000000000..967202978b6
--- /dev/null
+++ b/librpc/ndr/ndr_keycredlink.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Support routines for packing and unpacking of msDS-KeyCredentialLink
+ structures.
+
+ See [MS-ADTS] 2.2.20 Key Credential Link Structures
+
+ 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/>.
+*/
+
+#include "lib/replace/replace.h"
+#include "librpc/gen_ndr/ndr_keycredlink.h"
+#include "gen_ndr/keycredlink.h"
+#include "libndr.h"
+#include <assert.h>
+
+/*
+ * The KEYCREDENTIALLINK_BLOB consists of the version and a series of variable
+ * length KEYCREDENTIALLINK_ENTRIES.
+ */
+enum ndr_err_code ndr_pull_KEYCREDENTIALLINK_BLOB(
+ struct ndr_pull *ndr,
+ ndr_flags_type ndr_flags,
+ struct KEYCREDENTIALLINK_BLOB *blob)
+{
+ libndr_flags _flags_save_STRUCT = ndr->flags;
+ ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
+
+ NDR_CHECK(ndr_pull_uint32(ndr, ndr_flags, &blob->version));
+ if (blob->version != 0x0200) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid version of (0x%04x) "
+ "should be 0x0200, at byte %zu\n",
+ blob->version,
+ (ndr->offset - sizeof(uint32_t)));
+ }
+ blob->count = 0;
+ blob->entries = talloc_array(ndr->current_mem_ctx,
+ struct KEYCREDENTIALLINK_ENTRY,
+ blob->count);
+ if (blob->entries == NULL) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_ALLOC,
+ "Failed to pull KEYCREDENTIALLINK_ENTRY");
+ }
+ while (ndr->offset < ndr->data_size) {
+ blob->entries = talloc_realloc(ndr->current_mem_ctx,
+ blob->entries,
+ struct KEYCREDENTIALLINK_ENTRY,
+ blob->count + 1);
+ if (blob->entries == NULL) {
+ return ndr_pull_error(
+ ndr,
+ NDR_ERR_ALLOC,
+ "Failed to pull KEYCREDENTIALLINK_ENTRY");
+ }
+ NDR_CHECK(ndr_pull_KEYCREDENTIALLINK_ENTRY(
+ ndr, ndr_flags, &blob->entries[blob->count]));
+ blob->count++;
+ }
+ ndr->flags = _flags_save_STRUCT;
+ return NDR_ERR_SUCCESS;
+}
+
+enum ndr_err_code ndr_push_KEYCREDENTIALLINK_BLOB(
+ struct ndr_push *ndr,
+ ndr_flags_type ndr_flags,
+ const struct KEYCREDENTIALLINK_BLOB *blob)
+{
+ int i = 0;
+
+ if (blob->version != 0x0200) {
+ return ndr_push_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid version of (0x%04x) "
+ "should be 0x0200, at byte %zu\n",
+ blob->version,
+ (ndr->offset - sizeof(uint32_t)));
+ }
+ NDR_CHECK(ndr_push_uint32(ndr, ndr_flags, blob->version));
+
+ for (i = 0; i < blob->count; i++) {
+ NDR_CHECK(ndr_push_KEYCREDENTIALLINK_ENTRY(ndr,
+ ndr_flags,
+ &blob->entries[i]));
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+/*
+ * To pull the CUSTOM_KEY_INFORMATION the length from the enclosing
+ * KEYCREDENTIALLINK_ENTRY needs to be passed in.
+ *
+ * CUSTOM_KEY_INFORMATION has two representations based on the size parameter
+ *
+ * If size is 2 only the version and flags are expected.
+ * If the size is greater than 2 then
+ * version, flags, volType, supportsNotification, fekKeyVersion,
+ * keyStrength and the reserved bytes are expected
+ * Optionally followed by a series of EncodedExtendedCKI entries
+ *
+ */
+static enum ndr_err_code pull_cki(struct ndr_pull *ndr,
+ ndr_flags_type ndr_flags,
+ struct CUSTOM_KEY_INFORMATION *info,
+ uint32_t size)
+{
+ /* Calculate the end of the CUSTOM_KEY_INFORMATION in the raw bytes */
+ uint32_t end_offset = ndr->offset + size;
+
+ /*
+ * Initialise the CUSTOM_KEY_INFORMATION, in case this is the
+ * short form.
+ */
+ *info = (struct CUSTOM_KEY_INFORMATION){0};
+
+ NDR_CHECK(ndr_pull_uint8(ndr, ndr_flags, &info->version));
+ if (info->version != 0x01) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid version of (0x%02x) "
+ "should be 0x01, at byte %zu\n",
+ info->version,
+ (ndr->offset - sizeof(uint8_t)));
+ }
+ NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_Flags(ndr, ndr_flags, &info->flags));
+
+ if (size == 2) {
+ info->isExtended = false;
+ return NDR_ERR_SUCCESS;
+ }
+ info->isExtended = true;
+ NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_VolType(ndr,
+ ndr_flags,
+ &info->volType));
+ NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_SupportsNotification(
+ ndr, ndr_flags, &info->supportsNotification));
+ NDR_CHECK(ndr_pull_uint8(ndr, ndr_flags, &info->fekKeyVersion));
+ if (info->fekKeyVersion != 0x01) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid fekKeyVersion of (0x%02x) "
+ "should be 0x01, at byte %zu\n",
+ info->fekKeyVersion,
+ (ndr->offset - sizeof(uint8_t)));
+ }
+ NDR_CHECK(ndr_pull_CUSTOM_KEY_INFO_KeyStrength(ndr,
+ ndr_flags,
+ &info->keyStrength));
+ NDR_CHECK(ndr_pull_array_uint8(ndr, ndr_flags, info->reserved, 10));
+
+ /* Pull the EncodedExtendedCKI values */
+ info->count = 0;
+ info->cki = talloc_array(ndr->current_mem_ctx,
+ struct EncodedExtendedCKI,
+ info->count);
+ if (info->cki == NULL) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_ALLOC,
+ "Failed to pull EncodedExtendCKI");
+ }
+ while (ndr->offset < end_offset) {
+ info->cki = talloc_realloc(ndr->current_mem_ctx,
+ info->cki,
+ struct EncodedExtendedCKI,
+ info->count + 1);
+ if (info->cki == NULL) {
+ return ndr_pull_error(
+ ndr,
+ NDR_ERR_ALLOC,
+ "Failed to pull EncodedExtendedCKI");
+ }
+ NDR_CHECK(ndr_pull_EncodedExtendedCKI(ndr,
+ ndr_flags,
+ &info->cki[info->count]));
+ info->count++;
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+/*
+ * CUSTOM_KEY-INFORMATION has two representations with differing sizes
+ * the flag isExtended controls which version is written.
+ */
+enum ndr_err_code ndr_push_CUSTOM_KEY_INFORMATION(
+ struct ndr_push *ndr,
+ ndr_flags_type ndr_flags,
+ const struct CUSTOM_KEY_INFORMATION *info)
+{
+ int i = 0;
+
+ if (info->version != 0x01) {
+ return ndr_push_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid version of (0x%02x) "
+ "should be 0x01, at byte %zu\n",
+ info->version,
+ (ndr->offset - sizeof(uint8_t)));
+ }
+ NDR_CHECK(ndr_push_uint8(ndr, ndr_flags, info->version));
+ NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_Flags(ndr, ndr_flags, info->flags));
+ if (!info->isExtended) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_VolType(ndr,
+ ndr_flags,
+ info->volType));
+ NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_SupportsNotification(
+ ndr, ndr_flags, info->supportsNotification));
+ if (info->fekKeyVersion != 0x01) {
+ return ndr_push_error(ndr,
+ NDR_ERR_RANGE,
+ "Invalid fekKeyVersion of (0x%02x) "
+ "should be 0x01, at byte %zu\n",
+ info->fekKeyVersion,
+ (ndr->offset - sizeof(uint8_t)));
+ }
+ NDR_CHECK(ndr_push_uint8(ndr, ndr_flags, info->fekKeyVersion));
+ NDR_CHECK(ndr_push_CUSTOM_KEY_INFO_KeyStrength(ndr,
+ ndr_flags,
+ info->keyStrength));
+ NDR_CHECK(ndr_push_array_uint8(ndr, ndr_flags, info->reserved, 10));
+
+ for (i = 0; i < info->count; i++) {
+ NDR_CHECK(ndr_push_EncodedExtendedCKI(ndr,
+ ndr_flags,
+ &info->cki[i]));
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+/*
+ * To pull a KEYCREDENTIALLINK_Value the length from the enclosing
+ * KEYCREDENTIALLINK_ENTRY needs to be passed in.
+ *
+ */
+static enum ndr_err_code ndr_pull_value(struct ndr_pull *ndr,
+ ndr_flags_type ndr_flags,
+ union KEYCREDENTIALLINK_ENTRY_Value *r,
+ uint32_t size)
+{
+ uint32_t level;
+ const size_t header_len = sizeof(uint16_t) + sizeof(uint8_t);
+ const size_t identifier_len = sizeof(uint8_t);
+ libndr_flags flags_save = ndr->flags;
+
+ /* this function should only be called if NDR_SCALARS is set */
+ assert(ndr_flags & NDR_SCALARS);
+
+ ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
+
+ /* This token is not used again */
+ NDR_CHECK(ndr_pull_steal_switch_value(ndr, r, &level));
+
+ switch (level) {
+ case KeyID: {
+ if (size != 32) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_ARRAY_SIZE,
+ "Invalid size of (%" PRIu32
+ ") for KeyID "
+ "should be (32), at byte %zu\n",
+ size,
+ (ndr->offset - header_len));
+ }
+ NDR_CHECK(
+ ndr_pull_array_uint8(ndr, NDR_SCALARS, r->keyId, size));
+ break;
+ }
+
+ case KeyHash: {
+ if (size != 32) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_ARRAY_SIZE,
+ "Invalid size of (%" PRIu32
+ ") for KeyHash "
+ "should be (32), at byte %zu\n",
+ size,
+ (ndr->offset - header_len));
+ }
+ NDR_CHECK(ndr_pull_array_uint8(
+ ndr, NDR_SCALARS, r->keyHash, size));
+ break;
+ }
+
+ case KeyUsage: {
+ if (size != 1) {
+ return ndr_pull_error(ndr,
+ NDR_ERR_LENGTH,
+ "Invalid length of (%" PRIu32
+ ") for KeyUsage "
+ "should be (1), at byte %zu\n",
+ size,
--
Samba Shared Repository