Merge pull request #31531 from poettering/verity-userspace-optional

dissect: make use of userspace verity keyring optional
This commit is contained in:
Lennart Poettering 2024-02-28 22:04:58 +01:00 committed by GitHub
commit 5b97957376
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 142 additions and 56 deletions

View file

@ -488,6 +488,12 @@ disk images with `--image=` or similar:
devices when opening them. Defaults to on, set this to "0" to disable this
feature.
* `$SYSTEMD_ALLOW_USERSPACE_VERITY` — takes a boolean, which controls whether
to consider the userspace Verity public key store in `/etc/verity.d/` (and
related directories) to authenticate signatures on Verity hashes of disk
images. Defaults to true, i.e. userspace signature validation is allowed. If
false, authentication can be done only via the kernel's internal keyring.
`systemd-cryptsetup`:
* `$SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE` takes a boolean, which controls

View file

@ -676,6 +676,17 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.allow_userspace_verity=</varname></term>
<listitem><para>Takes a boolean argument. Controls whether disk images that are Verity protected may
be authenticated in userspace signature checks via <filename>/etc/verity.d/</filename> (and related
directories) public key drop-ins, or whether in-kernel signature checking only. Defaults to
on.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.hostname=</varname></term>

View file

@ -681,7 +681,8 @@ static int run(int argc, char *argv[]) {
arg_image_policy,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_READ_ONLY,
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -537,7 +537,8 @@ static int run(int argc, char *argv[]) {
arg_image,
arg_image_policy,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK,
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -2125,7 +2125,8 @@ int setup_namespace(const NamespaceParameters *p, char **error_path) {
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES;
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
int r;
assert(p);

View file

@ -1394,7 +1394,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_VALIDATE_OS,
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -79,7 +79,8 @@ static DissectImageFlags arg_flags =
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ADD_PARTITION_DEVICES;
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;

View file

@ -1694,7 +1694,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
&rfd,
&loop_device);

View file

@ -2326,7 +2326,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
(arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY),
(arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY) |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -1699,7 +1699,8 @@ static int run(int argc, char* argv[]) {
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_VALIDATE_OS,
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -156,7 +156,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -3712,6 +3712,14 @@ static int setup_unix_export_host_inside(const char *directory, const char *unix
return 0;
}
static DissectImageFlags determine_dissect_image_flags(void) {
return
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_DISCARD_ON_LOOP |
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS) |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
}
static int outer_child(
Barrier *barrier,
const char *directory,
@ -3773,10 +3781,8 @@ static int outer_child(
arg_uid_shift,
arg_uid_range,
/* userns_fd= */ -EBADF,
determine_dissect_image_flags()|
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
DISSECT_IMAGE_DISCARD_ON_LOOP|
DISSECT_IMAGE_USR_NO_ROOT|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS)|
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
if (r < 0)
return r;
@ -3958,10 +3964,8 @@ static int outer_child(
arg_uid_shift,
arg_uid_range,
/* userns_fd= */ -EBADF,
determine_dissect_image_flags()|
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
DISSECT_IMAGE_DISCARD_ON_LOOP|
DISSECT_IMAGE_USR_NO_ROOT|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS)|
(idmap ? DISSECT_IMAGE_MOUNT_IDMAPPED : 0));
if (r == -EUCLEAN)
return log_error_errno(r, "File system check for image failed: %m");

View file

@ -7952,7 +7952,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_GPT_ONLY |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT,
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -362,7 +362,13 @@ static int portable_extract_by_path(
assert(path);
r = loop_device_make_by_path(path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
r = loop_device_make_by_path(
path,
O_RDONLY,
/* sector_size= */ UINT32_MAX,
LO_FLAGS_PARTSCAN,
LOCK_SH,
&d);
if (r == -EISDIR) {
_cleanup_free_ char *image_name = NULL;
@ -384,6 +390,21 @@ static int portable_extract_by_path(
_cleanup_(rmdir_and_freep) char *tmpdir = NULL;
_cleanup_close_pair_ int seq[2] = EBADF_PAIR;
_cleanup_(sigkill_waitp) pid_t child = 0;
DissectImageFlags flags =
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
if (path_is_extension)
flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0);
else
flags |= DISSECT_IMAGE_VALIDATE_OS;
/* We now have a loopback block device, let's fork off a child in its own mount namespace, mount it
* there, and extract the metadata we need. The metadata is sent from the child back to us. */
@ -399,14 +420,7 @@ static int portable_extract_by_path(
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES,
flags,
&m);
if (r == -ENOPKG)
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
@ -428,15 +442,8 @@ static int portable_extract_by_path(
if (r < 0)
return r;
if (r == 0) {
DissectImageFlags flags = DISSECT_IMAGE_READ_ONLY;
seq[0] = safe_close(seq[0]);
if (path_is_extension)
flags |= DISSECT_IMAGE_VALIDATE_OS_EXT | (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0);
else
flags |= DISSECT_IMAGE_VALIDATE_OS;
r = dissected_image_mount(
m,
tmpdir,

View file

@ -1471,8 +1471,25 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
case IMAGE_BLOCK: {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
DissectImageFlags flags =
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_VALIDATE_OS_EXT |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
r = loop_device_make_by_path(i->path, O_RDONLY, /* sector_size= */ UINT32_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &d);
r = loop_device_make_by_path(
i->path,
O_RDONLY,
/* sector_size= */ UINT32_MAX,
LO_FLAGS_PARTSCAN,
LOCK_SH,
&d);
if (r < 0)
return r;
@ -1481,20 +1498,12 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_READ_ONLY |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES,
flags,
&m);
if (r < 0)
return r;
r = dissected_image_acquire_metadata(m,
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_VALIDATE_OS_EXT);
r = dissected_image_acquire_metadata(m, flags);
if (r < 0)
return r;

View file

@ -60,6 +60,7 @@
#include "openssl-util.h"
#include "os-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "raw-clone.h"
#include "resize-fs.h"
@ -2537,7 +2538,35 @@ static char* dm_deferred_remove_clean(char *name) {
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
static int validate_signature_userspace(const VeritySettings *verity) {
static int validate_signature_userspace(const VeritySettings *verity, DissectImageFlags flags) {
int r;
if (!FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_USERSPACE_VERITY)) {
log_debug("Userspace dm-verity signature authentication disabled via flag.");
return 0;
}
r = getenv_bool_secure("SYSTEMD_ALLOW_USERSPACE_VERITY");
if (r < 0 && r != -ENXIO) {
log_debug_errno(r, "Failed to parse $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable, refusing userspace dm-verity signature authentication.");
return 0;
}
if (!r) {
log_debug("Userspace dm-verity signature authentication disabled via $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable.");
return 0;
}
bool b;
r = proc_cmdline_get_bool("systemd.allow_userspace_verity", PROC_CMDLINE_TRUE_WHEN_MISSING, &b);
if (r < 0) {
log_debug_errno(r, "Failed to parse systemd.allow_userspace_verity= kernel command line option, refusing userspace dm-verity signature authentication.");
return 0;
}
if (!b) {
log_debug("Userspace dm-verity signature authentication disabled via systemd.allow_userspace_verity= kernel command line variable.");
return 0;
}
#if HAVE_OPENSSL
_cleanup_(sk_X509_free_allp) STACK_OF(X509) *sk = NULL;
_cleanup_strv_free_ char **certs = NULL;
@ -2546,7 +2575,6 @@ static int validate_signature_userspace(const VeritySettings *verity) {
_cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order
* of declaration in place, please */
const unsigned char *d;
int r;
assert(verity);
assert(verity->root_hash);
@ -2618,7 +2646,8 @@ static int validate_signature_userspace(const VeritySettings *verity) {
static int do_crypt_activate_verity(
struct crypt_device *cd,
const char *name,
const VeritySettings *verity) {
const VeritySettings *verity,
DissectImageFlags flags) {
bool check_signature;
int r, k;
@ -2663,7 +2692,7 @@ static int do_crypt_activate_verity(
/* Preferably propagate the original kernel error, so that the fallback logic can work,
* as the device-mapper is finicky around concurrent activations of the same volume */
k = validate_signature_userspace(verity);
k = validate_signature_userspace(verity, flags);
if (k < 0)
return r < 0 ? r : k;
if (k == 0)
@ -2784,7 +2813,7 @@ static int verity_partition(
goto check; /* The device already exists. Let's check it. */
/* The symlink to the device node does not exist yet. Assume not activated, and let's activate it. */
r = do_crypt_activate_verity(cd, name, verity);
r = do_crypt_activate_verity(cd, name, verity, flags);
if (r >= 0)
goto try_open; /* The device is activated. Let's open it. */
/* libdevmapper can return EINVAL when the device is already in the activation stage.
@ -3966,10 +3995,12 @@ int verity_dissect_and_mount(
if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags = (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
dissect_image_flags =
(verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
(relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0) |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES;
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
/* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
* accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */

View file

@ -87,6 +87,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_DISKSEQ_DEVNODE = 1 << 23, /* Prefer /dev/disk/by-diskseq/… device nodes */
DISSECT_IMAGE_ALLOW_EMPTY = 1 << 24, /* Allow that no usable partitions is present */
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE = 1 << 25, /* Try to mount the image beneath the specified mountpoint, rather than on top of it, and then umount the top */
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY = 1 << 26, /* Allow userspace verity keyring in /etc/verity.d/ and related dirs */
} DissectImageFlags;
struct DissectedImage {

View file

@ -1214,7 +1214,9 @@ static int mount_in_namespace(
(void) mkdir_parents(dest, 0755);
if (img) {
DissectImageFlags f = DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE;
DissectImageFlags f =
DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
if (make_file_or_directory)
f |= DISSECT_IMAGE_MKDIR;

View file

@ -1382,7 +1382,8 @@ static int merge_subprocess(
DISSECT_IMAGE_MOUNT_ROOT_ONLY |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES;
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
r = verity_settings_load(&verity_settings, img->path, NULL, NULL);
if (r < 0)

View file

@ -1270,7 +1270,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_VALIDATE_OS,
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -882,7 +882,8 @@ static int process_image(
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT,
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -2247,7 +2247,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);

View file

@ -4627,7 +4627,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
DISSECT_IMAGE_GROWFS |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);