Support CopyBlocks= for Verity={hash,sig} (#41393)
This enables deriving the minimum size of the `Verity=hash` partition
using the `Verity=` logic when the size of the `Verity=data` partition
is bigger than the `CopyBlocks=` target.
This enables using `Minimize=true` for an "installer image" and later
using sd-repart to install to a system with reserve space for future
updates by specifying `Size{Min,Max}Bytes=` only in the `Verity=data`
partition, without needing to hardcode the corresponding size for the
`Verity=hash` partition.
While not strictly necessary for `Verity=signature` partitions (since
they have a fixed size) there isn't too much reason to not support it,
since then you can still specify `VerityMatchKey=` to indicate that the
partition is logically still part of that group of partitions.
---
Alternative to: https://github.com/systemd/systemd/pull/41156
Fixes https://github.com/systemd/systemd/issues/40995
This commit is contained in:
+77
-11
@@ -3012,10 +3012,20 @@ static int partition_read_definition(
|
||||
"VerityMatchKey= can only be set if Verity= is not \"%s\".",
|
||||
verity_mode_to_string(p->verity));
|
||||
|
||||
if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) && (p->copy_blocks_path || p->copy_blocks_auto || p->format || partition_needs_populate(p)))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"CopyBlocks=/CopyFiles=/Format=/MakeDirectories=/MakeSymlinks= cannot be used with Verity=%s.",
|
||||
verity_mode_to_string(p->verity));
|
||||
if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG)) {
|
||||
if (p->format || partition_needs_populate(p))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"CopyFiles=/Format=/MakeDirectories=/MakeSymlinks= cannot be used with Verity=%s.",
|
||||
verity_mode_to_string(p->verity));
|
||||
|
||||
/* Later we check that the same CopyBlocks= type (auto vs path) is used for the entire verity set.
|
||||
* So we assume that CopyBlocks=auto is going to be correct and just path based blocks might result in
|
||||
* a broken setup */
|
||||
if (p->copy_blocks_path)
|
||||
log_syntax(NULL, LOG_DEBUG, path, 1, 0,
|
||||
"CopyBlocks= with Verity=%s bypasses dm-verity hash/signature computation; repart cannot verify the resulting setup is correct.",
|
||||
verity_mode_to_string(p->verity));
|
||||
}
|
||||
|
||||
if (p->verity != VERITY_OFF && p->encrypt != ENCRYPT_OFF)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
@@ -3516,6 +3526,27 @@ static int context_read_definitions(Context *context) {
|
||||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
if (!IN_SET(p->verity, VERITY_HASH, VERITY_SIG))
|
||||
continue;
|
||||
|
||||
/* We check all verity siblings up until our current type and ensure that if we are using CopyBlocks=
|
||||
* the previous ones are using the same type of CopyBlocks=. */
|
||||
for (VerityMode mode = VERITY_DATA; mode < p->verity; mode++) {
|
||||
Partition *q = ASSERT_PTR(p->siblings[mode]);
|
||||
|
||||
if (p->copy_blocks_auto && !q->copy_blocks_auto)
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"CopyBlocks=auto set with Verity=%s but Verity=%s partition does not set CopyBlocks=auto.",
|
||||
verity_mode_to_string(p->verity), verity_mode_to_string(mode));
|
||||
|
||||
if (p->copy_blocks_path && !q->copy_blocks_path)
|
||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"CopyBlocks= set with Verity=%s but Verity=%s partition does not set CopyBlocks=.",
|
||||
verity_mode_to_string(p->verity), verity_mode_to_string(mode));
|
||||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
Partition *dp;
|
||||
|
||||
@@ -5658,7 +5689,7 @@ static int partition_format_verity_hash(
|
||||
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
|
||||
return 0;
|
||||
|
||||
/* Minimized partitions will use the copy blocks logic so skip those here. */
|
||||
/* Either we are minimizing the partition or we were instructed to copy an existing hash block directly. */
|
||||
if (p->copy_blocks_fd >= 0)
|
||||
return 0;
|
||||
|
||||
@@ -5795,21 +5826,41 @@ static int sign_verity_roothash(
|
||||
#endif
|
||||
}
|
||||
|
||||
static const VeritySettings *lookup_verity_settings_by_uuid_pair(sd_id128_t data_uuid, sd_id128_t hash_uuid) {
|
||||
uint8_t root_hash_key[sizeof(sd_id128_t) * 2];
|
||||
static int iovec_roothash_from_uuid_pair(
|
||||
sd_id128_t data_uuid,
|
||||
sd_id128_t hash_uuid,
|
||||
struct iovec *ret_roothash) {
|
||||
|
||||
uint8_t roothash_bytes[sizeof(sd_id128_t) * 2];
|
||||
|
||||
assert(ret_roothash);
|
||||
|
||||
if (sd_id128_is_null(data_uuid) || sd_id128_is_null(hash_uuid))
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
|
||||
/* As per the 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, so we can use them to match the signature to the right partition. */
|
||||
|
||||
memcpy(root_hash_key, data_uuid.bytes, sizeof(sd_id128_t));
|
||||
memcpy(root_hash_key + sizeof(sd_id128_t), hash_uuid.bytes, sizeof(sd_id128_t));
|
||||
memcpy(roothash_bytes, data_uuid.bytes, sizeof(sd_id128_t));
|
||||
memcpy(roothash_bytes + sizeof(sd_id128_t), hash_uuid.bytes, sizeof(sd_id128_t));
|
||||
|
||||
if (!iovec_memdup(&IOVEC_MAKE(roothash_bytes, sizeof(roothash_bytes)), ret_roothash))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VeritySettings *lookup_verity_settings_by_uuid_pair(sd_id128_t data_uuid, sd_id128_t hash_uuid) {
|
||||
_cleanup_(iovec_done) struct iovec roothash = {};
|
||||
int r;
|
||||
|
||||
r = iovec_roothash_from_uuid_pair(data_uuid, hash_uuid, &roothash);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
VeritySettings key = {
|
||||
.root_hash = IOVEC_MAKE(root_hash_key, sizeof(root_hash_key)),
|
||||
.root_hash = roothash,
|
||||
};
|
||||
|
||||
return set_get(arg_verity_settings, &key);
|
||||
@@ -5832,11 +5883,23 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
|
||||
if (PARTITION_EXISTS(p))
|
||||
return 0;
|
||||
|
||||
/* We were instructed to copy an existing signature block directly */
|
||||
if (p->copy_blocks_fd >= 0)
|
||||
return 0;
|
||||
|
||||
assert_se(hp = p->siblings[VERITY_HASH]);
|
||||
assert(!hp->dropped);
|
||||
assert_se(rp = p->siblings[VERITY_DATA]);
|
||||
assert(!rp->dropped);
|
||||
|
||||
/* Currently only set while formatting the hash partition. But if this is skipped via CopyBlocks=
|
||||
* we just derive the roothash from the UUIDs from the data + hash partition. */
|
||||
if (!iovec_is_set(&hp->roothash)) {
|
||||
r = iovec_roothash_from_uuid_pair(rp->new_uuid, hp->new_uuid, &hp->roothash);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to derive roothash: %m");
|
||||
}
|
||||
|
||||
verity_settings = lookup_verity_settings_by_uuid_pair(rp->current_uuid, hp->current_uuid);
|
||||
|
||||
if (!verity_settings) {
|
||||
@@ -9259,6 +9322,9 @@ static int context_minimize(Context *context) {
|
||||
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
|
||||
continue;
|
||||
|
||||
if (p->copy_blocks_fd >= 0)
|
||||
continue;
|
||||
|
||||
if (p->minimize == MINIMIZE_OFF)
|
||||
continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user