]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
veritysetup: add support for superblock and underlying options
authorGaël PORTAY <gael.portay@collabora.com>
Wed, 23 Dec 2020 15:10:15 +0000 (10:10 -0500)
committerGaël PORTAY <gael.portay@rtone.fr>
Thu, 13 Apr 2023 03:15:20 +0000 (05:15 +0200)
The verity parameter no_superblock allows to format/open an hash device
without the superblock. However, the superblock data must be set to open
the data-device.

This adds the option superblocks (sixth argument) and all the underlying
options which are implied to set the superblock manually if hash device
has no superblock:

 - superblock=BOOL
 - format=NUMBER (hash version type, 0 for original ChromeOS, 1 for
   modern)
 - data-block-size=BYTES (max page-size, multiple of 512)
 - hash-block-size=BYTES (max page-size, multiple of 512)
 - data-blocks=BLOCKS (size of data-device in blocks)
 - salt=HEXSTR (salt used at format, max 256 bytes)
 - uuid=UUID
 - hash=STR (algorithm name for dm-verity used at format, default is
   sha256)

See `veritysetup(8)` for more details.

man/systemd-veritysetup-generator.xml
man/veritytab.xml
src/veritysetup/veritysetup.c

index 6098895f55f1e67f979dea6ac82a176f84ec7277..c591fcb24f37f6e48c9efa1336b8a2f42ea80c1d 100644 (file)
         <term><varname>systemd.verity_root_options=</varname></term>
 
         <listitem><para>Takes a comma-separated list of dm-verity options. Expects the following options
-        <option>hash-offset=<replaceable>BYTES</replaceable></option>, <option>ignore-corruption</option>,
-        <option>restart-on-corruption</option>, <option>ignore-zero-blocks</option>,
-        <option>check-at-most-once</option>, <option>panic-on-corruption</option> and
+        <option>superblock=<replaceable>BOOLEAN</replaceable></option>,
+        <option>format=<replaceable>NUMBER</replaceable></option>,
+        <option>data-block-size=<replaceable>BYTES</replaceable></option>,
+        <option>hash-block-size=<replaceable>BYTES</replaceable></option>,
+        <option>data-blocks=<replaceable>BLOCKS</replaceable></option>,
+        <option>hash-offset=<replaceable>BYTES</replaceable></option>,
+        <option>salt=<replaceable>HEX</replaceable></option>, <option>uuid=<replaceable>UUID</replaceable></option>,
+        <option>ignore-corruption</option>, <option>restart-on-corruption</option>, <option>ignore-zero-blocks</option>,
+        <option>check-at-most-once</option>, <option>panic-on-corruption</option>,
+        <option>hash=<replaceable>HASH</replaceable></option> and
         <option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>HEX</replaceable></option>. See
         <citerefentry project='die-net'><refentrytitle>veritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
         details.</para></listitem>
index ec5d0f45a1a279519a2200d7cd2d87127b59df84..b4f2be3e30601a852efe45478c759c879c1b1e4a 100644 (file)
@@ -60,6 +60,40 @@ This is based on crypttab(5).
 
     <variablelist class='fstab-options'>
 
+      <varlistentry>
+        <term><option>superblock=<replaceable>BOOL</replaceable></option></term>
+
+        <listitem><para>Use dm-verity with or without permanent on-disk superblock.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>format=<replaceable>NUMBER</replaceable></option></term>
+
+        <listitem><para>Specifies the hash version type. Format type 0 is original Chrome OS version. Format type 1 is
+        modern version.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>data-block-size=<replaceable>BYTES</replaceable></option></term>
+
+        <listitem><para>Used block size for the data device. (Note kernel supports only page-size as maximum
+        here; Multiples of 512 bytes.) </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>hash-block-size=<replaceable>BYTES</replaceable></option></term>
+
+        <listitem><para>Used block size for the hash device. (Note kernel supports only page-size as maximum
+        here; Multiples of 512 bytes.)</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>data-blocks=<replaceable>BLOCKS</replaceable></option></term>
+
+        <listitem><para>Number of blocks of data device used in verification. If not specified, the whole device is
+        used.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>hash-offset=<replaceable>BYTES</replaceable></option></term>
 
@@ -67,6 +101,21 @@ This is based on crypttab(5).
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>salt=<replaceable>HEX</replaceable></option></term>
+
+        <listitem><para>Salt used for format or verification. Format is a hexadecimal string; 256 bytes long maximum;
+        <literal>-</literal>is the special value for empty.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>uuid=<replaceable>UUID</replaceable></option></term>
+
+        <listitem><para>Use the provided UUID for format command instead of generating new one. The UUID must be
+        provided in standard UUID format, e.g. 12345678-1234-1234-1234-123456789abc.</para></listitem>
+        <listitem><para></para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>ignore-corruption</option></term>
         <term><option>restart-on-corruption</option></term>
@@ -101,6 +150,13 @@ This is based on crypttab(5).
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>hash=<replaceable>HASH</replaceable></option></term>
+
+        <listitem><para>Hash algorithm for dm-verity. This should be the name of the algorithm, like "sha1". For default
+        see <command>veritysetup --help</command>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>HEX</replaceable></option></term>
 
index e1b0e00e421cd78edb7082985e4f497d81f605ce..120d8b619ace5b10cc9d7e5684b9b5615382be7c 100644 (file)
 #include "string-util.h"
 #include "terminal-util.h"
 
