From: Lennart Poettering Date: Tue, 24 Mar 2026 08:41:03 +0000 (+0100) Subject: imds: add TPM measurements to imds tool X-Git-Tag: v261-rc1~721^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19a783f4dd558a7b83dee7f950f393b29a452bc1;p=thirdparty%2Fsystemd.git 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.) --- diff --git a/src/imds/imds-tool.c b/src/imds/imds-tool.c index d4a5b6b6eb3..4ae8dbb33ce 100644 --- a/src/imds/imds-tool.c +++ b/src/imds/imds-tool.c @@ -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.) */ diff --git a/src/shared/pcrextend-util.c b/src/shared/pcrextend-util.c index 8586e85cbbd..7af436217d5 100644 --- a/src/shared/pcrextend-util.c +++ b/src/shared/pcrextend-util.c @@ -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 +} diff --git a/src/shared/pcrextend-util.h b/src/shared/pcrextend-util.h index 00bc5b9b48d..eadc2d5cffc 100644 --- a/src/shared/pcrextend-util.h +++ b/src/shared/pcrextend-util.h @@ -1,9 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + 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); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index cfa057c02ba..47a6a309ddb 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -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); diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 51670e8b061..841f33b8dea 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -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; diff --git a/src/shared/varlink-io.systemd.PCRExtend.c b/src/shared/varlink-io.systemd.PCRExtend.c index 87edec349ef..d309330f405 100644 --- a/src/shared/varlink-io.systemd.PCRExtend.c +++ b/src/shared/varlink-io.systemd.PCRExtend.c @@ -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,