dissect: support single-filesystem verity images with external verity hash

dm-verity support in dissect-image at the moment is restricted to GPT
volumes.
If the image a single-filesystem type without a partition table (eg: squashfs)
and a roothash/verity file are passed, set the verity flag and mark as
read-only.
This commit is contained in:
Luca Boccassi 2020-05-29 17:51:20 +01:00
parent b1806441bb
commit e7cbe5cb9e
14 changed files with 271 additions and 93 deletions

View File

@ -302,6 +302,10 @@
hash partitions are set up if the root hash for them is specified using the <option>--root-hash=</option>
option.</para>
<para>Single file system images (i.e. file systems without a surrounding partition table) can be opened using
dm-verity if the integrity data is passed using the <option>--root-hash=</option> and
<option>--verity-data=</option> options.</para>
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
</varlistentry>
@ -390,8 +394,20 @@
project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>), then the root
hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or
is not supported by the underlying file system), but a file with the <filename>.roothash</filename> suffix is
found next to the image file, bearing otherwise the same name, the root hash is read from it and automatically
used, also as formatted hexadecimal characters.</para></listitem>
found next to the image file, bearing otherwise the same name (except if the image has the
<filename>.raw</filename> suffix, in which case the root hash file must not have it in its name), the root hash
is read from it and automatically used, also as formatted hexadecimal characters.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--verity-data=</option></term>
<listitem><para>Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks
using dm-verity, if a root-hash is passed and if the used image itself does not contains the integrity data.
The integrity data must be matched by the root hash. If this option is not specified, but a file with the
<filename>.verity</filename> suffix is found next to the image file, bearing otherwise the same name (except if
the image has the <filename>.raw</filename> suffix, in which case the verity data file must not have it in its name),
the verity data is read from it and automatically used.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -1264,6 +1264,7 @@ int setup_namespace(
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash = NULL;
_cleanup_free_ char *verity_data = NULL;
MountEntry *m = NULL, *mounts = NULL;
size_t n_mounts, root_hash_size = 0;
bool require_prefix = false;
@ -1294,15 +1295,16 @@ int setup_namespace(
if (r < 0)
return log_debug_errno(r, "Failed to create loop device for root image: %m");
r = root_hash_load(root_image, &root_hash, &root_hash_size);
r = verity_metadata_load(root_image, &root_hash, &root_hash_size, &verity_data);
if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags |= verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
r = dissect_image(loop_device->fd, root_hash, root_hash_size, verity_data, dissect_image_flags, &dissected_image);
if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, verity_data, dissect_image_flags, &decrypted_image);
if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m");
}

View File

