From: Lennart Poettering Date: Fri, 14 Mar 2025 10:57:34 +0000 (+0100) Subject: dissect-image: guess verity root hash from the resources we found X-Git-Tag: v258-rc1~923^2~16 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e34c89897af9b0c7af49d0137bf04fa31b172c4a;p=thirdparty%2Fsystemd.git dissect-image: guess verity root hash from the resources we found 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. --- diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index c340e01d016..55f6d22b49e 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -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 diff --git a/src/core/namespace.c b/src/core/namespace.c index 7e131b14250..fac3c05f61e 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -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, diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index fc03df5cc68..06d6d3935f1 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -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, diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c index 41c1d65391b..adbb91d8a0d 100644 --- a/src/mountfsd/mountwork.c +++ b/src/mountfsd/mountwork.c @@ -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, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 3004bfc1b7d..32796375c54 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -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); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 27509515f0c..c8e6f8f121f 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -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, diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index dbdd13b5aee..8725ff9921a 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -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); diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 420262cd452..d2bb992d148 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -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, diff --git a/src/udev/udev-builtin-dissect_image.c b/src/udev/udev-builtin-dissect_image.c index 9ba58cdadd0..3a598952aca 100644 --- a/src/udev/udev-builtin-dissect_image.c +++ b/src/udev/udev-builtin-dissect_image.c @@ -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");