]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Add verity configuration section and options
authorMichael A Cassaniti <michael@cassaniti.id.au>
Fri, 11 Aug 2023 09:30:11 +0000 (19:30 +1000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 15 Aug 2023 14:32:09 +0000 (15:32 +0100)
man/repart.d.xml
src/partition/repart.c
test/TEST-58-REPART/test.sh
test/units/testsuite-58.sh

index 261c673249c2029f8cc88cedabbbb4c64a632e5b..2fe723673eaf99a5bb101b90fc204eff056042bc 100644 (file)
         <varname>Verity=</varname>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>VerityDataBlockSizeBytes=</varname></term>
+
+        <listitem><para>Configures the data block size of the generated verity hash partition. Must be between 512 and
+        4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying
+        block device sector size, or 4K if systemd-repart is not operating on a block device.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>VerityHashBlockSizeBytes=</varname></term>
+
+        <listitem><para>Configures the hash block size of the generated verity hash partition. Must be between 512 and
+        4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying
+        block device sector size, or 4K if systemd-repart is not operating on a block device.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>FactoryReset=</varname></term>
 
@@ -831,6 +849,9 @@ VerityMatchKey=root
 Type=root-verity
 Verity=hash
 VerityMatchKey=root
+# Explicitly set the hash and data block size to 4K
+VerityDataBlockSizeBytes=4096
+VerityHashBlockSizeBytes=4096
 </programlisting></para>
     </example>
 
index 4410cad1e53a7a3d418e267ddb820ad2c1dad3fe..1e7978e97b8391dd00cdeab299ddc7fb28b9df70 100644 (file)
@@ -252,6 +252,8 @@ typedef struct Partition {
         VerityMode verity;
         char *verity_match_key;
         MinimizeMode minimize;
+        uint64_t verity_data_block_size;
+        uint64_t verity_hash_block_size;
 
         uint64_t gpt_flags;
         int no_auto;
@@ -363,6 +365,8 @@ static Partition *partition_new(void) {
                 .no_auto = -1,
                 .read_only = -1,
                 .growfs = -1,
+                .verity_data_block_size = UINT64_MAX,
+                .verity_hash_block_size = UINT64_MAX,
         };
 
         return p;
@@ -1322,6 +1326,40 @@ static int config_parse_size4096(
         return 0;
 }
 
+static int config_parse_block_size(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint64_t *blksz = ASSERT_PTR(data), parsed;
+        int r;
+
+        assert(rvalue);
+
+        r = parse_size(rvalue, 1024, &parsed);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "Failed to parse size value: %s", rvalue);
+
+        if (parsed < 512 || parsed > 4096)
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+                                  "Value not between 512 and 4096: %s", rvalue);
+
+        if (!ISPOWEROF2(parsed))
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+                                  "Value not a power of 2: %s", rvalue);
+
+        *blksz = parsed;
+        return 0;
+}
+
 static int config_parse_fstype(
                 const char *unit,
                 const char *filename,
@@ -1616,33 +1654,35 @@ static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mod
 static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",               config_parse_type,          0, &p->type                 },
-                { "Partition", "Label",              config_parse_label,         0, &p->new_label            },
-                { "Partition", "UUID",               config_parse_uuid,          0, p                        },
-                { "Partition", "Priority",           config_parse_int32,         0, &p->priority             },
-                { "Partition", "Weight",             config_parse_weight,        0, &p->weight               },
-                { "Partition", "PaddingWeight",      config_parse_weight,        0, &p->padding_weight       },
-                { "Partition", "SizeMinBytes",       config_parse_size4096,      1, &p->size_min             },
-                { "Partition", "SizeMaxBytes",       config_parse_size4096,     -1, &p->size_max             },
-                { "Partition", "PaddingMinBytes",    config_parse_size4096,      1, &p->padding_min          },
-                { "Partition", "PaddingMaxBytes",    config_parse_size4096,     -1, &p->padding_max          },
-                { "Partition", "FactoryReset",       config_parse_bool,          0, &p->factory_reset        },
-                { "Partition", "CopyBlocks",         config_parse_copy_blocks,   0, p                        },
-                { "Partition", "Format",             config_parse_fstype,        0, &p->format               },
-                { "Partition", "CopyFiles",          config_parse_copy_files,    0, &p->copy_files           },
-                { "Partition", "ExcludeFiles",       config_parse_exclude_files, 0, &p->exclude_files_source },
-                { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
-                { "Partition", "MakeDirectories",    config_parse_make_dirs,     0, &p->make_directories     },
-                { "Partition", "Encrypt",            config_parse_encrypt,       0, &p->encrypt              },
-                { "Partition", "Verity",             config_parse_verity,        0, &p->verity               },
-                { "Partition", "VerityMatchKey",     config_parse_string,        0, &p->verity_match_key     },
-                { "Partition", "Flags",              config_parse_gpt_flags,     0, &p->gpt_flags            },
-                { "Partition", "ReadOnly",           config_parse_tristate,      0, &p->read_only            },
-                { "Partition", "NoAuto",             config_parse_tristate,      0, &p->no_auto              },
-                { "Partition", "GrowFileSystem",     config_parse_tristate,      0, &p->growfs               },
-                { "Partition", "SplitName",          config_parse_string,        0, &p->split_name_format    },
-                { "Partition", "Minimize",           config_parse_minimize,      0, &p->minimize             },
-                { "Partition", "Subvolumes",         config_parse_make_dirs,     0, &p->subvolumes           },
+                { "Partition", "Type",                     config_parse_type,          0, &p->type                    },
+                { "Partition", "Label",                    config_parse_label,         0, &p->new_label               },
+                { "Partition", "UUID",                     config_parse_uuid,          0, p                           },
+                { "Partition", "Priority",                 config_parse_int32,         0, &p->priority                },
+                { "Partition", "Weight",                   config_parse_weight,        0, &p->weight                  },
+                { "Partition", "PaddingWeight",            config_parse_weight,        0, &p->padding_weight          },
+                { "Partition", "SizeMinBytes",             config_parse_size4096,      1, &p->size_min                },
+                { "Partition", "SizeMaxBytes",             config_parse_size4096,     -1, &p->size_max                },
+                { "Partition", "PaddingMinBytes",          config_parse_size4096,      1, &p->padding_min             },
+                { "Partition", "PaddingMaxBytes",          config_parse_size4096,     -1, &p->padding_max             },
+                { "Partition", "FactoryReset",             config_parse_bool,          0, &p->factory_reset           },
+                { "Partition", "CopyBlocks",               config_parse_copy_blocks,   0, p                           },
+                { "Partition", "Format",                   config_parse_fstype,        0, &p->format                  },
+                { "Partition", "CopyFiles",                config_parse_copy_files,    0, &p->copy_files              },
+                { "Partition", "ExcludeFiles",             config_parse_exclude_files, 0, &p->exclude_files_source    },
+                { "Partition", "ExcludeFilesTarget",       config_parse_exclude_files, 0, &p->exclude_files_target    },
+                { "Partition", "MakeDirectories",          config_parse_make_dirs,     0, &p->make_directories        },
+                { "Partition", "Encrypt",                  config_parse_encrypt,       0, &p->encrypt                 },
+                { "Partition", "Verity",                   config_parse_verity,        0, &p->verity                  },
+                { "Partition", "VerityMatchKey",           config_parse_string,        0, &p->verity_match_key        },
+                { "Partition", "Flags",                    config_parse_gpt_flags,     0, &p->gpt_flags               },
+                { "Partition", "ReadOnly",                 config_parse_tristate,      0, &p->read_only               },
+                { "Partition", "NoAuto",                   config_parse_tristate,      0, &p->no_auto                 },
+                { "Partition", "GrowFileSystem",           config_parse_tristate,      0, &p->growfs                  },
+                { "Partition", "SplitName",                config_parse_string,        0, &p->split_name_format       },
+                { "Partition", "Minimize",                 config_parse_minimize,      0, &p->minimize                },
+                { "Partition", "Subvolumes",               config_parse_make_dirs,     0, &p->subvolumes              },
+                { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size,    0, &p->verity_data_block_size  },
+                { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size,    0, &p->verity_hash_block_size  },
                 {}
         };
         int r;
@@ -3938,6 +3978,11 @@ static int partition_format_verity_hash(
                 node = partition_target_path(t);
         }
 
+        if (p->verity_data_block_size == UINT64_MAX)
+                p->verity_data_block_size = context->fs_sector_size;
+        if (p->verity_hash_block_size == UINT64_MAX)
+                p->verity_hash_block_size = context->fs_sector_size;
+
         r = sym_crypt_init(&cd, node);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
@@ -3951,8 +3996,8 @@ static int partition_format_verity_hash(
                                 .flags = CRYPT_VERITY_CREATE_HASH,
                                 .hash_name = "sha256",
                                 .hash_type = 1,
-                                .data_block_size = context->fs_sector_size,
-                                .hash_block_size = context->fs_sector_size,
+                                .data_block_size = p->verity_data_block_size,
+                                .hash_block_size = p->verity_hash_block_size,
                                 .salt_size = sizeof(p->verity_salt),
                                 .salt = (const char*)p->verity_salt,
                         });
