1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

dissect: wrap verity settings in new VeritySettings structure

Just some refactoring: let's place the various verity related parameters
in a common structure, and pass that around instead of the individual
parameters.

Also, let's load the PKCS#7 signature data when finding metadata
right-away, instead of delaying this until we need it. In all cases we
call this there's not much time difference between the metdata finding
and the loading, hence this simplifies things and makes sure root hash
data and its signature is now always acquired together.
This commit is contained in:
Lennart Poettering 2020-09-15 22:09:08 +02:00 committed by Yu Watanabe
parent dc0e90d2e0
commit 89e62e0bd3
8 changed files with 399 additions and 333 deletions

View File

@ -942,32 +942,51 @@ static int mount_images(const MountEntry *m) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash_decoded = NULL; _cleanup_(verity_settings_done) VeritySettings verity = {};
_cleanup_free_ char *verity_data = NULL, *hash_sig = NULL; DissectImageFlags dissect_image_flags;
DissectImageFlags dissect_image_flags = m->read_only ? DISSECT_IMAGE_READ_ONLY : 0;
size_t root_hash_size = 0;
int r; int r;
r = verity_metadata_load(mount_entry_source(m), NULL, &root_hash_decoded, &root_hash_size, &verity_data, &hash_sig); assert(m);
r = verity_settings_load(&verity, mount_entry_source(m), NULL, NULL);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m"); return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags |= verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
r = loop_device_make_by_path(mount_entry_source(m), dissect_image_flags =
m->read_only ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */, (m->read_only ? DISSECT_IMAGE_READ_ONLY : 0) |
verity_data ? 0 : LO_FLAGS_PARTSCAN, (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0);
&loop_device);
r = loop_device_make_by_path(
mount_entry_source(m),
m->read_only ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
&loop_device);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to create loop device for image: %m"); return log_debug_errno(r, "Failed to create loop device for image: %m");
r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags, &dissected_image); r = dissect_image(
loop_device->fd,
&verity,
m->image_options,
dissect_image_flags,
&dissected_image);
/* No partition table? Might be a single-filesystem image, try again */ /* No partition table? Might be a single-filesystem image, try again */
if (!verity_data && r < 0 && r == -ENOPKG) if (!verity.data_path && r == -ENOPKG)
r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE, &dissected_image); r = dissect_image(
loop_device->fd,
&verity,
m->image_options,
dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE,
&dissected_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m"); return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, NULL, root_hash_decoded, root_hash_size, verity_data, hash_sig, NULL, 0, dissect_image_flags, &decrypted_image); r = dissected_image_decrypt(
dissected_image,
NULL,
&verity,
dissect_image_flags,
&decrypted_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m"); return log_debug_errno(r, "Failed to decrypt dissected image: %m");
@ -1374,6 +1393,60 @@ static bool home_read_only(
return false; return false;
} }
static int verity_settings_prepare(
VeritySettings *verity,
const char *root_image,
const void *root_hash,
size_t root_hash_size,
const char *root_hash_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
const char *root_hash_sig_path,
const char *verity_data_path) {
int r;
assert(verity);
if (root_hash) {
void *d;
d = memdup(root_hash, root_hash_size);
if (!d)
return -ENOMEM;
free_and_replace(verity->root_hash, d);
verity->root_hash_size = root_hash_size;
}
if (root_hash_sig) {
void *d;
d = memdup(root_hash_sig, root_hash_sig_size);
if (!d)
return -ENOMEM;
free_and_replace(verity->root_hash_sig, d);
verity->root_hash_sig_size = root_hash_sig_size;
}
if (verity_data_path) {
r = free_and_strdup(&verity->data_path, verity_data_path);
if (r < 0)
return r;
}
r = verity_settings_load(
verity,
root_image,
root_hash_path,
root_hash_sig_path);
if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m");
return 0;
}
int setup_namespace( int setup_namespace(
const char* root_directory, const char* root_directory,
const char* root_image, const char* root_image,
@ -1400,20 +1473,19 @@ int setup_namespace(
const void *root_hash_sig, const void *root_hash_sig,
size_t root_hash_sig_size, size_t root_hash_sig_size,
const char *root_hash_sig_path, const char *root_hash_sig_path,
const char *root_verity, const char *verity_data_path,
DissectImageFlags dissect_image_flags, DissectImageFlags dissect_image_flags,
char **error_path) { char **error_path) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash_decoded = NULL; _cleanup_(verity_settings_done) VeritySettings verity = {};
_cleanup_free_ char *verity_data = NULL, *hash_sig_path = NULL;
MountEntry *m = NULL, *mounts = NULL; MountEntry *m = NULL, *mounts = NULL;
size_t n_mounts;
bool require_prefix = false; bool require_prefix = false;
const char *root; const char *root;
int r = 0; size_t n_mounts;
int r;
assert(ns_info); assert(ns_info);
@ -1432,43 +1504,40 @@ int setup_namespace(
strv_isempty(read_write_paths)) strv_isempty(read_write_paths))
dissect_image_flags |= DISSECT_IMAGE_READ_ONLY; dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
r = loop_device_make_by_path(root_image, r = verity_settings_prepare(
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */, &verity,
LO_FLAGS_PARTSCAN, root_image,
&loop_device); root_hash, root_hash_size, root_hash_path,
root_hash_sig, root_hash_sig_size, root_hash_sig_path,
verity_data_path);
if (r < 0)
return r;
SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, verity.data_path);
r = loop_device_make_by_path(
root_image,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&loop_device);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to create loop device for root image: %m"); return log_debug_errno(r, "Failed to create loop device for root image: %m");
r = verity_metadata_load(root_image, r = dissect_image(
root_hash_path, loop_device->fd,
root_hash ? NULL : &root_hash_decoded, &verity,
root_hash ? NULL : &root_hash_size, root_image_options,
root_verity ? NULL : &verity_data, dissect_image_flags,
root_hash_sig || root_hash_sig_path ? NULL : &hash_sig_path); &dissected_image);
if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
r = dissect_image(loop_device->fd,
root_hash ?: root_hash_decoded,
root_hash_size,
root_verity ?: verity_data,
root_image_options,
dissect_image_flags,
&dissected_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m"); return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, r = dissected_image_decrypt(
NULL, dissected_image,
root_hash ?: root_hash_decoded, NULL,
root_hash_size, &verity,
root_verity ?: verity_data, dissect_image_flags,
root_hash_sig_path ?: hash_sig_path, &decrypted_image);
root_hash_sig,
root_hash_sig_size,
dissect_image_flags,
&decrypted_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m"); return log_debug_errno(r, "Failed to decrypt dissected image: %m");
} }

