diff --git a/src/imds/imds-tool.c b/src/imds/imds-tool.c index d4a5b6b6eb..4ae8dbb33c 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 8586e85cbb..7af436217d 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 00bc5b9b48..eadc2d5cff 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 cfa057c02b..47a6a309dd 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 51670e8b06..841f33b8de 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 87edec349e..d309330f40 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,