index d6e6ac0b226d77d7a9d3621e2f80b4c6eb898a35..06b1313477a134020cf4514d4bdb301afec872f9 100755 (executable)
@@ -16,6 +16,7 @@ test_append_files() {
         instmods dm_verity =md
         instmods erofs
         generate_module_dependencies
+        image_install veritysetup
         image_install -o mksquashfs
         image_install -o mkfs.erofs
     fi
index 4c395b365424d2d44ddc0df1750ee0103ac80571..7e21cdd603059e76bc9da2fcc87aefb58260ad11 100755 (executable)
@@ -99,7 +99,7 @@ testcase_basic() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** 1. create an empty image ***"
 
@@ -395,7 +395,7 @@ testcase_dropin() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     tee "$defs/root.conf" <<EOF
 [Partition]
@@ -455,7 +455,7 @@ testcase_multiple_definitions() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     mkdir -p "$defs/1"
     tee "$defs/1/root1.conf" <<EOF
@@ -526,7 +526,7 @@ testcase_copy_blocks() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** First, create a disk image and verify its in order ***"
 
@@ -610,7 +610,7 @@ testcase_unaligned_partition() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** Operate on an image with unaligned partition ***"
 
@@ -647,7 +647,7 @@ testcase_issue_21817() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** testcase for #21817 ***"
 
@@ -685,7 +685,7 @@ testcase_issue_24553() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** testcase for #24553 ***"
 
@@ -790,7 +790,7 @@ testcase_zero_uuid() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** Test image with zero UUID ***"
 
@@ -820,7 +820,7 @@ testcase_verity() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** dm-verity ***"
 
@@ -907,6 +907,64 @@ EOF
     systemd-dissect -U "$imgs/mnt"
 }
 