View File

@ -11,6 +11,7 @@
#include "copy.h" #include "copy.h"
#include "dissect-image.h" #include "dissect-image.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h"
#include "format-table.h" #include "format-table.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
@ -43,19 +44,11 @@ static const char *arg_path = NULL;
static const char *arg_source = NULL; static const char *arg_source = NULL;
static const char *arg_target = NULL; static const char *arg_target = NULL;
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK; static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
static void *arg_root_hash = NULL; static VeritySettings arg_verity_settings = {};
static char *arg_verity_data = NULL;
static size_t arg_root_hash_size = 0;
static char *arg_root_hash_sig_path = NULL;
static void *arg_root_hash_sig = NULL;
static size_t arg_root_hash_sig_size = 0;
static bool arg_json = false; static bool arg_json = false;
static JsonFormatFlags arg_json_format_flags = 0; static JsonFormatFlags arg_json_format_flags = 0;
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
static int help(void) { static int help(void) {
_cleanup_free_ char *link = NULL; _cleanup_free_ char *link = NULL;
@ -105,10 +98,10 @@ static int parse_argv(int argc, char *argv[]) {
enum { enum {
ARG_VERSION = 0x100, ARG_VERSION = 0x100,
ARG_DISCARD, ARG_DISCARD,
ARG_ROOT_HASH,
ARG_FSCK, ARG_FSCK,
ARG_VERITY_DATA, ARG_ROOT_HASH,
ARG_ROOT_HASH_SIG, ARG_ROOT_HASH_SIG,
ARG_VERITY_DATA,
ARG_MKDIR, ARG_MKDIR,
ARG_JSON, ARG_JSON,
}; };
@ -119,10 +112,10 @@ static int parse_argv(int argc, char *argv[]) {
{ "mount", no_argument, NULL, 'm' }, { "mount", no_argument, NULL, 'm' },
{ "read-only", no_argument, NULL, 'r' }, { "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD }, { "discard", required_argument, NULL, ARG_DISCARD },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "fsck", required_argument, NULL, ARG_FSCK }, { "fsck", required_argument, NULL, ARG_FSCK },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG }, { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "mkdir", no_argument, NULL, ARG_MKDIR }, { "mkdir", no_argument, NULL, ARG_MKDIR },
{ "copy-from", no_argument, NULL, 'x' }, { "copy-from", no_argument, NULL, 'x' },
{ "copy-to", no_argument, NULL, 'a' }, { "copy-to", no_argument, NULL, 'a' },
@ -199,55 +192,47 @@ static int parse_argv(int argc, char *argv[]) {
} }
case ARG_ROOT_HASH: { case ARG_ROOT_HASH: {
void *p; _cleanup_free_ void *p = NULL;
size_t l; size_t l;
r = unhexmem(optarg, strlen(optarg), &p, &l); r = unhexmem(optarg, strlen(optarg), &p, &l);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg); return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg);
if (l < sizeof(sd_id128_t)) { if (l < sizeof(sd_id128_t))
log_error("Root hash must be at least 128bit long: %s", optarg); return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
free(p); "Root hash must be at least 128bit long: %s", optarg);
return -EINVAL;
free_and_replace(arg_verity_settings.root_hash, p);
arg_verity_settings.root_hash_size = l;
break;
}
case ARG_ROOT_HASH_SIG: {
char *value;
size_t l;
void *p;
if ((value = startswith(optarg, "base64:"))) {
r = unbase64mem(value, strlen(value), &p, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
} else {
r = read_full_file(optarg, (char**) &p, &l);
if (r < 0)
return log_error_errno(r, "Failed to read root hash signature file '%s': %m", optarg);
} }
free(arg_root_hash); free_and_replace(arg_verity_settings.root_hash_sig, p);
arg_root_hash = p; arg_verity_settings.root_hash_sig_size = l;
arg_root_hash_size = l;
break; break;
} }
case ARG_VERITY_DATA: case ARG_VERITY_DATA:
r = parse_path_argument_and_warn(optarg, false, &arg_verity_data); r = parse_path_argument_and_warn(optarg, false, &arg_verity_settings.data_path);
if (r < 0) if (r < 0)
return r; return r;
break; break;
case ARG_ROOT_HASH_SIG: {
char *value;
if ((value = startswith(optarg, "base64:"))) {
void *p;
size_t l;
r = unbase64mem(value, strlen(value), &p, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
free_and_replace(arg_root_hash_sig, p);
arg_root_hash_sig_size = l;
arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
} else {
r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
if (r < 0)
return r;
arg_root_hash_sig = mfree(arg_root_hash_sig);
arg_root_hash_sig_size = 0;
}
break;
}
case ARG_FSCK: case ARG_FSCK:
r = parse_boolean(optarg); r = parse_boolean(optarg);
if (r < 0) if (r < 0)
@ -483,7 +468,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
if (arg_verity_data) if (arg_verity_settings.data_path)
r = table_add_cell(t, NULL, TABLE_STRING, "external"); r = table_add_cell(t, NULL, TABLE_STRING, "external");
else if (dissected_image_can_do_verity(m, i)) else if (dissected_image_can_do_verity(m, i))
r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_has_verity(m, i))); r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_has_verity(m, i)));
@ -539,9 +524,7 @@ static int action_mount(DissectedImage *m, LoopDevice *d) {
r = dissected_image_decrypt_interactively( r = dissected_image_decrypt_interactively(
m, NULL, m, NULL,
arg_root_hash, arg_root_hash_size, &arg_verity_settings,
arg_verity_data,
arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
arg_flags, arg_flags,
&di); &di);
if (r < 0) if (r < 0)
@ -573,9 +556,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
r = dissected_image_decrypt_interactively( r = dissected_image_decrypt_interactively(
m, NULL, m, NULL,
arg_root_hash, arg_root_hash_size, &arg_verity_settings,
arg_verity_data,
arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
arg_flags, arg_flags,
&di); &di);
if (r < 0) if (r < 0)
@ -739,34 +720,30 @@ static int run(int argc, char *argv[]) {
if (r <= 0) if (r <= 0)
return r; return r;
r = verity_metadata_load( r = verity_settings_load(
arg_image, NULL, &arg_verity_settings,
arg_root_hash ? NULL : &arg_root_hash, arg_image, NULL, NULL);
&arg_root_hash_size,
arg_verity_data ? NULL : &arg_verity_data,
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image); return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
r = loop_device_make_by_path( if (arg_verity_settings.data_path)
arg_image,
(arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
arg_verity_data ? 0 : LO_FLAGS_PARTSCAN,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m");
if (arg_verity_data)
arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system, arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system,
* hence if there's external Verity data * hence if there's external Verity data
* available we turn off partition table * available we turn off partition table
* support */ * support */
r = loop_device_make_by_path(
arg_image,
FLAGS_SET(arg_flags, DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&d);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m");
r = dissect_image_and_warn( r = dissect_image_and_warn(
d->fd, d->fd,
arg_image, arg_image,
arg_root_hash, &arg_verity_settings,
arg_root_hash_size,
arg_verity_data,
NULL, NULL,
arg_flags, arg_flags,
&m); &m);

View File

@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) {
if (r <= 0) if (r <= 0)
return r; return r;
r = dissect_image(fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m); r = dissect_image(fd, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
if (r == -ENOPKG) { if (r == -ENOPKG) {
log_debug_errno(r, "No suitable partition table found, ignoring."); log_debug_errno(r, "No suitable partition table found, ignoring.");
return 0; return 0;

View File

@ -199,12 +199,7 @@ static bool arg_notify_ready = false;
static bool arg_use_cgns = true; static bool arg_use_cgns = true;
static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS; static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP; static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP;
static void *arg_root_hash = NULL; static VeritySettings arg_verity_settings = {};
static char *arg_verity_data = NULL;
static char *arg_root_hash_sig_path = NULL;
static void *arg_root_hash_sig = NULL;
static size_t arg_root_hash_sig_size = 0;
static size_t arg_root_hash_size = 0;
static char **arg_syscall_allow_list = NULL; static char **arg_syscall_allow_list = NULL;
static char **arg_syscall_deny_list = NULL; static char **arg_syscall_deny_list = NULL;
#if HAVE_SECCOMP #if HAVE_SECCOMP
@ -248,10 +243,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_oci_bundle, freep);
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep);
#if HAVE_SECCOMP #if HAVE_SECCOMP
@ -672,6 +664,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PRIVATE_USERS_CHOWN, ARG_PRIVATE_USERS_CHOWN,
ARG_NOTIFY_READY, ARG_NOTIFY_READY,
ARG_ROOT_HASH, ARG_ROOT_HASH,
ARG_ROOT_HASH_SIG,
ARG_VERITY_DATA,
ARG_SYSTEM_CALL_FILTER, ARG_SYSTEM_CALL_FILTER,
ARG_RLIMIT, ARG_RLIMIT,
ARG_HOSTNAME, ARG_HOSTNAME,
@ -684,8 +678,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PIPE, ARG_PIPE,
ARG_OCI_BUNDLE, ARG_OCI_BUNDLE,
ARG_NO_PAGER, ARG_NO_PAGER,
ARG_VERITY_DATA,
ARG_ROOT_HASH_SIG,
ARG_SET_CREDENTIAL, ARG_SET_CREDENTIAL,
ARG_LOAD_CREDENTIAL, ARG_LOAD_CREDENTIAL,
}; };
@ -743,6 +735,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT }, { "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT },
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER }, { "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER },
{ "rlimit", required_argument, NULL, ARG_RLIMIT }, { "rlimit", required_argument, NULL, ARG_RLIMIT },
{ "oom-score-adjust", required_argument, NULL, ARG_OOM_SCORE_ADJUST }, { "oom-score-adjust", required_argument, NULL, ARG_OOM_SCORE_ADJUST },
@ -753,8 +747,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "pipe", no_argument, NULL, ARG_PIPE }, { "pipe", no_argument, NULL, ARG_PIPE },
{ "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE }, { "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL }, { "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL }, { "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
{} {}
@ -1328,54 +1320,47 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_ROOT_HASH: { case ARG_ROOT_HASH: {
void *k; _cleanup_free_ void *k = NULL;
size_t l; size_t l;
r = unhexmem(optarg, strlen(optarg), &k, &l); r = unhexmem(optarg, strlen(optarg), &k, &l);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse root hash: %s", optarg); return log_error_errno(r, "Failed to parse root hash: %s", optarg);
if (l < sizeof(sd_id128_t)) { if (l < sizeof(sd_id128_t))
free(k);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash must be at least 128bit long: %s", optarg); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash must be at least 128bit long: %s", optarg);
}
free(arg_root_hash); free_and_replace(arg_verity_settings.root_hash, k);
arg_root_hash = k; arg_verity_settings.root_hash_size = l;
arg_root_hash_size = l;
break; break;
} }
case ARG_VERITY_DATA:
r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
if (r < 0)
return r;
break;
case ARG_ROOT_HASH_SIG: { case ARG_ROOT_HASH_SIG: {
char *value; char *value;
size_t l;
void *p;
if ((value = startswith(optarg, "base64:"))) { if ((value = startswith(optarg, "base64:"))) {
void *p;
size_t l;
r = unbase64mem(value, strlen(value), &p, &l); r = unbase64mem(value, strlen(value), &p, &l);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg); return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
free_and_replace(arg_root_hash_sig, p);
arg_root_hash_sig_size = l;
arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
} else { } else {
r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path); r = read_full_file(optarg, (char**) &p, &l);
if (r < 0) if (r < 0)
return r; return log_error_errno(r, "Failed parse root hash signature file '%s': %m", optarg);
arg_root_hash_sig = mfree(arg_root_hash_sig);
arg_root_hash_sig_size = 0;
} }
free_and_replace(arg_verity_settings.root_hash_sig, p);
arg_verity_settings.root_hash_sig_size = l;
break; break;
} }
case ARG_VERITY_DATA:
r = parse_path_argument_and_warn(optarg, false, &arg_verity_settings.data_path);
if (r < 0)
return r;
break;
case ARG_SYSTEM_CALL_FILTER: { case ARG_SYSTEM_CALL_FILTER: {
bool negative; bool negative;
const char *items; const char *items;
@ -5375,14 +5360,16 @@ static int run(int argc, char *argv[]) {
goto finish; goto finish;
} }
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size, r = verity_settings_load(
arg_verity_data ? NULL : &arg_verity_data, &arg_verity_settings,
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path); arg_image, NULL, NULL);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image); log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
goto finish; goto finish;
} }
dissect_image_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
if (arg_verity_settings.data_path)
dissect_image_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
} }
if (!mkdtemp(tmprootdir)) { if (!mkdtemp(tmprootdir)) {
@ -5398,7 +5385,11 @@ static int run(int argc, char *argv[]) {
goto finish; goto finish;
} }
r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop); r = loop_device_make_by_path(
arg_image,
arg_read_only ? O_RDONLY : O_RDWR,
FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
&loop);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to set up loopback block device: %m"); log_error_errno(r, "Failed to set up loopback block device: %m");
goto finish; goto finish;
@ -5407,8 +5398,7 @@ static int run(int argc, char *argv[]) {
r = dissect_image_and_warn( r = dissect_image_and_warn(
loop->fd, loop->fd,
arg_image, arg_image,
arg_root_hash, arg_root_hash_size, &arg_verity_settings,
arg_verity_data,
NULL, NULL,
dissect_image_flags, dissect_image_flags,
&dissected_image); &dissected_image);
@ -5425,10 +5415,15 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
goto finish; goto finish;
if (!arg_root_hash && dissected_image->can_verity) if (!arg_verity_settings.root_hash && dissected_image->can_verity)
log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image); log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, 0, &decrypted_image); r = dissected_image_decrypt_interactively(
dissected_image,
NULL,
&arg_verity_settings,
0,
&decrypted_image);
if (r < 0) if (r < 0)
goto finish; goto finish;

