]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn/dissect: automatically discover dm-verity verity partitions
authorLennart Poettering <lennart@poettering.net>
Wed, 7 Dec 2016 17:28:13 +0000 (18:28 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 7 Dec 2016 17:38:41 +0000 (18:38 +0100)
This adds support for discovering and making use of properly tagged dm-verity
data integrity partitions. This extends both systemd-nspawn and systemd-dissect
with a new --root-hash= switch that takes the root hash to use for the root
partition, and is otherwise fully automatic.

Verity partitions are discovered automatically by GPT table type UUIDs, as
listed in
https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/
(which I updated prior to this change, to include new UUIDs for this purpose.

mkosi with https://github.com/systemd/mkosi/pull/39 applied may generate images
that carry the necessary integrity data. With that PR and this commit, the
following simply lines suffice to boot up an integrity-protected container image:

```
 # mkdir test
 # cd test
 # mkosi --verity
 # systemd-nspawn -i ./image.raw -bn
```

Note that mkosi writes the image file to "image.raw" next to a a file
"image.roothash" that contains the root hash. systemd-nspawn will look for that
file and use it if it exists, in case --root-hash= is not specified explicitly.

src/dissect/dissect.c
src/machine/image-dbus.c
src/nspawn/nspawn.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/gpt.h
src/test/test-dissect-image.c

index 5e6848acb43d0b503b39652bea4c7b142b57eed7..e3c96b7407b08c413c84dbdd09fdd4e60c4dd09d 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "architecture.h"
 #include "dissect-image.h"
+#include "hexdecoct.h"
 #include "log.h"
 #include "loop-util.h"
 #include "string-util.h"
@@ -35,6 +36,8 @@ static enum {
 static const char *arg_image = NULL;
 static const char *arg_path = NULL;
 static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
 
 static void help(void) {
         printf("%s [OPTIONS...] IMAGE\n"
@@ -44,7 +47,8 @@ static void help(void) {
                "     --version         Show package version\n"
                "  -m --mount           Mount the image to the specified directory\n"
                "  -r --read-only       Mount read-only\n"
-               "     --discard=MODE    Choose 'discard' mode (disabled, loop, all, crypto)\n",
+               "     --discard=MODE    Choose 'discard' mode (disabled, loop, all, crypto)\n"
+               "     --root-hash=HASH  Specify root hash for verity\n",
                program_invocation_short_name,
                program_invocation_short_name);
 }
@@ -54,6 +58,7 @@ static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_DISCARD,
+                ARG_ROOT_HASH,
         };
 
         static const struct option options[] = {
@@ -62,10 +67,11 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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 },
                 {}
         };
 
-        int c;
+        int c, r;
 
         assert(argc >= 0);
         assert(argv);
@@ -105,6 +111,25 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_ROOT_HASH: {
+                        void *p;
+                        size_t l;
+
+                        r = unhexmem(optarg, strlen(optarg), &p, &l);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+                        if (l < sizeof(sd_id128_t)) {
+                                log_error("Root hash must be at least 128bit long: %s", optarg);
+                                free(p);
+                                return -EINVAL;
+                        }
+
+                        free(arg_root_hash);
+                        arg_root_hash = p;
+                        arg_root_hash_size = l;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
@@ -162,11 +187,15 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        r = dissect_image(d->fd, &m);
+        r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m);
         if (r == -ENOPKG) {
                 log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
                 goto finish;
         }
+        if (r == -EADDRNOTAVAIL) {
+                log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
+                goto finish;
+        }
         if (r < 0) {
                 log_error_errno(r, "Failed to dissect image: %m");
                 goto finish;
@@ -179,6 +208,7 @@ int main(int argc, char *argv[]) {
 
                 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
                         DissectedPartition *p = m->partitions + i;
+                        int k;
 
                         if (!p->found)
                                 continue;
@@ -193,6 +223,10 @@ int main(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 (p->partno >= 0)
                                 printf(" on partition #%i", p->partno);
 
@@ -206,7 +240,7 @@ int main(int argc, char *argv[]) {
         }
 
         case ACTION_MOUNT:
-                r = dissected_image_decrypt_interactively(m, NULL, arg_flags, &di);
+                r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
                 if (r < 0)
                         goto finish;
 
@@ -232,5 +266,6 @@ int main(int argc, char *argv[]) {
         }
 
 finish:
+        free(arg_root_hash);
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 65953b368fe35e2390014ccd797d5b41eb257452..e2fb8823933deab125bd3913bf94b5f884f540ca 100644 (file)
@@ -336,7 +336,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
         if (r < 0)
                 return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path);
 
-        r = dissect_image(d->fd, &m);
+        r = dissect_image(d->fd, NULL, 0, &m);
         if (r == -ENOPKG)
                 return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path);
         if (r < 0)
index 9168228f4a5551702dc6c13e24b06b9b20cb77f4..de05b6c5efd8195763c638ddf8879f8e64edc81f 100644 (file)
@@ -61,6 +61,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "gpt.h"
+#include "hexdecoct.h"
 #include "hostname-util.h"
 #include "id128-util.h"
 #include "log.h"
@@ -200,6 +201,8 @@ static bool arg_notify_ready = false;
 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;
+static void *arg_root_hash = NULL;
+static size_t arg_root_hash_size = 0;
 
 static void help(void) {
         printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -213,6 +216,7 @@ static void help(void) {
                "  -x --ephemeral            Run container with snapshot of root directory, and\n"
                "                            remove it after exit\n"
                "  -i --image=PATH           File system device or disk image for the container\n"
+               "     --root-hash=HASH       Specify verity root hash\n"
                "  -a --as-pid2              Maintain a stub init as PID1, invoke binary as PID2\n"
                "  -b --boot                 Boot up full system (i.e. invoke init)\n"
                "     --chdir=PATH           Set working directory in the container\n"
@@ -424,6 +428,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_CHDIR,
                 ARG_PRIVATE_USERS_CHOWN,
                 ARG_NOTIFY_READY,
+                ARG_ROOT_HASH,
         };
 
         static const struct option options[] = {
@@ -473,6 +478,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "settings",              required_argument, NULL, ARG_SETTINGS            },
                 { "chdir",                 required_argument, NULL, ARG_CHDIR               },
                 { "notify-ready",          required_argument, NULL, ARG_NOTIFY_READY        },
+                { "root-hash",             required_argument, NULL, ARG_ROOT_HASH           },
                 {}
         };
 
@@ -1016,6 +1022,25 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_settings_mask |= SETTING_NOTIFY_READY;
                         break;
 
+                case ARG_ROOT_HASH: {
+                        void *k;
+                        size_t l;
+
+                        r = unhexmem(optarg, strlen(optarg), &k, &l);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse root hash: %s", optarg);
+                        if (l < sizeof(sd_id128_t)) {
+                                log_error("Root hash must be at least 128bit long: %s", optarg);
+                                free(k);
+                                return -EINVAL;
+                        }
+
+                        free(arg_root_hash);
+                        arg_root_hash = k;
+                        arg_root_hash_size = l;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
@@ -3409,6 +3434,53 @@ static int run(int master,
         return 1; /* loop again */
 }
 
+static int load_root_hash(const char *image) {
+        _cleanup_free_ char *text = NULL;
+        char *fn, *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;
@@ -3623,6 +3695,10 @@ int main(int argc, char *argv[]) {
                                 r = log_error_errno(r, "Failed to create image lock: %m");
                                 goto finish;
                         }
+
+                        r = load_root_hash(arg_image);
+                        if (r < 0)
+                                goto finish;
                 }
 
                 if (!mkdtemp(tmprootdir)) {
@@ -3644,7 +3720,7 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                r = dissect_image(loop->fd, &dissected_image);
+                r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image);
                 if (r == -ENOPKG) {
                         log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image);
 
@@ -3656,6 +3732,10 @@ int main(int argc, char *argv[]) {
                                    "in order to be bootable with systemd-nspawn.");
                         goto finish;
                 }
+                if (r == -EADDRNOTAVAIL) {
+                        log_error_errno(r, "No root partition for specified root hash found.");
+                        goto finish;
+                }
                 if (r == -EOPNOTSUPP) {
                         log_error_errno(r, "--image= is not supported, compiled without blkid support.");
                         goto finish;
@@ -3665,7 +3745,10 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                r = dissected_image_decrypt_interactively(dissected_image, NULL, 0, &decrypted_image);
+                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);
                 if (r < 0)
                         goto finish;
 
@@ -3792,6 +3875,7 @@ finish:
         strv_free(arg_parameters);
         custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
         expose_port_free_all(arg_expose_ports);
+        free(arg_root_hash);
 
         return r < 0 ? EXIT_FAILURE : ret;
 }
index bc4e45be6ed10801277671234b2840c5eea2757c..257af787817f658dfc91a7b5ff12cdb1596a3a23 100644 (file)
@@ -84,9 +84,10 @@ not_found:
 #endif
 }
 
-int dissect_image(int fd, DissectedImage **ret) {
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
 
 #ifdef HAVE_BLKID
+        sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
         bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
@@ -103,10 +104,29 @@ int dissect_image(int fd, DissectedImage **ret) {
 
         assert(fd >= 0);
         assert(ret);
+        assert(root_hash || root_hash_size == 0);
 
         /* Probes a disk image, and returns information about what it found in *ret.
          *
-         * Returns -ENOPKG if no suitable partition table or file system could be found. */
+         * Returns -ENOPKG if no suitable partition table or file system could be found.
+         * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
+
+        if (root_hash) {
+                /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
+                 * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
+                 * 128bit. */
+
+                if (root_hash_size < sizeof(sd_id128_t))
+                        return -EINVAL;
+
+                memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
+                memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
+
+                if (sd_id128_is_null(root_uuid))
+                        return -EINVAL;
+                if (sd_id128_is_null(verity_uuid))
+                        return -EINVAL;
+        }
 
         if (fstat(fd, &st) < 0)
                 return -errno;
@@ -313,17 +333,22 @@ int dissect_image(int fd, DissectedImage **ret) {
 
                 if (is_gpt) {
                         int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
-                        const char *stype, *fstype = NULL;
-                        sd_id128_t type_id;
+                        const char *stype, *sid, *fstype = NULL;
+                        sd_id128_t type_id, id;
                         bool rw = true;
 
                         if (flags & GPT_FLAG_NO_AUTO)
                                 continue;
 
+                        sid = blkid_partition_get_uuid(pp);
+                        if (!sid)
+                                continue;
+                        if (sd_id128_from_string(sid, &id) < 0)
+                                continue;
+
                         stype = blkid_partition_get_type_string(pp);
                         if (!stype)
                                 continue;
-
                         if (sd_id128_from_string(stype, &type_id) < 0)
                                 continue;
 
@@ -339,17 +364,57 @@ int dissect_image(int fd, DissectedImage **ret) {
                         }
 #ifdef GPT_ROOT_NATIVE
                         else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+
+                                /* If a root ID is specified, ignore everything but the root id */
+                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+                                        continue;
+
                                 designator = PARTITION_ROOT;
                                 architecture = native_architecture();
                                 rw = !(flags & GPT_FLAG_READ_ONLY);
                         }
+#ifdef GPT_ROOT_NATIVE_VERITY
+                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless a root hash is specified */
+                                if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = native_architecture();
+                                rw = false;
+                        }
+#endif
 #endif
 #ifdef GPT_ROOT_SECONDARY
                         else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+
+                                /* If a root ID is specified, ignore everything but the root id */
+                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
+                                        continue;
+
                                 designator = PARTITION_ROOT_SECONDARY;
                                 architecture = SECONDARY_ARCHITECTURE;
                                 rw = !(flags & GPT_FLAG_READ_ONLY);
                         }
+#ifdef GPT_ROOT_SECONDARY_VERITY
+                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless root has is specified */
+                                if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_ROOT_SECONDARY_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = SECONDARY_ARCHITECTURE;
+                                rw = false;
+                        }
+#endif
 #endif
                         else if (sd_id128_equal(type_id, GPT_SWAP)) {
                                 designator = PARTITION_SWAP;
@@ -420,10 +485,17 @@ int dissect_image(int fd, DissectedImage **ret) {
                 /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
                  * either, then check if there's a single generic one, and use that. */
 
+                if (m->partitions[PARTITION_ROOT_VERITY].found)
+                        return -ENXIO;
+
                 if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
                         m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
                         zero(m->partitions[PARTITION_ROOT_SECONDARY]);
-                } else if (generic_node) {
+
+                        m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
+                        zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
+
+                } else if (generic_node && !root_hash) {
 
                         if (multiple_generic)
                                 return -ENOTUNIQ;
@@ -441,6 +513,24 @@ int dissect_image(int fd, DissectedImage **ret) {
                         return -ENXIO;
         }
 
+        assert(m->partitions[PARTITION_ROOT].found);
+
+        if (root_hash) {
+                if (!m->partitions[PARTITION_ROOT_VERITY].found)
+                        return -EADDRNOTAVAIL;
+
+                /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
+                 * (which would be weird, after all the root hash should only be assigned to one pair of
+                 * partitions... */
+                m->partitions[PARTITION_ROOT_SECONDARY].found = false;
+                m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
+
+                /* If we found a verity setup, then the root partition is necessarily read-only. */
+                m->partitions[PARTITION_ROOT].rw = false;
+
+                m->verity = true;
+        }
+
         blkid_free_probe(b);
         b = NULL;
 
@@ -637,6 +727,40 @@ DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
 }
 
 #ifdef HAVE_LIBCRYPTSETUP
+
+static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
+        _cleanup_free_ char *name = NULL, *node = NULL;
+        const char *base;
+
+        assert(original_node);
+        assert(suffix);
+        assert(ret_name);
+        assert(ret_node);
+
+        base = strrchr(original_node, '/');
+        if (!base)
+                return -EINVAL;
+        base++;
+        if (isempty(base))
+                return -EINVAL;
+
+        name = strjoin(base, suffix);
+        if (!name)
+                return -ENOMEM;
+        if (!filename_is_valid(name))
+                return -EINVAL;
+
+        node = strjoin(crypt_get_dir(), "/", name);
+        if (!node)
+                return -ENOMEM;
+
+        *ret_name = name;
+        *ret_node = node;
+
+        name = node = NULL;
+        return 0;
+}
+
 static int decrypt_partition(
                 DissectedPartition *m,
                 const char *passphrase,
@@ -645,7 +769,6 @@ static int decrypt_partition(
 
         _cleanup_free_ char *node = NULL, *name = NULL;
         struct crypt_device *cd;
-        const char *suffix;
         int r;
 
         assert(m);
@@ -657,22 +780,9 @@ static int decrypt_partition(
         if (!streq(m->fstype, "crypto_LUKS"))
                 return 0;
 
-        suffix = strrchr(m->node, '/');
-        if (!suffix)
-                return -EINVAL;
-        suffix++;
-        if (isempty(suffix))
-                return -EINVAL;
-
-        name = strjoin(suffix, "-decrypted");
-        if (!name)
-                return -ENOMEM;
-        if (!filename_is_valid(name))
-                return -EINVAL;
-
-        node = strjoin(crypt_get_dir(), "/", name);
-        if (!node)
-                return -ENOMEM;
+        r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
+        if (r < 0)
+                return r;
 
         if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
                 return -ENOMEM;
@@ -706,6 +816,71 @@ static int decrypt_partition(
 
         return 0;
 
+fail:
+        crypt_free(cd);
+        return r;
+}
+
+static int verity_partition(
+                DissectedPartition *m,
+                DissectedPartition *v,
+                const void *root_hash,
+                size_t root_hash_size,
+                DissectImageFlags flags,
+                DecryptedImage *d) {
+
+        _cleanup_free_ char *node = NULL, *name = NULL;
+        struct crypt_device *cd;
+        int r;
+
+        assert(m);
+        assert(v);
+
+        if (!root_hash)
+                return 0;
+
+        if (!m->found || !m->node || !m->fstype)
+                return 0;
+        if (!v->found || !v->node || !v->fstype)
+                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)
+                return r;
+
+        if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+                return -ENOMEM;
+
+        r = crypt_init(&cd, v->node);
+        if (r < 0)
+                return r;
+
+        r = crypt_load(cd, CRYPT_VERITY, NULL);
+        if (r < 0)
+                goto fail;
+
+        r = crypt_set_data_device(cd, m->node);
+        if (r < 0)
+                goto fail;
+
+        r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
+        if (r < 0)
+                goto fail;
+
+        d->decrypted[d->n_decrypted].name = name;
+        name = NULL;
+
+        d->decrypted[d->n_decrypted].device = cd;
+        d->n_decrypted++;
+
+        m->decrypted_node = node;
+        node = NULL;
+
+        return 0;
+
 fail:
         crypt_free(cd);
         return r;
@@ -715,6 +890,8 @@ fail:
 int dissected_image_decrypt(
                 DissectedImage *m,
                 const char *passphrase,
+                const void *root_hash,
+                size_t root_hash_size,
                 DissectImageFlags flags,
                 DecryptedImage **ret) {
 
@@ -725,6 +902,7 @@ int dissected_image_decrypt(
 #endif
 
         assert(m);
+        assert(root_hash || root_hash_size == 0);
 
         /* Returns:
          *
@@ -734,13 +912,16 @@ int dissected_image_decrypt(
          *      -EKEYREJECTED → Passed key was not correct
          */
 
-        if (!m->encrypted) {
+        if (root_hash && root_hash_size < sizeof(sd_id128_t))
+                return -EINVAL;
+
+        if (!m->encrypted && !m->verity) {
                 *ret = NULL;
                 return 0;
         }
 
 #ifdef HAVE_LIBCRYPTSETUP
-        if (!passphrase)
+        if (m->encrypted && !passphrase)
                 return -ENOKEY;
 
         d = new0(DecryptedImage, 1);
@@ -749,6 +930,7 @@ int dissected_image_decrypt(
 
         for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
                 DissectedPartition *p = m->partitions + i;
+                int k;
 
                 if (!p->found)
                         continue;
@@ -757,6 +939,13 @@ int dissected_image_decrypt(
                 if (r < 0)
                         return r;
 
+                k = PARTITION_VERITY_OF(i);
+                if (k >= 0) {
+                        r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (!p->decrypted_fstype && p->decrypted_node) {
                         r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
                         if (r < 0)
@@ -776,6 +965,8 @@ int dissected_image_decrypt(
 int dissected_image_decrypt_interactively(
                 DissectedImage *m,
                 const char *passphrase,
+                const void *root_hash,
+                size_t root_hash_size,
                 DissectImageFlags flags,
                 DecryptedImage **ret) {
 
@@ -786,7 +977,7 @@ int dissected_image_decrypt_interactively(
                 n--;
 
         for (;;) {
-                r = dissected_image_decrypt(m, passphrase, flags, ret);
+                r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
                 if (r >= 0)
                         return r;
                 if (r == -EKEYREJECTED)
@@ -880,6 +1071,8 @@ static const char *const partition_designator_table[] = {
         [PARTITION_SRV] = "srv",
         [PARTITION_ESP] = "esp",
         [PARTITION_SWAP] = "swap",
+        [PARTITION_ROOT_VERITY] = "root-verity",
+        [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
index 69484eb32cb97c204bdd45e42e56a17bac36f710..902c8d4a3774f52dbdafc09dfe2b5490eb88d4da 100644 (file)
@@ -45,10 +45,20 @@ enum  {
         PARTITION_SRV,
         PARTITION_ESP,
         PARTITION_SWAP,
+        PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
+        PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
         _PARTITION_DESIGNATOR_MAX,
         _PARTITION_DESIGNATOR_INVALID = -1
 };
 
+static inline int PARTITION_VERITY_OF(int p) {
+        if (p == PARTITION_ROOT)
+                return PARTITION_ROOT_VERITY;
+        if (p == PARTITION_ROOT_SECONDARY)
+                return PARTITION_ROOT_SECONDARY_VERITY;
+        return _PARTITION_DESIGNATOR_INVALID;
+}
+
 typedef enum DissectImageFlags {
         DISSECT_IMAGE_READ_ONLY = 1,
         DISSECT_IMAGE_DISCARD_ON_LOOP = 2,   /* Turn on "discard" if on loop device and file system supports it */
@@ -57,17 +67,19 @@ typedef enum DissectImageFlags {
 } DissectImageFlags;
 
 struct DissectedImage {
-        bool encrypted;
+        bool encrypted:1;
+        bool verity:1;     /* verity available and usable */
+        bool can_verity:1; /* verity available, but not necessarily used */
         DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
 };
 
-int dissect_image(int fd, DissectedImage **ret);
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, 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, DissectImageFlags flags, DecryptedImage **ret);
-int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, DissectImageFlags flags, DecryptedImage **ret);
+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_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
 
 DecryptedImage* decrypted_image_unref(DecryptedImage *p);
index 55b41bbcd83babdb8bcf64911f324339788b4f63..13d80d611c3aab9b0a3b8d5b72f2f1795515c57e 100644 (file)
 #define GPT_ROOT_ARM    SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
 #define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
 #define GPT_ROOT_IA64   SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
-
 #define GPT_ESP         SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
 #define GPT_SWAP        SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
 #define GPT_HOME        SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
 #define GPT_SRV         SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
 
+/* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
+ * are commonly read-only and hence suitable for verity). */
+#define GPT_ROOT_X86_VERITY    SD_ID128_MAKE(d1,3c,5d,3b,b5,d1,42,2a,b2,9f,94,54,fd,c8,9d,76)
+#define GPT_ROOT_X86_64_VERITY SD_ID128_MAKE(2c,73,57,ed,eb,d2,46,d9,ae,c1,23,d4,37,ec,2b,f5)
+#define GPT_ROOT_ARM_VERITY    SD_ID128_MAKE(73,86,cd,f2,20,3c,47,a9,a4,98,f2,ec,ce,45,a2,d6)
+#define GPT_ROOT_ARM_64_VERITY SD_ID128_MAKE(df,33,00,ce,d6,9f,4c,92,97,8c,9b,fb,0f,38,d8,20)
+#define GPT_ROOT_IA64_VERITY   SD_ID128_MAKE(86,ed,10,d5,b6,07,45,bb,89,57,d3,50,f2,3d,05,71)
+
+
 #if defined(__x86_64__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_X86_64
 #  define GPT_ROOT_SECONDARY GPT_ROOT_X86
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_64_VERITY
+#  define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_X86_VERITY
 #elif defined(__i386__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_X86
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_VERITY
 #endif
 
 #if defined(__ia64__)
 #  define GPT_ROOT_NATIVE GPT_ROOT_IA64
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_IA64_VERITY
 #endif
 
 #if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
 #  define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
 #  define GPT_ROOT_SECONDARY GPT_ROOT_ARM
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_64_VERITY
+#  define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_ARM_VERITY
 #elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN)
 #  define GPT_ROOT_NATIVE GPT_ROOT_ARM
+#  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
 #endif
 
 /* Flags we recognize on the root, swap, home and srv partitions when
index 0363ef8eb6a0e19948124730b7dbbe104b350604..0512a15e88c9c92b333ccfe2a00fecc78bd164cb 100644 (file)
@@ -43,7 +43,7 @@ int main(int argc, char *argv[]) {
                 return EXIT_FAILURE;
         }
 
-        r = dissect_image(d->fd, &m);
+        r = dissect_image(d->fd, NULL, 0, &m);
         if (r < 0) {
                 log_error_errno(r, "Failed to dissect image: %m");
                 return EXIT_FAILURE;