+testcase_verity_explicit_block_size() {
+    local defs imgs loop
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping verity block size tests in container."
+        return
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** varying-dm-verity-block-sizes ***"
+
+    tee "$defs/verity-data.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=${defs}
+Verity=data
+VerityMatchKey=root
+Minimize=guess
+EOF
+
+    tee "$defs/verity-hash.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity
+Verity=hash
+VerityMatchKey=root
+VerityHashBlockSizeBytes=1024
+VerityDataBlockSizeBytes=4096
+Minimize=yes
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   --empty=create \
+                   --size=auto \
+                   --json=pretty \
+                   "$imgs/verity"
+
+    loop="$(losetup --partscan --show --find "$imgs/verity")"
+
+    # Make sure the loopback device gets cleaned up
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
+
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    # Check that the verity block sizes are as expected
+    veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
+    veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024'
+}
+
 testcase_exclude_files() {
     local defs imgs root output
 
@@ -915,7 +973,7 @@ testcase_exclude_files() {
     root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs' '$root'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     echo "*** file exclusion ***"
 
@@ -1070,7 +1128,7 @@ testcase_free_area_calculation() {
     imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
     # shellcheck disable=SC2064
     trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod a+rx "$defs"
+    chmod 0755 "$defs"
 
     # https://github.com/systemd/systemd/issues/28225
     echo "*** free area calculation ***"