]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
imds: add TPM measurements to imds tool
authorLennart Poettering <lennart@amutable.com>
Tue, 24 Mar 2026 08:41:03 +0000 (09:41 +0100)
committerLennart Poettering <lennart@amutable.com>
Thu, 26 Mar 2026 09:54:15 +0000 (10:54 +0100)
This automatically measures the IMDS 'userdata' into PCR 12, i.e. where
we measure the other owner-supplied configuration, such as confexts and
credentials and similar.

(Why 12? It's really about who owns the data and what it is for.
PCRs/NvPCRs are scarce hence there's a strong incentive to not go
overboard with new allocations, and IMDS userdata in purpose and owner
is very very similar to confexts and credentials, hence let's reuse the
PCR for this purpose.)

src/imds/imds-tool.c
src/shared/pcrextend-util.c
src/shared/pcrextend-util.h
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/shared/varlink-io.systemd.PCRExtend.c

index d4a5b6b6eb34875dbdd5db8693845ab499a6b235..4ae8dbb33cec9adf61f0eab3239b18e4fb3ec516 100644 (file)
@@ -27,6 +27,7 @@
 #include "log.h"
 #include "main-func.h"
 #include "parse-argument.h"
+#include "pcrextend-util.h"
 #include "pretty-print.h"
 #include "string-util.h"
 #include "strv.h"
@@ -822,6 +823,9 @@ static int action_import(sd_varlink *link) {
                 return ret;
         }
 
+        /* Measure the userdata before we use it */
+        (void) pcrextend_imds_userdata_now(&data);
+
         /* Keep a pristine copy of the userdata we actually applied. (Note that this data is typically also
          * kept as cached item on systemd-imdsd, but that one is possibly subject to cache invalidation,
          * while this one is supposed to pin the data actually in effect.) */
index 8586e85cbbd3f5ebb730c9c83fa74313928a8dd4..7af436217d5eb8cc7813bd126e672c2984813ca6 100644 (file)
 #include "mountpoint-util.h"
 #include "pcrextend-util.h"
 #include "pkcs7-util.h"
+#include "sha256.h"
 #include "string-util.h"
 #include "strv.h"
+#include "tpm2-pcr.h"
 
 static int device_get_file_system_word(
                 sd_device *d,
@@ -291,3 +293,70 @@ int pcrextend_verity_now(
         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring Verity root hashes and signatures.");
 #endif
 }
+
+#define IMDS_USERDATA_TRUNCATED_MAX 256U
+
+int pcrextend_imds_userdata_word(const struct iovec *data, char **ret) {
+        assert(iovec_is_set(data));
+        assert(ret);
+
+        /* We include both a hash of the complete user data, and a truncated version of the data in the word
+         * we measure. The former protects the actual data, the latter is useful for debugging. */
+
+        _cleanup_free_ char *hash = hexmem(SHA256_DIRECT(data->iov_base, data->iov_len), SHA256_DIGEST_SIZE);
+        if (!hash)
+                return log_oom();
+
+        _cleanup_free_ char *data_encoded = NULL;
+        if (base64mem_full(data->iov_base, MIN(data->iov_len, IMDS_USERDATA_TRUNCATED_MAX), /* line_break= */ SIZE_MAX, &data_encoded) < 0)
+                return log_oom();
+
+        _cleanup_free_ char *word = strjoin("imds-userdata:", hash, ":", data_encoded);
+        if (!word)
+                return log_oom();
+
+        *ret = TAKE_PTR(word);
+        return 0;
+}
+
+int pcrextend_imds_userdata_now(const struct iovec *data) {
+
+#if HAVE_TPM2
+        int r;
+
+        _cleanup_free_ char *word = NULL;
+        r = pcrextend_imds_userdata_word(data, &word);
+        if (r < 0)
+                return r;
+
+        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.PCRExtend");
+        if (r < 0)
+                return r;
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
+        const char *error_id = NULL;
+        r = sd_varlink_callbo(
+                        vl,
+                        "io.systemd.PCRExtend.Extend",
+                        /* ret_reply= */ NULL,
+                        &error_id,
+                        SD_JSON_BUILD_PAIR_INTEGER("pcr", TPM2_PCR_KERNEL_CONFIG),
+                        SD_JSON_BUILD_PAIR_STRING("text", word),
+                        SD_JSON_BUILD_PAIR_STRING("eventType", "imds_userdata"));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %m");
+        if (error_id) {
+                r = sd_varlink_error_to_errno(error_id, reply);
+                if (r != -EBADR)
+                        return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %m");
+
+                return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %s", error_id);
+        }
+
+        log_debug("Measurement of '%s' into PCR 12 completed.", word);
+        return 1;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring IMDS userdata.");
+#endif
+}
index 00bc5b9b48dc7bbaf0c5ca8f9e3e897e3f134b1a..eadc2d5cffc980814ceda96bd4829709a3a305f1 100644 (file)
@@ -1,9 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <sys/uio.h>
+
 int pcrextend_file_system_word(const char *path, char **ret, char **ret_normalized_path);
 int pcrextend_machine_id_word(char **ret);
 int pcrextend_product_id_word(char **ret);
 int pcrextend_verity_word(const char *name, const struct iovec *root_hash, const struct iovec *root_hash_sig, char **ret);
+int pcrextend_imds_userdata_word(const struct iovec *data, char **ret);
 
-int pcrextend_verity_now(const char *name, const struct iovec *root_hash,const struct iovec *root_hash_sig);
+int pcrextend_verity_now(const char *name, const struct iovec *root_hash, const struct iovec *root_hash_sig);
+int pcrextend_imds_userdata_now(const struct iovec *data);
index cfa057c02ba7a9497c1c212e450f322db2f649f7..47a6a309ddb478d2f3712c5b94fb785ab8b01d3c 100644 (file)
@@ -6675,6 +6675,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA
         [TPM2_EVENT_NVPCR_INIT]      = "nvpcr-init",
         [TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
         [TPM2_EVENT_DM_VERITY]       = "dm-verity",
+        [TPM2_EVENT_IMDS_USERDATA]   = "imds-userdata",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
index 51670e8b061a8f8569c9e25ca2ef9b721f1a70dd..841f33b8deaa3a88624bdf513686b52e8de34f69 100644 (file)
@@ -148,6 +148,7 @@ typedef enum Tpm2UserspaceEventType {
         TPM2_EVENT_NVPCR_INIT,
         TPM2_EVENT_NVPCR_SEPARATOR,
         TPM2_EVENT_DM_VERITY,
+        TPM2_EVENT_IMDS_USERDATA,
         _TPM2_USERSPACE_EVENT_TYPE_MAX,
         _TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
 } Tpm2UserspaceEventType;
index 87edec349ef80a26e411fbaa852319ebe83386ed..d309330f405a6c53b5bc96249c97e6ab839bcf55 100644 (file)
@@ -12,7 +12,8 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
                 SD_VARLINK_DEFINE_ENUM_VALUE(keyslot),
                 SD_VARLINK_DEFINE_ENUM_VALUE(nvpcr_init),
                 SD_VARLINK_DEFINE_ENUM_VALUE(nvpcr_separator),
-                SD_VARLINK_DEFINE_ENUM_VALUE(dm_verity));
+                SD_VARLINK_DEFINE_ENUM_VALUE(dm_verity),
+                SD_VARLINK_DEFINE_ENUM_VALUE(imds_userdata));
 
 static SD_VARLINK_DEFINE_METHOD(
                 Extend,