]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
veritysetup: optionally pick up roothash/roothashsig from udev device db entry
authorLennart Poettering <lennart@poettering.net>
Tue, 18 Mar 2025 11:51:04 +0000 (12:51 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 3 Apr 2025 09:08:57 +0000 (11:08 +0200)
man/veritytab.xml
src/veritysetup/veritysetup.c

index 74b757a961eeb349ed40d03f902c1c33140806b8..9b131e350c46c6f147613f8ac8973c3d0af53681 100644 (file)
@@ -53,7 +53,9 @@ This is based on crypttab(5).
     <para>The third field contains a path to the underlying block hash device, or a specification of a block device via
     <varname>UUID=</varname> followed by the <replaceable>UUID</replaceable>.</para>
 
-    <para>The fourth field is the <replaceable>roothash</replaceable> in hexadecimal.</para>
+    <para>The fourth field is the <replaceable>roothash</replaceable> in hexadecimal. If this field is
+    specified as dash, it is attempted to read the root hash from the udev property
+    <literal>ID_DISSECT_PART_ROOTHASH=</literal> (encoded in hexadecimal) of the data device.</para>
 
     <para>The fifth field, if present, is a comma-delimited list of options. The following options are
     recognized:</para>
@@ -213,11 +215,14 @@ This is based on crypttab(5).
       </varlistentry>
 
       <varlistentry>
-        <term><option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>BASE64</replaceable></option></term>
-
-        <listitem><para>A base64 string encoding the root hash signature prefixed by
-        <literal>base64:</literal> or an absolute path to a root hash signature file used to verify the root
-        hash (in kernel). This feature requires Linux kernel version 5.4 or more recent.</para>
+        <term><option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>BASE64</replaceable>|auto</option></term>
+
+        <listitem><para>A Base64 string encoding the root hash signature prefixed by
+        <literal>base64:</literal>, or an absolute path to a root hash signature file used to verify the root
+        hash (in kernel). If the special string <literal>auto</literal> is specified, the root hash signature
+        is attempted to be read from the udev property <literal>ID_DISSECT_PART_ROOTHASH_SIG=</literal> (in
+        Base64 format) of the data device. This feature requires Linux kernel version 5.4 or more
+        recent.</para>
 
         <xi:include href="version-info.xml" xpointer="v248"/></listitem>
       </varlistentry>
index 97f233c906e22bc1febaefda1787bb546c4dd4e4..6fd5527424a3839980a090880b21b7fb51f3e392 100644 (file)
@@ -4,6 +4,8 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
 #include "cryptsetup-util.h"
 #include "fileio.h"
@@ -19,7 +21,7 @@
 #include "terminal-util.h"
 #include "verbs.h"
 
-static char *arg_hash = NULL;
+static char *arg_hash = NULL; /* the hash algorithm */
 static bool arg_superblock = true;
 static int arg_format = 1;
 static uint64_t arg_data_block_size = 4096;
@@ -33,7 +35,9 @@ static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY;
 static char *arg_fec_what = NULL;
 static uint64_t arg_fec_offset = 0;
 static uint64_t arg_fec_roots = 2;
-static char *arg_root_hash_signature = NULL;
+static void *arg_root_hash_signature = NULL;
+static size_t arg_root_hash_signature_size = 0;
+static bool arg_root_hash_signature_auto = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
@@ -60,26 +64,52 @@ static int help(void) {
         return 0;
 }
 
-static int save_roothashsig_option(const char *option, bool strict) {
+static int parse_roothashsig_option(const char *option, bool strict) {
+        _cleanup_free_ void *rhs = NULL;
+        size_t rhss = 0;
+        bool set_auto = false;
         int r;
 
-        if (path_is_absolute(option) || startswith(option, "base64:")) {
-                if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
-                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                               "Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
+        assert(option);
 
-                r = free_and_strdup_warn(&arg_root_hash_signature, option);
+        const char *value = startswith(option, "base64:");
+        if (value) {
+                r = unbase64mem(value, &rhs, &rhss);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse root hash signature '%s': %m", option);
+
+        } else if (path_is_absolute(option)) {
+                r = read_full_file_full(
+                                AT_FDCWD,
+                                option,
+                                /* offset= */ UINT64_MAX,
+                                /* size= */ SIZE_MAX,
+                                READ_FULL_FILE_CONNECT_SOCKET,
+                                /* bind_name= */ NULL,
+                                (char**) &rhs,
+                                &rhss);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to read root hash signature: %m");
+
+        } else if (streq(option, "auto"))
+                /* auto → Derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
+                set_auto = true;
+        else if (strict)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "root-hash-signature= expects either full path to signature file or "
+                                       "base64 string encoding signature prefixed by base64:.");
+        else
+                return false;
 
-                return true;
-        }
+        if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
 
-        if (!strict)
-                return false;
-        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                               "root-hash-signature= expects either full path to signature file or "
-                               "base64 string encoding signature prefixed by base64:.");
+        free_and_replace(arg_root_hash_signature, rhs);
+        arg_root_hash_signature_size = rhss;
+        arg_root_hash_signature_auto = set_auto;
+
+        return true;
 }
 
 static int parse_block_size(const char *t, uint64_t *size) {
@@ -105,7 +135,7 @@ static int parse_options(const char *options) {
         int r;
 
         /* backward compatibility with the obsolete ROOTHASHSIG positional argument */
-        r = save_roothashsig_option(options, /* strict= */ false);
+        r = parse_roothashsig_option(options, /* strict= */ false);
         if (r < 0)
                 return r;
         if (r > 0) {
@@ -264,7 +294,7 @@ static int parse_options(const char *options) {
 
                         arg_fec_roots = u;
                 } else if ((val = startswith(word, "root-hash-signature="))) {
-                        r = save_roothashsig_option(val, /* strict= */ true);
+                        r = parse_roothashsig_option(val, /* strict= */ true);
                         if (r < 0)
                                 return r;
 
@@ -277,10 +307,10 @@ static int parse_options(const char *options) {
 
 static int verb_attach(int argc, char *argv[], void *userdata) {
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
-        _cleanup_free_ void *m = NULL;
+        _cleanup_free_ void *rh = NULL;
         struct crypt_params_verity p = {};
         crypt_status_info status;
-        size_t l;
+        size_t rh_size = 0;
         int r;
 
         assert(argc >= 5);
@@ -294,10 +324,48 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
         if (!filename_is_valid(volume))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
 
-        r = unhexmem(root_hash, &m, &l);
+        if (options) {
+                r = parse_options(options);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse options: %m");
+        }
+
+        if (empty_or_dash(root_hash) || streq_ptr(root_hash, "auto"))
+                root_hash = NULL;
+
+        _cleanup_(sd_device_unrefp) sd_device *datadev = NULL;
+        if (!root_hash || arg_root_hash_signature_auto) {
+                r = sd_device_new_from_path(&datadev, data_device);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire udev object for data device '%s': %m", data_device);
+        }
+
+        if (!root_hash) {
+                /* If no literal root hash is specified try to determine it automatically from the
+                 * ID_DISSECT_PART_ROOTHASH udev property. */
+                r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH", &root_hash);
+                if (r < 0)
+                        return log_error_errno(r, "No root hash specified, and device doesn't carry ID_DISSECT_PART_ROOTHASH property, cannot determine root hash.");
+        }
+
+        r = unhexmem(root_hash, &rh, &rh_size);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse root hash: %m");
 
+        if (arg_root_hash_signature_auto) {
+                assert(!arg_root_hash_signature);
+                assert(arg_root_hash_signature_size == 0);
+
+                const char *t;
+                r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH_SIG", &t);
+                if (r < 0)
+                        return log_error_errno(r, "Automatic root hash signature pick up requested, and device doesn't carry ID_DISSECT_PART_ROOTHASH_SIG property, cannot determine root hash signature.");
+
+                r = unbase64mem(t, &arg_root_hash_signature, &arg_root_hash_signature_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode root hash signature data from udev data device: %m");
+        }
+
         r = crypt_init(&cd, verity_device);
         if (r < 0)
                 return log_error_errno(r, "Failed to open verity device %s: %m", verity_device);
@@ -310,12 +378,6 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
                 return 0;
         }
 
-        if (options) {
-                r = parse_options(options);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse options: %m");
-        }
-
         if (arg_superblock) {
                 p = (struct crypt_params_verity) {
                         .fec_device = arg_fec_what,
@@ -353,32 +415,14 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to configure data device: %m");
 
-        if (arg_root_hash_signature) {
+        if (arg_root_hash_signature_size > 0)
 #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
-                _cleanup_free_ char *hash_sig = NULL;
-                size_t hash_sig_size;
-                char *value;
-
-                if ((value = startswith(arg_root_hash_signature, "base64:"))) {
-                        r = unbase64mem(value, (void*) &hash_sig, &hash_sig_size);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to parse root hash signature '%s': %m", arg_root_hash_signature);
-                } else {
-                        r = read_full_file_full(
-                                        AT_FDCWD, arg_root_hash_signature, UINT64_MAX, SIZE_MAX,
-                                        READ_FULL_FILE_CONNECT_SOCKET,
-                                        NULL,
-                                        &hash_sig, &hash_sig_size);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to read root hash signature: %m");
-                }
-
-                r = crypt_activate_by_signed_key(cd, volume, m, l, hash_sig, hash_sig_size, arg_activate_flags);
+                r = crypt_activate_by_signed_key(cd, volume, rh, rh_size, arg_root_hash_signature, arg_root_hash_signature_size, arg_activate_flags);
 #else
                 assert_not_reached();
 #endif
-        else
-                r = crypt_activate_by_volume_key(cd, volume, m, l, arg_activate_flags);
+        else
+                r = crypt_activate_by_volume_key(cd, volume, rh, rh_size, arg_activate_flags);
         if (r < 0)
                 return log_error_errno(r, "Failed to set up verity device '%s': %m", volume);