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

Reply via email to