From 2eedfd2d8b3441e8cf6dae4bdc9afaefbda19c39 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Dec 2016 11:09:47 +0100 Subject: dissect: make sure to manually follow symlinks when mounting dissected image If the dissected image contains symlinks for the mount points we need we need to make sure to follow this with chase_symlinks() so that we don't leave the image. --- src/shared/dissect-image.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'src/shared/dissect-image.c') diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 878cb008aa..5fc2ce25f0 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -28,6 +28,7 @@ #include "blkid-util.h" #include "dissect-image.h" #include "fd-util.h" +#include "fs-util.h" #include "gpt.h" #include "mount-util.h" #include "path-util.h" @@ -35,6 +36,7 @@ #include "stdio-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "udev-util.h" static int probe_filesystem(const char *node, char **ret_fstype) { @@ -624,7 +626,9 @@ static int mount_partition( DissectImageFlags flags) { const char *p, *options = NULL, *node, *fstype; + _cleanup_free_ char *chased = NULL; bool rw; + int r; assert(m); assert(where); @@ -641,9 +645,13 @@ static int mount_partition( rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY); - if (directory) - p = strjoina(where, directory); - else + if (directory) { + r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased); + if (r < 0) + return r; + + p = chased; + } else p = where; /* If requested, turn on discard support. */ @@ -677,22 +685,23 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag return r; if (m->partitions[PARTITION_ESP].found) { - const char *mp, *x; + const char *mp; /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */ - mp = "/efi"; - x = strjoina(where, mp); - r = dir_is_empty(x); - if (r == -ENOENT) { - mp = "/boot"; - x = strjoina(where, mp); - r = dir_is_empty(x); - } - if (r > 0) { - r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags); + FOREACH_STRING(mp, "/efi", "/boot") { + _cleanup_free_ char *p = NULL; + + r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p); if (r < 0) - return r; + continue; + + r = dir_is_empty(p); + if (r > 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags); + if (r < 0) + return r; + } } } -- cgit v1.2.3-54-g00ecf From 78ebe98061eb527f17691929f470f262a7ab2c8f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Dec 2016 17:10:42 +0100 Subject: core,nspawn,dissect: make nspawn's .roothash file search reusable This makes nspawn's logic of automatically discovering the root hash of an image file generic, and then reuses it in systemd-dissect and in PID1's RootImage= logic, so that verity is automatically set up whenever we can. --- src/core/namespace.c | 17 +++++++++++++- src/dissect/dissect.c | 8 +++++++ src/nspawn/nspawn.c | 57 ++++++---------------------------------------- src/shared/dissect-image.c | 51 +++++++++++++++++++++++++++++++++++++++++ src/shared/dissect-image.h | 2 ++ 5 files changed, 84 insertions(+), 51 deletions(-) (limited to 'src/shared/dissect-image.c') diff --git a/src/core/namespace.c b/src/core/namespace.c index 0ae5f704c7..75dca5b791 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -883,8 +883,11 @@ int setup_namespace( DissectImageFlags dissect_image_flags) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; + _cleanup_free_ void *root_hash = NULL; MountEntry *m, *mounts = NULL; + size_t root_hash_size = 0; bool make_slave = false; unsigned n_mounts; int r = 0; @@ -906,7 +909,15 @@ int setup_namespace( if (r < 0) return r; - r = dissect_image(loop_device->fd, NULL, 0, dissect_image_flags, &dissected_image); + r = root_hash_load(root_image, &root_hash, &root_hash_size); + if (r < 0) + return r; + + r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image); + if (r < 0) + return r; + + r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image); if (r < 0) return r; @@ -1038,6 +1049,10 @@ int setup_namespace( if (r < 0) goto finish; + r = decrypted_image_relinquish(decrypted_image); + if (r < 0) + goto finish; + loop_device_relinquish(loop_device); } else if (root_directory) { diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index fd9db5ba87..59bd7d9e84 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -191,6 +191,14 @@ int main(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 read root hash file for %s: %m", arg_image); + goto finish; + } + } + r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m); if (r == -ENOPKG) { log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 5594b87efa..213f50f796 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3480,53 +3480,6 @@ static int run(int master, return 1; /* loop again */ } -static int load_root_hash(const char *image) { - _cleanup_free_ char *text = NULL, *fn = NULL; - char *n, *e; - void *k; - size_t l; - int r; - - assert_se(image); - - /* Try to load the root hash from a file next to the image file if it exists. */ - - if (arg_root_hash) - return 0; - - fn = new(char, strlen(image) + strlen(".roothash") + 1); - if (!fn) - return log_oom(); - - n = stpcpy(fn, image); - e = endswith(fn, ".raw"); - if (e) - n = e; - - strcpy(n, ".roothash"); - - r = read_one_line_file(fn, &text); - if (r == -ENOENT) - return 0; - if (r < 0) { - log_warning_errno(r, "Failed to read %s, ignoring: %m", fn); - return 0; - } - - r = unhexmem(text, strlen(text), &k, &l); - if (r < 0) - return log_error_errno(r, "Invalid root hash: %s", text); - if (l < sizeof(sd_id128_t)) { - free(k); - return log_error_errno(r, "Root hash too short: %s", text); - } - - arg_root_hash = k; - arg_root_hash_size = l; - - return 0; -} - int main(int argc, char *argv[]) { _cleanup_free_ char *console = NULL; @@ -3742,9 +3695,13 @@ int main(int argc, char *argv[]) { goto finish; } - r = load_root_hash(arg_image); - if (r < 0) - 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; + } + } } if (!mkdtemp(tmprootdir)) { diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 5fc2ce25f0..f3cd663602 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -28,8 +28,10 @@ #include "blkid-util.h" #include "dissect-image.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "gpt.h" +#include "hexdecoct.h" #include "mount-util.h" #include "path-util.h" #include "stat-util.h" @@ -1087,6 +1089,55 @@ 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; + char *fn, *e, *n; + size_t l; + 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; + return 0; + } + + 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 == -ENOENT) { + *ret = NULL; + *ret_size = 0; + return 0; + } + 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; + + *ret = k; + *ret_size = l; + + k = NULL; + + return 1; +} + static const char *const partition_designator_table[] = { [PARTITION_ROOT] = "root", [PARTITION_ROOT_SECONDARY] = "root-secondary", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 26319bd8e7..cdb083be6f 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -94,3 +94,5 @@ 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); -- cgit v1.2.3-54-g00ecf From 41488e1f7acf5f4b5e11ff992a05ee1baa537d54 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Dec 2016 17:38:12 +0100 Subject: dissect: try to read roothash value off user.verity.roothash xattr of image file This slightly extends the roothash loading logic to first check for a user.verity.roothash extended attribute on the image file. If it exists, it is used as Verity root hash and the ".roothash" file is not used. This should improve the chance that the roothash is retained when the file is moved around, as the data snippet is attached directly to the image file. The field is still detached from the file payload however, in order to make sure it may be trusted independently. This does not replace the ".roothash" file loading, it simply adds a second way to retrieve the data. Extended attributes are often a poor choice for storing metadata like this as it is usually difficult to discover for admins and users, and hard to fix if it ever gets out of sync. However, in this case I think it's safe as verity implies read-only access, and thus there's little chance of it to get out of sync. --- man/systemd-nspawn.xml | 10 +++++++--- src/shared/dissect-image.c | 36 ++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 17 deletions(-) (limited to 'src/shared/dissect-image.c') diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index f6b3f57fc7..b8cae62818 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -257,9 +257,13 @@ Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The specified hash must match the root hash of integrity data, and is usually at least 256bits (and hence 64 - hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but a file with - the .roothash suffix is found next to the image file, bearing otherwise the same name the - root hash is read from it and automatically used. + formatted hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but + the image file carries the user.verity.roothash extended file attribute (see xattr7), then the root + hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or + not supported by the underlying file system), but a file with the .roothash suffix is + found next to the image file, bearing otherwise the same name the root hash is read from it and automatically + used (again, as formatted hexadecimal characters). diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index f3cd663602..66ddf3a872 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -40,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "udev-util.h" +#include "xattr-util.h" static int probe_filesystem(const char *node, char **ret_fstype) { #ifdef HAVE_BLKID @@ -1092,7 +1093,6 @@ int decrypted_image_relinquish(DecryptedImage *d) { int root_hash_load(const char *image, void **ret, size_t *ret_size) { _cleanup_free_ char *text = NULL; _cleanup_free_ void *k = NULL; - char *fn, *e, *n; size_t l; int r; @@ -1107,22 +1107,30 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) { return 0; } - fn = newa(char, strlen(image) + strlen(".roothash") + 1); - n = stpcpy(fn, image); - e = endswith(fn, ".raw"); - if (e) - n = e; + r = getxattr_malloc(image, "user.verity.roothash", &text, true); + if (r < 0) { + char *fn, *e, *n; - strcpy(n, ".roothash"); + if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) + return r; - r = read_one_line_file(fn, &text); - if (r == -ENOENT) { - *ret = NULL; - *ret_size = 0; - return 0; + 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 == -ENOENT) { + *ret = NULL; + *ret_size = 0; + return 0; + } + if (r < 0) + return r; } - if (r < 0) - return r; r = unhexmem(text, strlen(text), &k, &l); if (r < 0) -- cgit v1.2.3-54-g00ecf