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>
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>
_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;
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");
}
#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"
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);
}
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 },
{}
};
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)
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;
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
DissectedPartition *p = m->partitions + i;
- int k;
if (!p->found)
continue;
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);
}
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;
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;
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;
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
" --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"
ARG_PIPE,
ARG_OCI_BUNDLE,
ARG_NO_PAGER,
+ ARG_VERITY_DATA,
};
static const struct option options[] = {
{ "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 },
{}
};
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;
}
} else {
+ DissectImageFlags dissect_image_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK;
assert(arg_image);
assert(!arg_template);
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)) {
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 */
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;
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)
int fd,
const void *root_hash,
size_t root_hash_size,
+ const char *verity_data,
DissectImageFlags flags,
DissectedImage **ret) {
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.
*
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);
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),
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
- if (!streq(usage, "filesystem")) {
- r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
- if (r < 0)
- return r;
- }
+ /* Even on a single partition we need to wait for udev to create the
+ * /dev/block/X:Y symlink to /dev/loopZ */
+ r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
+ if (r < 0)
+ return r;
*ret = TAKE_PTR(m);
return 0;
DissectedPartition *v,
const void *root_hash,
size_t root_hash_size,
+ const char *verity_data,
DissectImageFlags flags,
DecryptedImage *d) {
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)
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;
const char *passphrase,
const void *root_hash,
size_t root_hash_size,
+ const char *verity_data,
DissectImageFlags flags,
DecryptedImage **ret) {
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;
}
const char *passphrase,
const void *root_hash,
size_t root_hash_size,
+ const char *verity_data,
DissectImageFlags flags,
DecryptedImage **ret) {
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)
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 (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
- return r;
+ if (ret_verity_data) {
+ char *e;
- 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(n, ".roothash");
+ strcpy(e, ".verity");
+ else
+ strcat(verity_filename, ".verity");
- 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);
+
+ 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;
- *ret = TAKE_PTR(k);
- *ret_size = l;
+ 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;
}
const char *name,
const void *root_hash,
size_t root_hash_size,
+ const char *verity_data,
DissectImageFlags flags,
DissectedImage **ret) {
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) {
}
}
+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",
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];
};
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);
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);
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;
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;
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/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
--- /dev/null
+[Unit]
+Description=TEST-50-DISSECT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/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