]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #16046 from bluca/dissect_squashfs_verity
authorLennart Poettering <lennart@poettering.net>
Tue, 9 Jun 2020 17:52:21 +0000 (19:52 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Jun 2020 17:52:21 +0000 (19:52 +0200)
dissect: single-filesystem verity images with external hashdevice

14 files changed:
man/systemd-nspawn.xml
src/core/namespace.c
src/dissect/dissect.c
src/gpt-auto-generator/gpt-auto-generator.c
src/nspawn/nspawn.c
src/portable/portable.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/machine-image.c
src/test/test-dissect-image.c
test/TEST-50-DISSECT/Makefile [new symlink]
test/TEST-50-DISSECT/test.sh [new file with mode: 0755]
test/units/testsuite-50.service [new file with mode: 0644]
test/units/testsuite-50.sh [new file with mode: 0755]

index f9cc5a8828f260924d47d9f30340e1a3288fae11..72d2f1e4bae65dedc1332a2aeddd97d97befd837 100644 (file)
         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>
index 34eb469fb825990d84a2c704fd451cea13dc4e02..b02274b9a6c9fbb5413bef5365ddc3be2e8ed75d 100644 (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");
         }
index e1418419f7b628b221075440ce4bac8cf1c970e6..9ae632f226700fa62bfcbcbd2b23d9abed3b3f96 100644 (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;
 
index 2067eeaf89afb403e7f4a3e7d50485da3eb15a39..a9478b9dbd886d2ce3dd96ccf76775e0747d9df3 100644 (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;
index 8e8881749aa54f56cf21936c8767a80556b65f9b..9a9ddecabbd3191025e989dedad4ac07116a97d2 100644 (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;
 
index a6aaa14ab6a1c59ab602b4594ba24f3f783f1e78..7917ea3902e860d73f4737d2ce5628913d1552cd 100644 (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)
index 8decac94b2238fd74011a57a66ae1357aa4c77f9..2c8a5d85bfec01ff228060ccabd08d490cc639b5 100644 (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),
@@ -424,11 +431,11 @@ int dissect_image(
 
                         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;
@@ -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 (!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;
 }
@@ -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",
index 6a666ca7c70bec70eaf39666b3705fdc10816040..92d223cfec5708a4a3e811512d92dbf22b397ff6 100644 (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);
index b45efcd1e6657d812b2953d76cfe41205dcbaf2f..1b7cfb5028adf763b2724dc9733c69e4774f24ee 100644 (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;
 
index a1ccf605b11c6580dcea23903da1badf4d41a37e..13ff8add5da3e970de3d0d0458b7e249e2722be1 100644 (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;
diff --git a/test/TEST-50-DISSECT/Makefile b/test/TEST-50-DISSECT/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh
new file mode 100755 (executable)
index 0000000..cfe97aa
--- /dev/null
@@ -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
diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service
new file mode 100644 (file)
index 0000000..5a10a64
--- /dev/null
@@ -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
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
new file mode 100755 (executable)
index 0000000..9f35ffd
--- /dev/null
@@ -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