+static char *arg_hash = NULL;
+static bool arg_superblock = true;
+static int arg_format = 1;
+static uint64_t arg_data_block_size = 4096;
+static uint64_t arg_hash_block_size = 4096;
+static uint64_t arg_data_blocks = 0;
 static uint64_t arg_hash_offset = 0;
+static void *arg_salt = NULL;
+static uint64_t arg_salt_size = 32;
+static char *arg_uuid = NULL;
 static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY;
 static char *arg_root_hash_signature = NULL;
 
+STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_uuid, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
 
 static int help(void) {
@@ -64,6 +76,25 @@ static int save_roothashsig_option(const char *option, bool strict) {
                                "base64 string encoding signature prefixed by base64:.");
 }
 
+static int parse_block_size(const char *t, uint64_t *size) {
+        uint64_t u;
+        int r;
+
+        r = parse_size(t, 1024, &u);
+        if (r < 0)
+                return r;
+
+        if (u < 512 || u > (512 * 1024))
+                return -ERANGE;
+
+        if ((u % 512) != 0 || !ISPOWEROF2(u))
+                return -EINVAL;
+
+        *size = u;
+
+        return 0;
+}
+
 static int parse_options(const char *options) {
         int r;
 
@@ -106,7 +137,45 @@ static int parse_options(const char *options) {
                 else if (streq(word, "panic-on-corruption"))
                         arg_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION;
 #endif
-                else if ((val = startswith(word, "hash-offset="))) {
+                else if ((val = startswith(word, "superblock="))) {
+
+                        r = parse_boolean(val);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse boolean '%s': %m", word);
+
+                        arg_superblock = r;
+                } else if ((val = startswith(word, "format="))) {
+
+                        if (!STR_IN_SET(val, "0", "1"))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "format= expects either 0 (original Chrome OS version) or "
+                                                                                "1 (modern version).");
+
+                        arg_format = val[0] - '0';
+                } else if ((val = startswith(word, "data-block-size="))) {
+                        uint64_t sz;
+
+                        r = parse_block_size(val, &sz);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse size '%s': %m", word);
+
+                        arg_data_block_size = sz;
+                } else if ((val = startswith(word, "hash-block-size="))) {
+                        uint64_t sz;
+
+                        r = parse_block_size(val, &sz);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse size '%s': %m", word);
+
+                        arg_hash_block_size = sz;
+                } else if ((val = startswith(word, "data-blocks="))) {
+                        uint64_t u;
+
+                        r = safe_atou64(val, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse number '%s': %m", word);
+
+                        arg_data_blocks = u;
+                } else if ((val = startswith(word, "hash-offset="))) {
                         uint64_t off;
 
                         r = parse_size(val, 1024, &off);
@@ -116,6 +185,42 @@ static int parse_options(const char *options) {
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "hash-offset= expects a 512-byte aligned value.");
 
                         arg_hash_offset = off;
+                } else if ((val = startswith(word, "salt="))) {
+
+                        if (!string_is_safe(val))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "salt= is not valid.");
+
+                        if (isempty(val)) {
+                                arg_salt = mfree(arg_salt);
+                                arg_salt_size = 32;
+                        } else if (streq(val, "-")) {
+                                arg_salt = mfree(arg_salt);
+                                arg_salt_size = 0;
+                        } else {
+                                size_t l;
+                                void *m;
+
+                                r = unhexmem(val, strlen(val), &m, &l);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse salt '%s': %m", word);
+
+                                free_and_replace(arg_salt, m);
+                                arg_salt_size = l;
+                        }
+                } else if ((val = startswith(word, "uuid="))) {
+
+                        r = sd_id128_from_string(val, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse UUID '%s': %m", word);
+
+                        r = free_and_strdup(&arg_uuid, val);
+                        if (r < 0)
+                                return log_oom();
+                } else if ((val = startswith(word, "hash="))) {
+
+                        r = free_and_strdup(&arg_hash, val);
+                        if (r < 0)
+                                return log_oom();
                 } else if ((val = startswith(word, "root-hash-signature="))) {
                         r = save_roothashsig_option(val, /* strict= */ true);
                         if (r < 0)
@@ -186,13 +291,34 @@ static int run(int argc, char *argv[]) {
                         r = parse_options(options);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse options: %m");
-
-                        p.hash_area_offset = arg_hash_offset;
                 }
 
-                r = crypt_load(cd, CRYPT_VERITY, &p);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to load verity superblock: %m");
+                if (arg_superblock) {
+                        p = (struct crypt_params_verity) {
+                                .hash_area_offset = arg_hash_offset,
+                        };
+
+                        r = crypt_load(cd, CRYPT_VERITY, &p);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to load verity superblock: %m");
+                } else {
+                        p = (struct crypt_params_verity) {
+                                .hash_name = arg_hash,
+                                .data_device = data_device,
+                                .salt = arg_salt,
+                                .salt_size = arg_salt_size,
+                                .hash_type = arg_format,
+                                .data_block_size = arg_data_block_size,
+                                .hash_block_size = arg_hash_block_size,
+                                .data_size = arg_data_blocks,
+                                .hash_area_offset = arg_hash_offset,
+                                .flags = CRYPT_VERITY_NO_HEADER,
+                        };
+
+                        r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, arg_uuid, NULL, 0, &p);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to format verity superblock: %m");
+                }
 
                 r = crypt_set_data_device(cd, data_device);
                 if (r < 0)