veritysetup: optionally pick up roothash/roothashsig from udev device db entry

This commit is contained in:
Lennart Poettering
2025-03-18 12:51:04 +01:00
parent 0a64b7ba6e
commit fb008a7333
2 changed files with 102 additions and 53 deletions

View 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>
<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). This feature requires Linux kernel version 5.4 or more recent.</para>
<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>

View 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 r;
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", option);
return true;
}
} 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 log_error_errno(r, "Failed to read root hash signature: %m");
if (!strict)
} 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 log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"root-hash-signature= expects either full path to signature file or "
"base64 string encoding signature prefixed by 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().");
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);