diff --git a/src/repart/repart.c b/src/repart/repart.c index 2af567eb6c..118c6dfc07 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -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;