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.
This commit is contained in:
Lennart Poettering
2025-03-14 11:57:34 +01:00
parent 923eabf085
commit e34c89897a
9 changed files with 124 additions and 4 deletions

View 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

View 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,

View 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,

View 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,

View 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;
}
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);

View 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,

View 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);

View 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,

View 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");