From deeae10e26aaa0114bc008285c5dfc721716dbc8 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 26 Apr 2023 10:04:08 +0200 Subject: [PATCH 1/5] repart: Turn condition into assert format_verity_hash() should only be called with VERITY_HASH type partitions, so assert() if that's not the case. --- src/partition/repart.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 3a4808d6a4..448513ff4d 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3559,6 +3559,7 @@ static int partition_format_verity_hash( assert(context); assert(p); + assert(p->verity == VERITY_HASH); assert(data_node); if (p->dropped) @@ -3567,9 +3568,6 @@ static int partition_format_verity_hash( if (PARTITION_EXISTS(p)) /* Never format existing partitions */ return 0; - if (p->verity != VERITY_HASH) - return 0; - if (partition_defer(p)) return 0; From e463e257c70120a66324abfaa11ef7fe4319a697 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 26 Apr 2023 10:09:10 +0200 Subject: [PATCH 2/5] repart: Move partition_defer() out of format_verity_hash/sig() To allow re-using format_verity_hash() for minimizing verity hash partitions. --- src/partition/repart.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 448513ff4d..08ca4f4c0b 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3568,9 +3568,6 @@ static int partition_format_verity_hash( if (PARTITION_EXISTS(p)) /* Never format existing partitions */ return 0; - if (partition_defer(p)) - return 0; - assert_se(dp = p->siblings[VERITY_DATA]); assert(!dp->dropped); @@ -3711,9 +3708,6 @@ static int partition_format_verity_sig(Context *context, Partition *p) { if (PARTITION_EXISTS(p)) return 0; - if (partition_defer(p)) - return 0; - assert_se(hp = p->siblings[VERITY_HASH]); assert(!hp->dropped); @@ -3814,14 +3808,14 @@ static int context_copy_blocks(Context *context) { log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path); - if (p->siblings[VERITY_HASH]) { + if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], partition_target_path(t)); if (r < 0) return r; } - if (p->siblings[VERITY_SIG]) { + if (p->siblings[VERITY_SIG] && !partition_defer(p->siblings[VERITY_SIG])) { r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]); if (r < 0) return r; @@ -4232,14 +4226,14 @@ static int context_mkfs(Context *context) { if (r < 0) return r; - if (p->siblings[VERITY_HASH]) { + if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], partition_target_path(t)); if (r < 0) return r; } - if (p->siblings[VERITY_SIG]) { + if (p->siblings[VERITY_SIG] && !partition_defer(p->siblings[VERITY_SIG])) { r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]); if (r < 0) return r; From 13bde177e4423a2f2646bbda724eb57ee7d7a3a0 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 26 Apr 2023 10:16:21 +0200 Subject: [PATCH 3/5] repart: Allow passing target to format_verity_hash() Let's allow specifying where the verity hash data should be written as preparation for adding verity hash partition minimize support. --- src/partition/repart.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 08ca4f4c0b..02b09c175c 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3547,6 +3547,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) { static int partition_format_verity_hash( Context *context, Partition *p, + const char *node, const char *data_node) { #if HAVE_LIBCRYPTSETUP @@ -3575,11 +3576,15 @@ static int partition_format_verity_hash( if (r < 0) return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m"); - r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t); - if (r < 0) - return r; + if (!node) { + r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t); + if (r < 0) + return r; - r = sym_crypt_init(&cd, partition_target_path(t)); + node = partition_target_path(t); + } + + r = sym_crypt_init(&cd, node); if (r < 0) return log_error_errno(r, "Failed to allocate libcryptsetup context: %m"); @@ -3608,9 +3613,11 @@ static int partition_format_verity_hash( return log_error_errno(r, "Failed to setup verity hash data: %m"); } - r = partition_target_sync(context, p, t); - if (r < 0) - return r; + if (t) { + r = partition_target_sync(context, p, t); + if (r < 0) + return r; + } r = sym_crypt_get_volume_key_size(cd); if (r < 0) @@ -3810,7 +3817,7 @@ static int context_copy_blocks(Context *context) { if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], - partition_target_path(t)); + /* node = */ NULL, partition_target_path(t)); if (r < 0) return r; } @@ -4228,7 +4235,7 @@ static int context_mkfs(Context *context) { if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) { r = partition_format_verity_hash(context, p->siblings[VERITY_HASH], - partition_target_path(t)); + /* node = */ NULL, partition_target_path(t)); if (r < 0) return r; } From cf18d96f62d201fc0240b2fd678a4c61f68f821e Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 26 Apr 2023 11:14:08 +0200 Subject: [PATCH 4/5] repart: Extend error logging for format_verity_hash/sig() --- src/partition/repart.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 02b09c175c..dc1de1207c 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3554,6 +3554,7 @@ static int partition_format_verity_hash( Partition *dp; _cleanup_(partition_target_freep) PartitionTarget *t = NULL; _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; + _cleanup_free_ char *hint = NULL; _cleanup_free_ uint8_t *rh = NULL; size_t rhs; int r; @@ -3572,6 +3573,8 @@ static int partition_format_verity_hash( assert_se(dp = p->siblings[VERITY_DATA]); assert(!dp->dropped); + (void) partition_hint(p, node, &hint); + r = dlopen_cryptsetup(); if (r < 0) return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m"); @@ -3586,7 +3589,7 @@ static int partition_format_verity_hash( r = sym_crypt_init(&cd, node); if (r < 0) - return log_error_errno(r, "Failed to allocate libcryptsetup context: %m"); + return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node); cryptsetup_enable_logging(cd); @@ -3607,10 +3610,10 @@ static int partition_format_verity_hash( * partition is too small. */ if (r == -EIO && errno == ENOSPC) return log_error_errno(errno, - "Verity hash data does not fit in partition %"PRIu64" with size %s", - p->partno, FORMAT_BYTES(p->new_size)); + "Verity hash data does not fit in partition %s with size %s", + strna(hint), FORMAT_BYTES(p->new_size)); - return log_error_errno(r, "Failed to setup verity hash data: %m"); + return log_error_errno(r, "Failed to setup verity hash data of partition %s: %m", strna(hint)); } if (t) { @@ -3621,7 +3624,7 @@ static int partition_format_verity_hash( r = sym_crypt_get_volume_key_size(cd); if (r < 0) - return log_error_errno(r, "Failed to determine verity root hash size: %m"); + return log_error_errno(r, "Failed to determine verity root hash size of partition %s: %m", strna(hint)); rhs = (size_t) r; rh = malloc(rhs); @@ -3630,7 +3633,7 @@ static int partition_format_verity_hash( r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0); if (r < 0) - return log_error_errno(r, "Failed to get verity root hash: %m"); + return log_error_errno(r, "Failed to get verity root hash of partition %s: %m", strna(hint)); assert(rhs >= sizeof(sd_id128_t) * 2); @@ -3701,7 +3704,7 @@ static int sign_verity_roothash( static int partition_format_verity_sig(Context *context, Partition *p) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ uint8_t *sig = NULL; - _cleanup_free_ char *text = NULL; + _cleanup_free_ char *text = NULL, *hint = NULL; Partition *hp; uint8_t fp[X509_FINGERPRINT_SIZE]; size_t sigsz = 0; /* avoid false maybe-uninitialized warning */ @@ -3715,6 +3718,8 @@ static int partition_format_verity_sig(Context *context, Partition *p) { if (PARTITION_EXISTS(p)) return 0; + (void) partition_hint(p, context->node, &hint); + assert_se(hp = p->siblings[VERITY_HASH]); assert(!hp->dropped); @@ -3741,25 +3746,25 @@ static int partition_format_verity_sig(Context *context, Partition *p) { ) ); if (r < 0) - return log_error_errno(r, "Failed to build JSON object: %m"); + return log_error_errno(r, "Failed to build verity signature JSON object: %m"); r = json_variant_format(v, 0, &text); if (r < 0) - return log_error_errno(r, "Failed to format JSON object: %m"); + return log_error_errno(r, "Failed to format verity signature JSON object: %m"); r = strgrowpad0(&text, p->new_size); if (r < 0) return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(p->new_size)); if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1) - return log_error_errno(errno, "Failed to seek to partition offset: %m"); + return log_error_errno(errno, "Failed to seek to partition %s offset: %m", strna(hint)); r = loop_write(whole_fd, text, p->new_size, /*do_poll=*/ false); if (r < 0) - return log_error_errno(r, "Failed to write verity signature to partition: %m"); + return log_error_errno(r, "Failed to write verity signature to partition %s: %m", strna(hint)); if (fsync(whole_fd) < 0) - return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m"); + return log_error_errno(errno, "Failed to synchronize partition %s: %m", strna(hint)); return 0; } From 5eef70473498cac127759e6326aed4e990a5645f Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 26 Apr 2023 11:52:54 +0200 Subject: [PATCH 5/5] repart: Add Minimize= support for verity hash partitions Fixes #27414 --- src/partition/repart.c | 100 +++++++++++++++++++++++++++++++++---- test/units/testsuite-58.sh | 2 + 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index dc1de1207c..d6db6ee73e 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -1676,13 +1676,13 @@ static int partition_read_definition(Partition *p, const char *path, const char return log_oom(); } - if (p->minimize != MINIMIZE_OFF && !p->format) + if (p->minimize != MINIMIZE_OFF && !p->format && p->verity != VERITY_HASH) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), - "Minimize= can only be enabled if Format= is set"); + "Minimize= can only be enabled if Format= or Verity=hash are set"); - if (p->minimize == MINIMIZE_BEST && !fstype_is_ro(p->format)) + if (p->minimize == MINIMIZE_BEST && (p->format && !fstype_is_ro(p->format)) && p->verity != VERITY_HASH) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), - "Minimize=best can only be used with read-only filesystems"); + "Minimize=best can only be used with read-only filesystems or Verity=hash"); if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && !mkfs_supports_root_option(p->format) && geteuid() != 0) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM), @@ -1874,6 +1874,24 @@ static int context_read_definitions( } } + LIST_FOREACH(partitions, p, context->partitions) { + Partition *dp; + + if (p->verity != VERITY_HASH) + continue; + + if (p->minimize == MINIMIZE_OFF) + continue; + + assert_se(dp = p->siblings[VERITY_DATA]); + + if (dp->minimize == MINIMIZE_OFF && !(p->copy_blocks_path || p->copy_blocks_auto)) + return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL), + "Minimize= set for verity hash partition but data partition does " + "not set CopyBlocks= or Minimize="); + + } + return 0; } @@ -3570,6 +3588,10 @@ 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 let's make sure to skip those here. */ + if (p->copy_blocks_fd >= 0) + return 0; + assert_se(dp = p->siblings[VERITY_DATA]); assert(!dp->dropped); @@ -5312,7 +5334,9 @@ static int context_open_copy_block_paths( uint64_t size; struct stat st; - assert(p->copy_blocks_fd < 0); + if (p->copy_blocks_fd >= 0) + continue; + assert(p->copy_blocks_size == UINT64_MAX); if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */ @@ -5623,6 +5647,58 @@ static int context_minimize(Context *context) { p->copy_blocks_path_is_our_file = true; } + /* Now that we've done the data partitions, do the verity hash partitions. We do these in a separate + * step because they might depend on data generated in the previous step. */ + + LIST_FOREACH(partitions, p, context->partitions) { + _cleanup_(unlink_and_freep) char *temp = NULL; + _cleanup_free_ char *hint = NULL; + struct stat st; + Partition *dp; + + if (p->dropped) + continue; + + if (PARTITION_EXISTS(p)) /* Never format existing partitions */ + continue; + + if (p->minimize == MINIMIZE_OFF) + continue; + + if (p->verity != VERITY_HASH) + continue; + + assert_se(dp = p->siblings[VERITY_DATA]); + assert(!dp->dropped); + assert(dp->copy_blocks_path); + + (void) partition_hint(p, context->node, &hint); + + log_info("Pre-populating verity hash data of partition %s to calculate minimal partition size", + strna(hint)); + + r = tempfn_random_child(vt, "repart", &temp); + if (r < 0) + return log_error_errno(r, "Failed to generate temporary file path: %m"); + + r = touch(temp); + if (r < 0) + return log_error_errno(r, "Failed to create temporary file: %m"); + + r = partition_format_verity_hash(context, p, temp, dp->copy_blocks_path); + if (r < 0) + return r; + + if (stat(temp, &st) < 0) + return log_error_errno(r, "Failed to stat temporary file: %m"); + + log_info("Minimal partition size of verity hash partition %s is %s", + strna(hint), FORMAT_BYTES(st.st_size)); + + p->copy_blocks_path = TAKE_PTR(temp); + p->copy_blocks_path_is_our_file = true; + } + return 0; } @@ -6685,10 +6761,6 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - r = context_minimize(context); - if (r < 0) - return r; - /* Open all files to copy blocks from now, since we want to take their size into consideration */ r = context_open_copy_block_paths( context, @@ -6698,6 +6770,16 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + r = context_minimize(context); + if (r < 0) + return r; + + /* We might have gotten more copy blocks paths to open during the minimize process, so let's make + * sure we open those as well. These should all be regular files, so don't allow any block devices. */ + r = context_open_copy_block_paths(context, 0); + if (r < 0) + return r; + if (arg_size_auto) { r = determine_auto_size(context); if (r < 0) diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 83d7757a13..7c05d8c71f 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -765,6 +765,7 @@ Type=root-${architecture} CopyFiles=${defs} Verity=data VerityMatchKey=root +Minimize=guess EOF runas testuser tee "$defs/verity-hash.conf" <