View File

@ -379,7 +379,7 @@ static int portable_extract_by_path(
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to create temporary directory: %m"); return log_debug_errno(r, "Failed to create temporary directory: %m");
r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); r = dissect_image(d->fd, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r == -ENOPKG) 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); sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
else if (r == -EADDRNOTAVAIL) else if (r == -EADDRNOTAVAIL)

View File

@ -304,9 +304,7 @@ static void check_partition_flags(
int dissect_image( int dissect_image(
int fd, int fd,
const void *root_hash, const VeritySettings *verity,
size_t root_hash_size,
const char *verity_data,
const MountOptions *mount_options, const MountOptions *mount_options,
DissectImageFlags flags, DissectImageFlags flags,
DissectedImage **ret) { DissectedImage **ret) {
@ -328,7 +326,7 @@ int dissect_image(
assert(fd >= 0); assert(fd >= 0);
assert(ret); assert(ret);
assert(root_hash || root_hash_size == 0); assert(!verity || verity->root_hash || verity->root_hash_size == 0);
assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE))); assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
/* Probes a disk image, and returns information about what it found in *ret. /* Probes a disk image, and returns information about what it found in *ret.
@ -336,16 +334,16 @@ int dissect_image(
* Returns -ENOPKG if no suitable partition table or file system could be found. * Returns -ENOPKG if no suitable partition table or file system could be found.
* Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */ * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
if (root_hash) { if (verity && verity->root_hash) {
/* If a root hash is supplied, then we use the root partition that has a UUID that match the first /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
* 128bit of the root hash. And we use the verity partition that has a UUID that match the final * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
* 128bit. */ * 128bit. */
if (root_hash_size < sizeof(sd_id128_t)) if (verity->root_hash_size < sizeof(sd_id128_t))
return -EINVAL; return -EINVAL;
memcpy(&root_uuid, root_hash, sizeof(sd_id128_t)); memcpy(&root_uuid, verity->root_hash, sizeof(sd_id128_t));
memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t)); memcpy(&verity_uuid, (const uint8_t*) verity->root_hash + verity->root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
if (sd_id128_is_null(root_uuid)) if (sd_id128_is_null(root_uuid))
return -EINVAL; return -EINVAL;
@ -416,8 +414,8 @@ int dissect_image(
return r; return r;
m->single_file_system = true; m->single_file_system = true;
m->verity = root_hash && verity_data; m->verity = verity && verity->root_hash && verity->data_path;
m->can_verity = !!verity_data; m->can_verity = verity && verity->data_path;
options = mount_options_from_designator(mount_options, PARTITION_ROOT); options = mount_options_from_designator(mount_options, PARTITION_ROOT);
if (options) { if (options) {
@ -815,7 +813,7 @@ int dissect_image(
/* If the root hash was set, then we won't fall back to a generic node, because the /* If the root hash was set, then we won't fall back to a generic node, because the
* root hash decides. */ * root hash decides. */
if (root_hash) if (verity && verity->root_hash)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
/* If we didn't find a generic node, then we can't fix this up either */ /* If we didn't find a generic node, then we can't fix this up either */
@ -846,7 +844,7 @@ int dissect_image(
} }
} }
if (root_hash) { if (verity && verity->root_hash) {
if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found) if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
@ -1333,14 +1331,20 @@ static int decrypt_partition(
return 0; return 0;
} }
static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool has_sig, const char *name, struct crypt_device **ret_cd) { static int verity_can_reuse(
const VeritySettings *verity,
const char *name,
struct crypt_device **ret_cd) {
/* If the same volume was already open, check that the root hashes match, and reuse it if they do */ /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
_cleanup_free_ char *root_hash_existing = NULL; _cleanup_free_ char *root_hash_existing = NULL;
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
struct crypt_params_verity crypt_params = {}; struct crypt_params_verity crypt_params = {};
size_t root_hash_existing_size = root_hash_size; size_t root_hash_existing_size;
int r; int r;
assert(verity);
assert(name);
assert(ret_cd); assert(ret_cd);
r = sym_crypt_init_by_name(&cd, name); r = sym_crypt_init_by_name(&cd, name);
@ -1351,20 +1355,23 @@ static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool h
if (r < 0) if (r < 0)
return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m"); return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
root_hash_existing = malloc0(root_hash_size); root_hash_existing_size = verity->root_hash_size;
root_hash_existing = malloc0(root_hash_existing_size);
if (!root_hash_existing) if (!root_hash_existing)
return -ENOMEM; return -ENOMEM;
r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0); r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m"); return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
if (root_hash_size != root_hash_existing_size || memcmp(root_hash_existing, root_hash, root_hash_size) != 0) if (verity->root_hash_size != root_hash_existing_size ||
memcmp(root_hash_existing, verity->root_hash, verity->root_hash_size) != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different."); return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different.");
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
/* Ensure that, if signatures are supported, we only reuse the device if the previous mount /* Ensure that, if signatures are supported, we only reuse the device if the previous mount used the
* used the same settings, so that a previous unsigned mount will not be reused if the user * same settings, so that a previous unsigned mount will not be reused if the user asks to use
* asks to use signing for the new one, and viceversa. */ * signing for the new one, and viceversa. */
if (has_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE)) if (!!verity->root_hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same."); return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same.");
#endif #endif
@ -1384,29 +1391,24 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
static int verity_partition( static int verity_partition(
DissectedPartition *m, DissectedPartition *m,
DissectedPartition *v, DissectedPartition *v,
const void *root_hash, const VeritySettings *verity,
size_t root_hash_size,
const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage *d) { DecryptedImage *d) {
_cleanup_free_ char *node = NULL, *name = NULL, *hash_sig_from_file = NULL;
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL; _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
_cleanup_free_ char *node = NULL, *name = NULL;
int r; int r;
assert(m); assert(m);
assert(v || verity_data); assert(v || (verity && verity->data_path));
if (!root_hash) if (!verity || !verity->root_hash)
return 0; return 0;
if (!m->found || !m->node || !m->fstype) if (!m->found || !m->node || !m->fstype)
return 0; return 0;
if (!verity_data) { if (!verity->data_path) {
if (!v->found || !v->node || !v->fstype) if (!v->found || !v->node || !v->fstype)
return 0; return 0;
@ -1422,7 +1424,7 @@ static int verity_partition(
/* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */ /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
_cleanup_free_ char *root_hash_encoded = NULL; _cleanup_free_ char *root_hash_encoded = NULL;
root_hash_encoded = hexmem(root_hash, root_hash_size); root_hash_encoded = hexmem(verity->root_hash, verity->root_hash_size);
if (!root_hash_encoded) if (!root_hash_encoded)
return -ENOMEM; return -ENOMEM;
@ -1432,13 +1434,7 @@ static int verity_partition(
if (r < 0) if (r < 0)
return r; return r;
if (!root_hash_sig && root_hash_sig_path) { r = sym_crypt_init(&cd, verity->data_path ?: v->node);
r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, &hash_sig_from_file, &root_hash_sig_size);
if (r < 0)
return r;
}
r = sym_crypt_init(&cd, verity_data ?: v->node);
if (r < 0) if (r < 0)
return r; return r;
@ -1459,20 +1455,33 @@ static int verity_partition(
* In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time, * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
* retry a few times before giving up. */ * retry a few times before giving up. */
for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) { for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
if (root_hash_sig || hash_sig_from_file) { if (verity->root_hash_sig) {
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
r = sym_crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, CRYPT_ACTIVATE_READONLY); r = sym_crypt_activate_by_signed_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
verity->root_hash_sig,
verity->root_hash_sig_size,
CRYPT_ACTIVATE_READONLY);
#else #else
r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()"); r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()");
#endif #endif
} else } else
r = sym_crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); r = sym_crypt_activate_by_volume_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
CRYPT_ACTIVATE_READONLY);
/* libdevmapper can return EINVAL when the device is already in the activation stage. /* libdevmapper can return EINVAL when the device is already in the activation stage.
* There's no way to distinguish this situation from a genuine error due to invalid * There's no way to distinguish this situation from a genuine error due to invalid
* parameters, so immediately fall back to activating the device with a unique name. * parameters, so immediately fall back to activating the device with a unique name.
* Improvements in libcrypsetup can ensure this never happens: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */ * Improvements in libcrypsetup can ensure this never happens:
* https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
if (!IN_SET(r, if (!IN_SET(r,
0, /* Success */ 0, /* Success */
-EEXIST, /* Volume is already open and ready to be used */ -EEXIST, /* Volume is already open and ready to be used */
@ -1495,10 +1504,10 @@ static int verity_partition(
} }
} }
r = verity_can_reuse(root_hash, root_hash_size, !!root_hash_sig || !!hash_sig_from_file, name, &existing_cd); r = verity_can_reuse(verity, name, &existing_cd);
/* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */ /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
if (!IN_SET(r, 0, -ENODEV, -ENOENT, -EBUSY)) if (!IN_SET(r, 0, -ENODEV, -ENOENT, -EBUSY))
return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node); return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node);
if (r == 0) { if (r == 0) {
@ -1526,7 +1535,7 @@ static int verity_partition(
/* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time. /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time.
* Fall back to activating it with a unique device name. */ * Fall back to activating it with a unique device name. */
if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
/* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */ /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
restore_deferred_remove = mfree(restore_deferred_remove); restore_deferred_remove = mfree(restore_deferred_remove);
@ -1544,12 +1553,7 @@ static int verity_partition(
int dissected_image_decrypt( int dissected_image_decrypt(
DissectedImage *m, DissectedImage *m,
const char *passphrase, const char *passphrase,
const void *root_hash, const VeritySettings *verity,
size_t root_hash_size,
const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage **ret) { DecryptedImage **ret) {
@ -1559,7 +1563,7 @@ int dissected_image_decrypt(
#endif #endif
assert(m); assert(m);
assert(root_hash || root_hash_size == 0); assert(!verity || verity->root_hash || verity->root_hash_size == 0);
/* Returns: /* Returns:
* *
@ -1569,7 +1573,7 @@ int dissected_image_decrypt(
* -EKEYREJECTED Passed key was not correct * -EKEYREJECTED Passed key was not correct
*/ */
if (root_hash && root_hash_size < sizeof(sd_id128_t)) if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
return -EINVAL; return -EINVAL;
if (!m->encrypted && !m->verity) { if (!m->encrypted && !m->verity) {
@ -1595,7 +1599,7 @@ int dissected_image_decrypt(
k = PARTITION_VERITY_OF(i); k = PARTITION_VERITY_OF(i);
if (k >= 0) { if (k >= 0) {
r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags | DISSECT_IMAGE_VERITY_SHARE, d); r = verity_partition(p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -1618,12 +1622,7 @@ int dissected_image_decrypt(
int dissected_image_decrypt_interactively( int dissected_image_decrypt_interactively(
DissectedImage *m, DissectedImage *m,
const char *passphrase, const char *passphrase,
const void *root_hash, const VeritySettings *verity,
size_t root_hash_size,
const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage **ret) { DecryptedImage **ret) {
@ -1634,7 +1633,7 @@ int dissected_image_decrypt_interactively(
n--; n--;
for (;;) { for (;;) {
r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags, ret); r = dissected_image_decrypt(m, passphrase, verity, flags, ret);
if (r >= 0) if (r >= 0)
return r; return r;
if (r == -EKEYREJECTED) if (r == -EKEYREJECTED)
@ -1686,126 +1685,138 @@ int decrypted_image_relinquish(DecryptedImage *d) {
return 0; return 0;
} }
int verity_metadata_load( static char *build_auxiliary_path(const char *image, const char *suffix) {
const char *image, const char *e;
const char *root_hash_path, char *n;
void **ret_roothash,
size_t *ret_roothash_size,
char **ret_verity_data,
char **ret_roothashsig) {
_cleanup_free_ char *verity_filename = NULL, *roothashsig_filename = NULL;
_cleanup_free_ void *roothash_decoded = NULL;
size_t roothash_decoded_size = 0;
int r;
assert(image); assert(image);
assert(suffix);
if (is_device_path(image)) { e = endswith(image, ".raw");
/* If we are asked to load the root hash for a device node, exit early */ if (!e)
if (ret_roothash) return strjoin(e, suffix);
*ret_roothash = NULL;
if (ret_roothash_size) n = new(char, e - image + strlen(suffix) + 1);
*ret_roothash_size = 0; if (!n)
if (ret_verity_data) return NULL;
*ret_verity_data = NULL;
if (ret_roothashsig) strcpy(mempcpy(n, image, e - image), suffix);
*ret_roothashsig = NULL; return n;
}
void verity_settings_done(VeritySettings *v) {
assert(v);
v->root_hash = mfree(v->root_hash);
v->root_hash_size = 0;
v->root_hash_sig = mfree(v->root_hash_sig);
v->root_hash_sig_size = 0;
v->data_path = mfree(v->data_path);
}
int verity_settings_load(
VeritySettings *verity,
const char *image,
const char *root_hash_path,
const char *root_hash_sig_path) {
_cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
size_t root_hash_size = 0, root_hash_sig_size = 0;
_cleanup_free_ char *verity_data_path = NULL;
int r;
assert(verity);
assert(image);
/* If we are asked to load the root hash for a device node, exit early */
if (is_device_path(image))
return 0; return 0;
}
if (ret_verity_data) { /* We only fill in what isn't already filled in */
char *e;
verity_filename = new(char, strlen(image) + STRLEN(".verity") + 1); if (!verity->root_hash) {
if (!verity_filename)
return -ENOMEM;
strcpy(verity_filename, image);
e = endswith(verity_filename, ".raw");
if (e)
strcpy(e, ".verity");
else
strcat(verity_filename, ".verity");
r = access(verity_filename, F_OK);
if (r < 0) {
if (errno != ENOENT)
return -errno;
verity_filename = mfree(verity_filename);
}
}
if (ret_roothashsig) {
char *e;
/* Follow naming convention recommended by the relevant RFC:
* https://tools.ietf.org/html/rfc5751#section-3.2.1 */
roothashsig_filename = new(char, strlen(image) + STRLEN(".roothash.p7s") + 1);
if (!roothashsig_filename)
return -ENOMEM;
strcpy(roothashsig_filename, image);
e = endswith(roothashsig_filename, ".raw");
if (e)
strcpy(e, ".roothash.p7s");
else
strcat(roothashsig_filename, ".roothash.p7s");
r = access(roothashsig_filename, R_OK);
if (r < 0) {
if (errno != ENOENT)
return -errno;
roothashsig_filename = mfree(roothashsig_filename);
}
}
if (ret_roothash) {
_cleanup_free_ char *text = NULL; _cleanup_free_ char *text = NULL;
assert(ret_roothash_size);
if (root_hash_path) { if (root_hash_path) {
/* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
r = read_one_line_file(root_hash_path, &text); r = read_one_line_file(root_hash_path, &text);
if (r < 0) if (r < 0)
return r; return r;
} else { } else {
r = getxattr_malloc(image, "user.verity.roothash", &text, true); r = getxattr_malloc(image, "user.verity.roothash", &text, true);
if (r < 0) { if (r < 0) {
char *fn, *e, *n; _cleanup_free_ char *p = NULL;
if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
return r; return r;
fn = newa(char, strlen(image) + STRLEN(".roothash") + 1); p = build_auxiliary_path(image, ".roothash");
n = stpcpy(fn, image); if (!p)
e = endswith(fn, ".raw"); return -ENOMEM;
if (e)
n = e;
strcpy(n, ".roothash"); r = read_one_line_file(p, &text);
r = read_one_line_file(fn, &text);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
} }
} }
if (text) { if (text) {
r = unhexmem(text, strlen(text), &roothash_decoded, &roothash_decoded_size); r = unhexmem(text, strlen(text), &root_hash, &root_hash_size);
if (r < 0) if (r < 0)
return r; return r;
if (roothash_decoded_size < sizeof(sd_id128_t)) if (root_hash_size < sizeof(sd_id128_t))
return -EINVAL; return -EINVAL;
} }
} }
if (ret_roothash) { if (!verity->root_hash_sig) {
*ret_roothash = TAKE_PTR(roothash_decoded); _cleanup_free_ char *p = NULL;
*ret_roothash_size = roothash_decoded_size;
if (!root_hash_sig_path) {
/* Follow naming convention recommended by the relevant RFC:
* https://tools.ietf.org/html/rfc5751#section-3.2.1 */
p = build_auxiliary_path(image, ".roothash.p7s");
if (!p)
return -ENOMEM;
root_hash_sig_path = p;
}
r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, (char**) &root_hash_sig, &root_hash_sig_size);
if (r < 0) {
if (r != -ENOENT)
return r;
} else if (root_hash_sig_size == 0) /* refuse empty size signatures */
return -EINVAL;
} }
if (ret_verity_data)
*ret_verity_data = TAKE_PTR(verity_filename); if (!verity->data_path) {
if (roothashsig_filename) _cleanup_free_ char *p = NULL;
*ret_roothashsig = TAKE_PTR(roothashsig_filename);
p = build_auxiliary_path(image, ".verity");
if (!p)
return -ENOMEM;
if (access(p, F_OK) < 0) {
if (errno != ENOENT)
return -errno;
} else
verity_data_path = TAKE_PTR(p);
}
if (root_hash) {
verity->root_hash = TAKE_PTR(root_hash);
verity->root_hash_size = root_hash_size;
}
if (root_hash_sig) {
verity->root_hash_sig = TAKE_PTR(root_hash_sig);
verity->root_hash_sig_size = root_hash_sig_size;
}
if (verity_data_path)
verity->data_path = TAKE_PTR(verity_data_path);
return 1; return 1;
} }
@ -1988,9 +1999,7 @@ finish:
int dissect_image_and_warn( int dissect_image_and_warn(
int fd, int fd,
const char *name, const char *name,
const void *root_hash, const VeritySettings *verity,
size_t root_hash_size,
const char *verity_data,
const MountOptions *mount_options, const MountOptions *mount_options,
DissectImageFlags flags, DissectImageFlags flags,
DissectedImage **ret) { DissectedImage **ret) {
@ -2006,7 +2015,7 @@ int dissect_image_and_warn(
name = buffer; name = buffer;
} }
r = dissect_image(fd, root_hash, root_hash_size, verity_data, mount_options, flags, ret); r = dissect_image(fd, verity, mount_options, flags, ret);
switch (r) { switch (r) {
@ -2110,11 +2119,11 @@ int mount_image_privately_interactively(
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m"); return log_error_errno(r, "Failed to set up loopback device: %m");
r = dissect_image_and_warn(d->fd, image, NULL, 0, NULL, NULL, flags, &dissected_image); r = dissect_image_and_warn(d->fd, image, NULL, NULL, flags, &dissected_image);
if (r < 0) if (r < 0)
return r; return r;
r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, flags, &decrypted_image); r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, flags, &decrypted_image);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -13,6 +13,7 @@ typedef struct DissectedImage DissectedImage;
typedef struct DissectedPartition DissectedPartition; typedef struct DissectedPartition DissectedPartition;
typedef struct DecryptedImage DecryptedImage; typedef struct DecryptedImage DecryptedImage;
typedef struct MountOptions MountOptions; typedef struct MountOptions MountOptions;
typedef struct VeritySettings VeritySettings;
struct DissectedPartition { struct DissectedPartition {
bool found:1; bool found:1;
@ -92,19 +93,32 @@ struct MountOptions {
LIST_FIELDS(MountOptions, mount_options); LIST_FIELDS(MountOptions, mount_options);
}; };
struct VeritySettings {
/* Binary root hash for the Verity Merkle tree */
void *root_hash;
size_t root_hash_size;
/* PKCS#7 signature of the above */
void *root_hash_sig;
size_t root_hash_sig_size;
/* Path to the verity data file, if stored externally */
char *data_path;
};
MountOptions* mount_options_free_all(MountOptions *options); MountOptions* mount_options_free_all(MountOptions *options);
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all); DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator); const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
int probe_filesystem(const char *node, char **ret_fstype); int probe_filesystem(const char *node, char **ret_fstype);
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret); int dissect_image(int fd, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret); int dissect_image_and_warn(int fd, const char *name, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m); DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags); int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags); int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags);
@ -117,7 +131,9 @@ int decrypted_image_relinquish(DecryptedImage *d);
const char* partition_designator_to_string(PartitionDesignator d) _const_; const char* partition_designator_to_string(PartitionDesignator d) _const_;
PartitionDesignator partition_designator_from_string(const char *name) _pure_; PartitionDesignator partition_designator_from_string(const char *name) _pure_;
int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig); int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
void verity_settings_done(VeritySettings *verity);
bool dissected_image_can_do_verity(const DissectedImage *image, PartitionDesignator d); bool dissected_image_can_do_verity(const DissectedImage *image, PartitionDesignator d);
bool dissected_image_has_verity(const DissectedImage *image, PartitionDesignator d); bool dissected_image_has_verity(const DissectedImage *image, PartitionDesignator d);

View File

@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) {
if (r < 0) if (r < 0)
return r; return r;
r = dissect_image(d->fd, NULL, 0, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); r = dissect_image(d->fd, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0) if (r < 0)
return r; return r;