]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrextend-util: add helpers for measuring roothash/signature of Verity volumes
authorLennart Poettering <lennart@amutable.com>
Wed, 11 Feb 2026 12:11:38 +0000 (13:11 +0100)
committerLennart Poettering <lennart@amutable.com>
Tue, 17 Feb 2026 21:00:14 +0000 (22:00 +0100)
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.

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 15e0da822a6ef1c0bf326d2128a1b19674300419..e4d3b958a87c06e2f084827620ce8464398d8c2c 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "sd-device.h"
 #include "sd-id128.h"
+#include "sd-varlink.h"
 
 #include "alloc-util.h"
 #include "blkid-util.h"
 #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
+}
index 88bd23c10d98304199cc80c5dd60a3756615937a..00bc5b9b48dc7bbaf0c5ca8f9e3e897e3f134b1a 100644 (file)
@@ -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);
index c751afb06b4af44355ce7618096fff896b4a05ab..3b559bf84c2bcd7d98a5c83b5c62ca518dcc8b13 100644 (file)
@@ -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);
index 6827719e865e52c6856f4fca90948b028a30e702..68289bec48a1c253878cf5271f174799b18c31ec 100644 (file)
@@ -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;
index 9179668d7dc7f513cb66c84e32a6111579bc0768..87edec349ef80a26e411fbaa852319ebe83386ed 100644 (file)
@@ -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,