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:
+105
-46
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user