]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: guess verity root hash from the resources we found
authorLennart Poettering <lennart@poettering.net>
Fri, 14 Mar 2025 10:57:34 +0000 (11:57 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 3 Apr 2025 09:08:57 +0000 (11:08 +0200)
When dissecting an image, let's make use of the Verity data even if we
got told no root hash explicitly: we can simply determine it by
concatenating the data partition uuid with the verity partition uuid.

Of course, on first thought this doesn't really add much: if the root
hash is not pinned from somewhere, this does not guarantee trust in
the image.

However, this is very useful for attestation: if we have the root hash
we can measure it before mounting things, even if we don't actually
authenticate it.

Hence, at best this helps us with attestation, at worst it doesn't improve
security but certainly doesn't hurt it.

docs/ENVIRONMENT.md
src/core/namespace.c
src/dissect/dissect.c
src/mountfsd/mountwork.c
src/nspawn/nspawn.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/sysext/sysext.c
src/udev/udev-builtin-dissect_image.c

index c340e01d0168c9ab919ab9d707427613a71c6b80..55f6d22b49e817b5f86fd3548cbc93c40e8c6691 100644 (file)
@@ -529,6 +529,14 @@ disk images with `--image=` or similar:
   images. Defaults to true, i.e. userspace signature validation is allowed. If
   false, authentication can be done only via the kernel's internal keyring.
 
+* `$SYSTEMD_DISSECT_VERITY_GUESS` – takes a boolean. Controls whether to guess
+  the Verity root hash from the partition UUIDs of a suitable pair of data
+  partition and matching Verity partition: the UUIDs two are simply joined and
+  used as root hash, in accordance with the recommendations in [Discoverable
+  Partitions
+  Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification). Defaults
+  to true.
+
 `systemd-cryptsetup`:
 
 * `$SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE` – takes a boolean, which controls
index 7e131b14250f382fee8bdaaff955c6f3c3968e5e..fac3c05f61e982bfa811f0aa316c03a629015ab5 100644 (file)
@@ -2364,6 +2364,12 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
                         if (r < 0)
                                 return r;
 
+                        r = dissected_image_guess_verity_roothash(
+                                        dissected_image,
+                                        p->verity);
+                        if (r < 0)
+                                return r;
+
                         r = dissected_image_decrypt(
                                         dissected_image,
                                         NULL,
index fc03df5cc68dd24dd141d5f2f5349cd8c03fb932..06d6d3935f15a00d83f22dcdd1482d67acb39e8c 100644 (file)
@@ -2177,8 +2177,10 @@ static int run(int argc, char *argv[]) {
 
         if (arg_image) {
                 r = verity_settings_load(
-                        &arg_verity_settings,
-                        arg_image, NULL, NULL);
+                                &arg_verity_settings,
+                                arg_image,
+                                /* root_hash_path= */ NULL,
+                                /* root_hash_sig_path= */ NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
 
@@ -2244,6 +2246,12 @@ static int run(int argc, char *argv[]) {
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to load verity signature partition: %m");
 
+                                r = dissected_image_guess_verity_roothash(
+                                                m,
+                                                &arg_verity_settings);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to guess verity root hash: %m");
+
                                 if (arg_action != ACTION_DISSECT) {
                                         r = dissected_image_decrypt_interactively(
                                                         m, NULL,
index 41c1d65391b5018086a10d3ee7cfe17760c9cc79..adbb91d8a0da0585cf622c049ce52b86f423dae0 100644 (file)
@@ -461,6 +461,12 @@ static int vl_method_mount_image(
         if (r < 0)
                 return r;
 
+        r = dissected_image_guess_verity_roothash(
+                        di,
+                        &verity);
+        if (r < 0)
+                return r;
+
         r = dissected_image_decrypt(
                         di,
                         p.password,
index 3004bfc1b7d1bd48f8bbcb235c313c930e5631cc..32796375c541fe993495d791876cd0a32722ead1 100644 (file)
@@ -6391,10 +6391,20 @@ static int run(int argc, char *argv[]) {
                                         dissected_image,
                                         loop->fd,
                                         &arg_verity_settings);
-                        if (r < 0)
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to load Verity signature partition: %m");
                                 goto finish;
+                        }
+
+                        r = dissected_image_guess_verity_roothash(
+                                        dissected_image,
+                                        &arg_verity_settings);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to guess Verity root hash: %m");
+                                goto finish;
+                        }
 
-                        if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
+                        if (dissected_image->has_verity && !arg_verity_settings.root_hash)
                                 log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
                                            "root hash signature found! Proceeding without integrity checking.", arg_image);
 
index 27509515f0cbf04d1ac416551719400b6f9c5ec6..c8e6f8f121f758b5e97032b1f02ef951a695b8db 100644 (file)
@@ -3525,6 +3525,69 @@ int dissected_image_load_verity_sig_partition(
         return 1;
 }
 
+int dissected_image_guess_verity_roothash(
+                DissectedImage *m,
+                VeritySettings *verity) {
+
+        int r;
+
+        assert(m);
+        assert(verity);
+
+        /* Guesses the Verity root hash from the partitions we found, taking into account that as per
+         * https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ the UUIDS of
+         * the data and verity partitions are respectively the first and second halves of the dm-verity
+         * roothash.
+         *
+         * Note of course that relying on this guesswork is mostly useful for later attestation, not so much
+         * for a-priori security. */
+
+        if (verity->root_hash) /* Already loaded? */
+                return 0;
+
+        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_GUESS");
+        if (r < 0 && r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_GUESS, ignoring: %m");
+        if (r == 0)
+                return 0;
+
+        PartitionDesignator dd = verity->designator;
+        if (dd < 0) {
+                if (m->partitions[PARTITION_ROOT_VERITY].found)
+                        dd = PARTITION_ROOT;
+                else if (m->partitions[PARTITION_USR_VERITY].found)
+                        dd = PARTITION_USR;
+                else
+                        return 0;
+        }
+
+        DissectedPartition *d = m->partitions + dd;
+        if (!d->found)
+                return 0;
+
+        PartitionDesignator dv = partition_verity_of(dd);
+        assert(dv >= 0);
+
+        DissectedPartition *p = m->partitions + dv;
+        if (!p->found)
+                return 0;
+
+        _cleanup_free_ uint8_t *rh = malloc(sizeof(sd_id128_t) * 2);
+        if (!rh)
+                return log_oom_debug();
+
+        memcpy(mempcpy(rh, &d->uuid, sizeof(sd_id128_t)), &p->uuid, sizeof(sd_id128_t));
+        verity->root_hash = TAKE_PTR(rh);
+        verity->root_hash_size = sizeof(sd_id128_t) * 2;
+
+        verity->designator = dd;
+
+        m->verity_ready = true;
+        m->partitions[dd].rw = false;
+
+        return 0;
+}
+
 int dissected_image_acquire_metadata(
                 DissectedImage *m,
                 int userns_fd,
@@ -4038,6 +4101,10 @@ int mount_image_privately_interactively(
         if (r < 0)
                 return r;
 
+        r = dissected_image_guess_verity_roothash(dissected_image, &verity);
+        if (r < 0)
+                return r;
+
         r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags);
         if (r < 0)
                 return r;
@@ -4181,6 +4248,10 @@ int verity_dissect_and_mount(
         if (r < 0)
                 return r;
 
+        r = dissected_image_guess_verity_roothash(dissected_image, verity);
+        if (r < 0)
+                return r;
+
         r = dissected_image_decrypt(
                         dissected_image,
                         NULL,
index dbdd13b5aee257d0fc66f51af4d91e611077f23e..8725ff9921ad8c61ab07a616b0f7e9baee9790b7 100644 (file)
@@ -229,6 +229,7 @@ static inline bool verity_settings_data_covers(const VeritySettings *verity, Par
 int verity_settings_copy(VeritySettings *dest, const VeritySettings *source);
 
 int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
+int dissected_image_guess_verity_roothash(DissectedImage *m, VeritySettings *verity);
 
 bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
 bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator d);
index 420262cd452261b249fc808bc37bf18e33087d58..d2bb992d1482244286ce09c90e5b59fb9cb9d4ed 100644 (file)
@@ -1814,6 +1814,12 @@ static int merge_subprocess(
                         if (r < 0)
                                 return r;
 
+                        r = dissected_image_guess_verity_roothash(
+                                        m,
+                                        &verity_settings);
+                        if (r < 0)
+                                return r;
+
                         r = dissected_image_decrypt_interactively(
                                         m, NULL,
                                         &verity_settings,
index 9ba58cdadd07d36dd177507f0850346e783b57e4..3a598952acadb49c8c9fc89882a5690d622217f1 100644 (file)
@@ -196,6 +196,10 @@ static int verb_probe(UdevEvent *event, sd_device *dev) {
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to load verity signature data from image: %m");
 
+        r = dissected_image_guess_verity_roothash(image, &verity);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to guess root hash from image: %m");
+
         /* Marker that we determined this to be a suitable image */
         (void) udev_builtin_add_property(event, "ID_DISSECT_IMAGE", "1");