diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index c340e01d01..55f6d22b49 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 7e131b1425..fac3c05f61 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 fc03df5cc6..06d6d3935f 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 41c1d65391..adbb91d8a0 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 3004bfc1b7..32796375c5 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; + } - if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig) + 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) 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 27509515f0..c8e6f8f121 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 dbdd13b5ae..8725ff9921 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 420262cd45..d2bb992d14 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 9ba58cdadd..3a598952ac 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");