From: Lennart Poettering Date: Wed, 11 Feb 2026 12:11:38 +0000 (+0100) Subject: pcrextend-util: add helpers for measuring roothash/signature of Verity volumes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=71ca7532de1cf56ed3c1360c8db3ea2bee0f9ace;p=thirdparty%2Fsystemd.git pcrextend-util: add helpers for measuring roothash/signature of Verity volumes This adds infrastructure for measuring Verity root hashes from userspace, along with he issuer/serial of the signatures used to unlock them. We measure the triplet of volume name, root hash and issuer/serial. if confext/sysext use different signing keys then this ensures the event log carry information about the type of image measures. --- diff --git a/src/shared/pcrextend-util.c b/src/shared/pcrextend-util.c index 15e0da822a6..e4d3b958a87 100644 --- a/src/shared/pcrextend-util.c +++ b/src/shared/pcrextend-util.c @@ -2,6 +2,7 @@ #include "sd-device.h" #include "sd-id128.h" +#include "sd-varlink.h" #include "alloc-util.h" #include "blkid-util.h" @@ -10,10 +11,13 @@ #include "errno-util.h" #include "escape.h" #include "fd-util.h" +#include "hexdecoct.h" #include "id128-util.h" +#include "iovec-util.h" #include "log.h" #include "mountpoint-util.h" #include "pcrextend-util.h" +#include "pkcs7-util.h" #include "string-util.h" #include "strv.h" @@ -180,3 +184,110 @@ int pcrextend_product_id_word(char **ret) { *ret = TAKE_PTR(word); return 0; } + +int pcrextend_verity_word( + const char *name, + const struct iovec *root_hash, + const struct iovec *root_hash_sig, + char **ret) { + + int r; + + assert(name); + assert(iovec_is_set(root_hash)); + + _cleanup_free_ char *name_escaped = xescape(name, ":"); /* Avoid ambiguity around ":" */ + if (!name_escaped) + return log_oom(); + + _cleanup_free_ char *h = hexmem(root_hash->iov_base, root_hash->iov_len); + if (!h) + return log_oom(); + + _cleanup_free_ char *sigs = NULL; + if (iovec_is_set(root_hash_sig)) { + size_t n_signers = 0; + Signer *signers = NULL; + + /* Let's extract the X.509 issuer + serial number from the PKCS#7 signature and include that + * in the measurement record. This is useful since it allows us to have different signing + * keys for confext + sysext + other types of DDIs, and by means of this information we can + * discern which kind it was. Ideally, we'd measure the fingerprint of the X.509 certificate, + * but typically that's not available in a PKCS#7 signature. */ + + CLEANUP_ARRAY(signers, n_signers, signer_free_many); + + r = pkcs7_extract_signers(root_hash_sig, &signers, &n_signers); + if (r < 0) + return r; + + FOREACH_ARRAY(i, signers, n_signers) { + _cleanup_free_ char *serial = hexmem(i->serial.iov_base, i->serial.iov_len); + if (!serial) + return log_oom(); + + _cleanup_free_ char *issuer = NULL; + if (base64mem(i->issuer.iov_base, i->issuer.iov_len, &issuer) < 0) + return log_oom(); + + if (strextendf_with_separator(&sigs, ",", "%s/%s", serial, issuer) < 0) + return log_oom(); + } + } + + _cleanup_free_ char *word = strjoin("verity:", name_escaped, ":", h, ":", strempty(sigs)); + if (!word) + return log_oom(); + + *ret = TAKE_PTR(word); + return 0; +} + +int pcrextend_verity_now( + const char *name, + const struct iovec *root_hash, + const struct iovec *root_hash_sig) { + +#if HAVE_TPM2 + int r; + + _cleanup_free_ char *word = NULL; + r = pcrextend_verity_word( + name, + root_hash, + root_hash_sig, + &word); + if (r < 0) + return r; + + _cleanup_free_ 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_STRING("nvpcr", "verity"), + SD_JSON_BUILD_PAIR_STRING("text", word), + SD_JSON_BUILD_PAIR_STRING("eventType", "dm_verity")); + 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 'images' NvPCR completed.", word); + return 1; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring Verity root hashes and signatures."); +#endif +} diff --git a/src/shared/pcrextend-util.h b/src/shared/pcrextend-util.h index 88bd23c10d9..00bc5b9b48d 100644 --- a/src/shared/pcrextend-util.h +++ b/src/shared/pcrextend-util.h @@ -4,3 +4,6 @@ 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_verity_now(const char *name, const struct iovec *root_hash,const struct iovec *root_hash_sig); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index c751afb06b4..3b559bf84c2 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -5441,7 +5441,7 @@ int tpm2_seal(Tpm2Context *c, seal_key_handle); primary_alg = primary_public->publicArea.type; - + /* Propagate fixedTPM/fixedParent flags from sealing key to hmac key */ hmac_template.objectAttributes = (hmac_template.objectAttributes & ~(TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT)) | (primary_public->publicArea.objectAttributes & (TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT)); @@ -6478,6 +6478,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA [TPM2_EVENT_KEYSLOT] = "keyslot", [TPM2_EVENT_NVPCR_INIT] = "nvpcr-init", [TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator", + [TPM2_EVENT_DM_VERITY] = "dm-verity", }; DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType); diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 6827719e865..68289bec48a 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -145,6 +145,7 @@ typedef enum Tpm2UserspaceEventType { TPM2_EVENT_KEYSLOT, TPM2_EVENT_NVPCR_INIT, TPM2_EVENT_NVPCR_SEPARATOR, + TPM2_EVENT_DM_VERITY, _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 9179668d7dc..87edec349ef 100644 --- a/src/shared/varlink-io.systemd.PCRExtend.c +++ b/src/shared/varlink-io.systemd.PCRExtend.c @@ -11,7 +11,8 @@ static SD_VARLINK_DEFINE_ENUM_TYPE( SD_VARLINK_DEFINE_ENUM_VALUE(product_id), 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(nvpcr_separator), + SD_VARLINK_DEFINE_ENUM_VALUE(dm_verity)); static SD_VARLINK_DEFINE_METHOD( Extend,