@ -12,6 +12,7 @@
#include "loop-util.h"
#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@ -25,21 +26,25 @@ static const char *arg_image = NULL;
static const char *arg_path = NULL;
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 char *arg_verity_data = NULL;
static size_t arg_root_hash_size = 0;
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
static void help(void) {
printf("%s [OPTIONS...] IMAGE\n"
"%s [OPTIONS...] --mount IMAGE PATH\n"
"Dissect a file system OS image.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -m --mount Mount the image to the specified directory\n"
" -r --read-only Mount read-only\n"
" --fsck=BOOL Run fsck before mounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n",
" -h --help Show this help\n"
" --version Show package version\n"
" -m --mount Mount the image to the specified directory\n"
" -r --read-only Mount read-only\n"
" --fsck=BOOL Run fsck before mounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n"
" --verity-data=PATH Specify data file with hash tree for verity if it is\n"
" not embedded in IMAGE\n",
program_invocation_short_name,
program_invocation_short_name);
}
@ -51,16 +56,18 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DISCARD,
ARG_ROOT_HASH,
ARG_FSCK,
ARG_VERITY_DATA,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "mount", no_argument, NULL, 'm' },
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "fsck", required_argument, NULL, ARG_FSCK },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "mount", no_argument, NULL, 'm' },
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "fsck", required_argument, NULL, ARG_FSCK },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{}
};
@ -127,6 +134,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_VERITY_DATA:
r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
if (r < 0)
return r;
break;
case ARG_FSCK:
r = parse_boolean(optarg);
if (r < 0)
@ -188,13 +201,13 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device: %m");
if (!arg_root_hash) {
r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
if (r < 0)
return log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
}
r = verity_metadata_load(arg_image, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
arg_verity_data ? NULL : &arg_verity_data);
if (r < 0)
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_flags, &m);
r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &m);
if (r < 0)
return r;
@ -205,7 +218,6 @@ static int run(int argc, char *argv[]) {
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
DissectedPartition *p = m->partitions + i;
int k;
if (!p->found)
continue;
@ -223,9 +235,8 @@ static int run(int argc, char *argv[]) {
if (p->architecture != _ARCHITECTURE_INVALID)
printf(" for %s", architecture_to_string(p->architecture));
k = PARTITION_VERITY_OF(i);
if (k >= 0)
printf(" %s verity", m->partitions[k].found ? "with" : "without");
if (dissected_image_can_do_verity(m, i))
printf(" %s verity", dissected_image_has_verity(m, i) ? "with" : "without");
if (p->partno >= 0)
printf(" on partition #%i", p->partno);
@ -268,7 +279,7 @@ static int run(int argc, char *argv[]) {
}
case ACTION_MOUNT:
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &di);
if (r < 0)
return r;

View File

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

View File

@ -199,6 +199,7 @@ static bool arg_use_cgns = true;
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 void *arg_root_hash = NULL;
static char *arg_verity_data = NULL;
static size_t arg_root_hash_size = 0;
static char **arg_syscall_whitelist = NULL;
static char **arg_syscall_blacklist = NULL;
@ -242,6 +243,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_whitelist, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_blacklist, strv_freep);
#if HAVE_SECCOMP
@ -303,6 +305,7 @@ static int help(void) {
" --read-only Mount the root directory read-only\n"
" --volatile[=MODE] Run the system in volatile mode\n"
" --root-hash=HASH Specify verity root hash for root disk image\n"
" --verity-data=PATH Specify hash device for verity\n"
" --pivot-root=PATH[:PATH]\n"
" Pivot root to given directory in the container\n\n"
"%3$sExecution:%4$s\n"
@ -663,6 +666,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PIPE,
ARG_OCI_BUNDLE,
ARG_NO_PAGER,
ARG_VERITY_DATA,
};
static const struct option options[] = {
@ -728,6 +732,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{}
};
@ -1316,6 +1321,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_VERITY_DATA:
r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
if (r < 0)
return r;
break;
case ARG_SYSTEM_CALL_FILTER: {
bool negative;
const char *items;
@ -5080,6 +5091,7 @@ static int run(int argc, char *argv[]) {
}
} else {
DissectImageFlags dissect_image_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK;
assert(arg_image);
assert(!arg_template);
@ -5129,13 +5141,13 @@ static int run(int argc, char *argv[]) {
goto finish;
}
if (!arg_root_hash) {
r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
if (r < 0) {
log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image);
goto finish;
}
r = verity_metadata_load(arg_image, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
arg_verity_data ? NULL : &arg_verity_data);
if (r < 0) {
log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
goto finish;
}
dissect_image_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
}
if (!mkdtemp(tmprootdir)) {
@ -5161,7 +5173,8 @@ static int run(int argc, char *argv[]) {
loop->fd,
arg_image,
arg_root_hash, arg_root_hash_size,
DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK,
arg_verity_data,
dissect_image_flags,
&dissected_image);
if (r == -ENOPKG) {
/* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
@ -5179,7 +5192,7 @@ static int run(int argc, char *argv[]) {
if (!arg_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);
r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image);
r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, 0, &decrypted_image);
if (r < 0)
goto finish;

View File

@ -380,7 +380,7 @@ static int portable_extract_by_path(
if (r < 0)
return log_debug_errno(r, "Failed to create temporary directory: %m");
r = dissect_image(d->fd, NULL, 0, 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, 0, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &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);
else if (r == -EADDRNOTAVAIL)

View File

@ -307,6 +307,7 @@ int dissect_image(
int fd,
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
DissectImageFlags flags,
DissectedImage **ret) {
@ -329,6 +330,7 @@ int dissect_image(
assert(fd >= 0);
assert(ret);
assert(root_hash || root_hash_size == 0);
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.
*
@ -391,8 +393,9 @@ int dissect_image(
if (r < 0)
return r;
if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) ||
(flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
const char *usage = NULL;
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
@ -413,9 +416,13 @@ int dissect_image(
if (r < 0)
return r;
m->single_file_system = true;
m->verity = root_hash && verity_data;
m->can_verity = !!verity_data;
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = true,
.rw = !m->verity,
.partno = -1,
.architecture = _ARCHITECTURE_INVALID,
.fstype = TAKE_PTR(t),
@ -1215,6 +1222,7 @@ static int verity_partition(
DissectedPartition *v,
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
DissectImageFlags flags,
DecryptedImage *d) {
@ -1223,18 +1231,20 @@ static int verity_partition(
int r;
assert(m);
assert(v);
assert(v || verity_data);
if (!root_hash)
return 0;
if (!m->found || !m->node || !m->fstype)
return 0;
if (!v->found || !v->node || !v->fstype)
return 0;
if (!verity_data) {
if (!v->found || !v->node || !v->fstype)
return 0;
if (!streq(v->fstype, "DM_verity_hash"))
return 0;
if (!streq(v->fstype, "DM_verity_hash"))
return 0;
}
r = make_dm_name_and_node(m->node, "-verity", &name, &node);
if (r < 0)
@ -1243,7 +1253,7 @@ static int verity_partition(
if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
return -ENOMEM;
r = crypt_init(&cd, v->node);
r = crypt_init(&cd, verity_data ?: v->node);
if (r < 0)
return r;
@ -1276,6 +1286,7 @@ int dissected_image_decrypt(
const char *passphrase,
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
DissectImageFlags flags,
DecryptedImage **ret) {
@ -1322,7 +1333,7 @@ int dissected_image_decrypt(
k = PARTITION_VERITY_OF(i);
if (k >= 0) {
r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, flags, d);
if (r < 0)
return r;
}
@ -1347,6 +1358,7 @@ int dissected_image_decrypt_interactively(
const char *passphrase,
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
DissectImageFlags flags,
DecryptedImage **ret) {
@ -1357,7 +1369,7 @@ int dissected_image_decrypt_interactively(
n--;
for (;;) {
r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, flags, ret);
if (r >= 0)
return r;
if (r == -EKEYREJECTED)
@ -1409,56 +1421,85 @@ int decrypted_image_relinquish(DecryptedImage *d) {
return 0;
}
int root_hash_load(const char *image, void **ret, size_t *ret_size) {
_cleanup_free_ char *text = NULL;
_cleanup_free_ void *k = NULL;
size_t l;
int verity_metadata_load(const char *image, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data) {
_cleanup_free_ char *verity_filename = NULL;
_cleanup_free_ void *roothash_decoded = NULL;
size_t roothash_decoded_size = 0;
int r;
assert(image);
assert(ret);
assert(ret_size);
if (is_device_path(image)) {
/* If we are asked to load the root hash for a device node, exit early */
*ret = NULL;
*ret_size = 0;
if (ret_roothash)
*ret_roothash = NULL;
if (ret_roothash_size)
*ret_roothash_size = 0;
if (ret_verity_data)
*ret_verity_data = NULL;
return 0;
}
r = getxattr_malloc(image, "user.verity.roothash", &text, true);
if (r < 0) {
char *fn, *e, *n;
if (ret_verity_data) {
char *e;
if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
return r;
fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
n = stpcpy(fn, image);
e = endswith(fn, ".raw");
verity_filename = new(char, strlen(image) + STRLEN(".verity") + 1);
if (!verity_filename)
return -ENOMEM;
strcpy(verity_filename, image);
e = endswith(verity_filename, ".raw");
if (e)
n = e;
strcpy(e, ".verity");
else
strcat(verity_filename, ".verity");
strcpy(n, ".roothash");
r = read_one_line_file(fn, &text);
if (r == -ENOENT) {
*ret = NULL;
*ret_size = 0;
return 0;
r = access(verity_filename, F_OK);
if (r < 0) {
if (errno != ENOENT)
return -errno;
verity_filename = mfree(verity_filename);
}
if (r < 0)
return r;
}
r = unhexmem(text, strlen(text), &k, &l);
if (r < 0)
return r;
if (l < sizeof(sd_id128_t))
return -EINVAL;
if (ret_roothash) {
_cleanup_free_ char *text = NULL;
assert(ret_roothash_size);
*ret = TAKE_PTR(k);
*ret_size = l;
r = getxattr_malloc(image, "user.verity.roothash", &text, true);
if (r < 0) {
char *fn, *e, *n;
if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
return r;
fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
n = stpcpy(fn, image);
e = endswith(fn, ".raw");
if (e)
n = e;
strcpy(n, ".roothash");
r = read_one_line_file(fn, &text);
if (r < 0 && r != -ENOENT)
return r;
}
if (text) {
r = unhexmem(text, strlen(text), &roothash_decoded, &roothash_decoded_size);
if (r < 0)
return r;
if (roothash_decoded_size < sizeof(sd_id128_t))
return -EINVAL;
}
}
if (ret_roothash) {
*ret_roothash = TAKE_PTR(roothash_decoded);
*ret_roothash_size = roothash_decoded_size;
}
if (ret_verity_data)
*ret_verity_data = TAKE_PTR(verity_filename);
return 1;
}
@ -1617,6 +1658,7 @@ int dissect_image_and_warn(
const char *name,
const void *root_hash,
size_t root_hash_size,
const char *verity_data,
DissectImageFlags flags,
DissectedImage **ret) {
@ -1631,7 +1673,7 @@ int dissect_image_and_warn(
name = buffer;
}
r = dissect_image(fd, root_hash, root_hash_size, flags, ret);
r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret);
switch (r) {
@ -1661,6 +1703,23 @@ int dissect_image_and_warn(
}
}
bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator) {
if (image->single_file_system)
return partition_designator == PARTITION_ROOT && image->can_verity;
return PARTITION_VERITY_OF(partition_designator) >= 0;
}
bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator) {
int k;
if (image->single_file_system)
return partition_designator == PARTITION_ROOT && image->verity;
k = PARTITION_VERITY_OF(partition_designator);
return k >= 0 && image->partitions[k].found;
}
static const char *const partition_designator_table[] = {
[PARTITION_ROOT] = "root",
[PARTITION_ROOT_SECONDARY] = "root-secondary",

View File

@ -63,12 +63,14 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */
DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */
} DissectImageFlags;
struct DissectedImage {
bool encrypted:1;
bool verity:1; /* verity available and usable */
bool can_verity:1; /* verity available, but not necessarily used */
bool single_file_system:1; /* MBR/GPT or single file system */
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
@ -79,14 +81,14 @@ struct DissectedImage {
};
int probe_filesystem(const char *node, char **ret_fstype);
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, 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, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
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, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, 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, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
int dissected_image_acquire_metadata(DissectedImage *m);
@ -98,4 +100,6 @@ int decrypted_image_relinquish(DecryptedImage *d);
const char* partition_designator_to_string(int i) _const_;
int partition_designator_from_string(const char *name) _pure_;
int root_hash_load(const char *image, void **ret, size_t *ret_size);
int verity_metadata_load(const char *image, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data);
bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator);
bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator);

