imds: add TPM measurements to imds tool

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.)
This commit is contained in:
Lennart Poettering
2026-03-26 10:54:15 +01:00
parent 1228660400
commit 19a783f4dd
6 changed files with 82 additions and 2 deletions
+4
View 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.) */
+69
View File
@@ -18,8 +18,10 @@
#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
}
+5 -1
View 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);
+1
View 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);
+1
View 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;
+2 -1
View 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,