View File

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

View File

@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
return EXIT_FAILURE;

View File

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

34
test/TEST-50-DISSECT/test.sh Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
TEST_DESCRIPTION="test systemd-dissect"
IMAGE_NAME="dissect"
TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
command -v mksquashfs >/dev/null 2>&1 || exit 0
command -v veritysetup >/dev/null 2>&1 || exit 0
# Need loop devices for systemd-dissect
test_create_image() {
create_empty_image_rootdir
# Create what will eventually be our root filesystem onto an overlay
# If some pieces are missing from the host, skip rather than fail
(
LOG_LEVEL=5
setup_basic_environment
mask_supporting_services
instmods loop =block
instmods squashfs =squashfs
instmods dm_verity =md
generate_module_dependencies
inst_binary mksquashfs
inst_binary veritysetup
)
}
do_test "$@" 50

View File

@ -0,0 +1,7 @@
[Unit]
Description=TEST-50-DISSECT
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot

31
test/units/testsuite-50.sh Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
set -o pipefail
cd /tmp
image=$(mktemp -d -t -p /tmp tmp.XXXXXX)
if [ -z "${image}" ] || [ ! -d "${image}" ]; then
echo "Could not create temporary directory with mktemp under /tmp"
exit 1
fi
mkdir -p ${image}/usr/lib ${image}/etc
cp /usr/lib/os-release ${image}/usr/lib/
cp /etc/machine-id /etc/os-release ${image}/etc/
mksquashfs ${image} ${image}.raw
veritysetup format ${image}.raw ${image}.verity | grep '^Root hash:' | cut -f2 | tr -d '\n' > ${image}.roothash
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=`cat ${image}.foohash` --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=`cat ${image}.foohash` --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
echo OK > /testok
exit 0