]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: process /usr/ GPT partition type
authorLennart Poettering <lennart@poettering.net>
Sat, 22 Aug 2020 10:21:51 +0000 (12:21 +0200)
committerLennart Poettering <lennart@poettering.net>
Sat, 19 Sep 2020 19:19:51 +0000 (21:19 +0200)
src/core/namespace.c
src/dissect/dissect.c
src/nspawn/nspawn.c
src/shared/dissect-image.c
src/shared/dissect-image.h

index 8932785239d02aabac358ffd56544f0f07f0547a..f2754f9d0202baadacc37c999748c0363a5390fe 100644 (file)
@@ -942,7 +942,7 @@ static int mount_images(const MountEntry *m) {
         _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_(verity_settings_done) VeritySettings verity = {};
+        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
         DissectImageFlags dissect_image_flags;
         int r;
 
@@ -1417,6 +1417,7 @@ static int verity_settings_prepare(
 
                 free_and_replace(verity->root_hash, d);
                 verity->root_hash_size = root_hash_size;
+                verity->designator = PARTITION_ROOT;
         }
 
         if (root_hash_sig) {
@@ -1428,6 +1429,7 @@ static int verity_settings_prepare(
 
                 free_and_replace(verity->root_hash_sig, d);
                 verity->root_hash_sig_size = root_hash_sig_size;
+                verity->designator = PARTITION_ROOT;
         }
 
         if (verity_data_path) {
@@ -1480,7 +1482,7 @@ int setup_namespace(
         _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_(verity_settings_done) VeritySettings verity = {};
+        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
         MountEntry *m = NULL, *mounts = NULL;
         bool require_prefix = false;
         const char *root;
index dd80e86feeeadcbf220c702a9f7660ec56480a1d..237395a4444c2f93faa45840e48e4211e3018ebd 100644 (file)
@@ -44,7 +44,7 @@ static const char *arg_path = NULL;
 static const char *arg_source = NULL;
 static const char *arg_target = NULL;
 static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
-static VeritySettings arg_verity_settings = {};
+static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
 static bool arg_json = false;
 static JsonFormatFlags arg_json_format_flags = 0;
 
index 11a82090b0980b2e662a138c3e08a1a262a9c6d3..42ba0f5e47c78f2cf96d44886539490e98782b06 100644 (file)
@@ -201,7 +201,7 @@ 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|MOUNT_APPLY_TMPFS_TMP;
-static VeritySettings arg_verity_settings = {};
+static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
 static char **arg_syscall_allow_list = NULL;
 static char **arg_syscall_deny_list = NULL;
 #if HAVE_SECCOMP
index b704268db2423e71e765360b9e8aab6085fdd2d8..e24740d44937bd00073f22b01f4e58101688252d 100644 (file)
@@ -310,7 +310,8 @@ int dissect_image(
                 DissectedImage **ret) {
 
 #if HAVE_BLKID
-        sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
+        sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL,
+                usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
         bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
@@ -335,20 +336,32 @@ int dissect_image(
          * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
 
         if (verity && verity->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. */
+                sd_id128_t fsuuid, vuuid;
+
+                /* 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 (verity->root_hash_size < sizeof(sd_id128_t))
                         return -EINVAL;
 
-                memcpy(&root_uuid, verity->root_hash, sizeof(sd_id128_t));
-                memcpy(&verity_uuid, (const uint8_t*) verity->root_hash + verity->root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
+                memcpy(&fsuuid, verity->root_hash, sizeof(sd_id128_t));
+                memcpy(&vuuid, (const uint8_t*) verity->root_hash + verity->root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
 
-                if (sd_id128_is_null(root_uuid))
+                if (sd_id128_is_null(fsuuid))
                         return -EINVAL;
-                if (sd_id128_is_null(verity_uuid))
+                if (sd_id128_is_null(vuuid))
                         return -EINVAL;
+
+                /* If the verity data declares it's for the /usr partition, then search for that, in all
+                 * other cases assume it's for the root partition. */
+                if (verity->designator == PARTITION_USR) {
+                        usr_uuid = fsuuid;
+                        usr_verity_uuid = vuuid;
+                } else {
+                        root_uuid = fsuuid;
+                        root_verity_uuid = vuuid;
+                }
         }
 
         if (fstat(fd, &st) < 0)
@@ -395,6 +408,8 @@ int dissect_image(
             (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
                 const char *usage = NULL;
 
+                /* If flags permit this, also allow using non-partitioned single-filesystem images */
+
                 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
                 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
                         _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
@@ -414,7 +429,7 @@ int dissect_image(
                                 return r;
 
                         m->single_file_system = true;
-                        m->verity = verity && verity->root_hash && verity->data_path;
+                        m->verity = verity && verity->root_hash && verity->data_path && (verity->designator < 0 || verity->designator == PARTITION_ROOT);
                         m->can_verity = verity && verity->data_path;
 
                         options = mount_options_from_designator(mount_options, PARTITION_ROOT);
@@ -528,6 +543,7 @@ int dissect_image(
 
                                 designator = PARTITION_HOME;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
+
                         } else if (sd_id128_equal(type_id, GPT_SRV)) {
 
                                 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
@@ -537,11 +553,13 @@ int dissect_image(
 
                                 designator = PARTITION_SRV;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
+
                         } else if (sd_id128_equal(type_id, GPT_ESP)) {
 
-                                /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined
-                                 * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the
-                                 * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */
+                                /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is
+                                 * not defined there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
+                                 * recommended by the UEFI spec (See "12.3.3 Number and Location of System
+                                 * Partitions"). */
 
                                 if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
                                         continue;
@@ -574,6 +592,7 @@ int dissect_image(
                                 designator = PARTITION_ROOT;
                                 architecture = native_architecture();
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
+
                         } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
 
                                 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
@@ -584,7 +603,7 @@ int dissect_image(
                                 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))
+                                if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
                                         continue;
 
                                 designator = PARTITION_ROOT_VERITY;
@@ -608,6 +627,7 @@ int dissect_image(
                                 designator = PARTITION_ROOT_SECONDARY;
                                 architecture = SECONDARY_ARCHITECTURE;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
+
                         } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
 
                                 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
@@ -618,7 +638,7 @@ int dissect_image(
                                 m->can_verity = true;
 
                                 /* Ignore verity unless root has is specified */
-                                if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
+                                if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
                                         continue;
 
                                 designator = PARTITION_ROOT_SECONDARY_VERITY;
@@ -626,6 +646,76 @@ int dissect_image(
                                 architecture = SECONDARY_ARCHITECTURE;
                                 rw = false;
                         }
+#endif
+#ifdef GPT_USR_NATIVE
+                        else if (sd_id128_equal(type_id, GPT_USR_NATIVE)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                /* If a usr ID is specified, ignore everything but the usr id */
+                                if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_USR;
+                                architecture = native_architecture();
+                                rw = !(pflags & GPT_FLAG_READ_ONLY);
+
+                        } else if (sd_id128_equal(type_id, GPT_USR_NATIVE_VERITY)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless a usr hash is specified */
+                                if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_USR_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = native_architecture();
+                                rw = false;
+                        }
+#endif
+#ifdef GPT_USR_SECONDARY
+                        else if (sd_id128_equal(type_id, GPT_USR_SECONDARY)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                /* If a usr ID is specified, ignore everything but the usr id */
+                                if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_USR_SECONDARY;
+                                architecture = SECONDARY_ARCHITECTURE;
+                                rw = !(pflags & GPT_FLAG_READ_ONLY);
+
+                        } else if (sd_id128_equal(type_id, GPT_USR_SECONDARY_VERITY)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                m->can_verity = true;
+
+                                /* Ignore verity unless usr has is specified */
+                                if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
+                                        continue;
+
+                                designator = PARTITION_USR_SECONDARY_VERITY;
+                                fstype = "DM_verity_hash";
+                                architecture = SECONDARY_ARCHITECTURE;
+                                rw = false;
+                        }
 #endif
                         else if (sd_id128_equal(type_id, GPT_SWAP)) {
 
@@ -636,6 +726,7 @@ int dissect_image(
 
                                 designator = PARTITION_SWAP;
                                 fstype = "swap";
+
                         } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
 
                                 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
@@ -798,6 +889,8 @@ int dissect_image(
                  * since we never want to mount the secondary arch in this case. */
                 m->partitions[PARTITION_ROOT_SECONDARY].found = false;
                 m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
+                m->partitions[PARTITION_USR_SECONDARY].found = false;
+                m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
         } else {
                 /* 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. */
@@ -805,13 +898,22 @@ int dissect_image(
                 if (m->partitions[PARTITION_ROOT_VERITY].found)
                         return -EADDRNOTAVAIL;
 
+                /* We didn't find a primary architecture root, but we found a primary architecture /usr? Refuse that for now. */
+                if (m->partitions[PARTITION_USR].found || m->partitions[PARTITION_USR_VERITY].found)
+                        return -EADDRNOTAVAIL;
+
                 if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
+                        /* Upgrade secondary arch to first */
                         m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
                         zero(m->partitions[PARTITION_ROOT_SECONDARY]);
-
                         m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
                         zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
 
+                        m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
+                        zero(m->partitions[PARTITION_USR_SECONDARY]);
+                        m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
+                        zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
+
                 } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
                         _cleanup_free_ char *o = NULL;
                         const char *options = NULL;
@@ -849,14 +951,31 @@ int dissect_image(
                 }
         }
 
+        /* Refuse if we found a verity partition for /usr but no matching file system partition */
+        if (!m->partitions[PARTITION_USR].found && m->partitions[PARTITION_USR_VERITY].found)
+                return -EADDRNOTAVAIL;
+
+        /* Combinations of verity /usr with verity-less root is OK, but the reverse is not */
+        if (m->partitions[PARTITION_ROOT_VERITY].found && !m->partitions[PARTITION_USR_VERITY].found)
+                return -EADDRNOTAVAIL;
+
         if (verity && verity->root_hash) {
-                if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
-                        return -EADDRNOTAVAIL;
+                if (verity->designator < 0 || verity->designator == PARTITION_ROOT) {
+                        if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
+                                return -EADDRNOTAVAIL;
+
+                        /* If we found a verity setup, then the root partition is necessarily read-only. */
+                        m->partitions[PARTITION_ROOT].rw = false;
+                        m->verity = true;
+                }
 
-                /* If we found a verity setup, then the root partition is necessarily read-only. */
-                m->partitions[PARTITION_ROOT].rw = false;
+                if (verity->designator == PARTITION_USR) {
+                        if (!m->partitions[PARTITION_USR_VERITY].found || !m->partitions[PARTITION_USR].found)
+                                return -EADDRNOTAVAIL;
 
-                m->verity = true;
+                        m->partitions[PARTITION_USR].rw = false;
+                        m->verity = true;
+                }
         }
 
         blkid_free_probe(b);
@@ -883,7 +1002,6 @@ int dissect_image(
         }
 
         *ret = TAKE_PTR(m);
-
         return 0;
 #else
         return -EOPNOTSUPP;
@@ -1089,6 +1207,17 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
                 r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
                 if (r < 0)
                         return r;
+        }
+
+        /* Mask DISSECT_IMAGE_MKDIR for all subdirs: the idea is that only the top-level mount point is
+         * created if needed, but the image itself not modified. */
+        flags &= ~DISSECT_IMAGE_MKDIR;
+
+        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
+                /* For us mounting root always means mounting /usr as well */
+                r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, flags);
+                if (r < 0)
+                        return r;
 
                 if (flags & DISSECT_IMAGE_VALIDATE_OS) {
                         r = path_is_os_tree(where);
@@ -1102,10 +1231,6 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
         if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
                 return 0;
 
-        /* Mask DISSECT_IMAGE_MKDIR for all subdirs: the idea is that only the top-level mount point is
-         * created if needed, but the image itself not modified. */
-        flags &= ~DISSECT_IMAGE_MKDIR;
-
         r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
         if (r < 0)
                 return r;
@@ -1389,6 +1514,7 @@ static inline void dm_deferred_remove_clean(char *name) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
 
 static int verity_partition(
+                PartitionDesignator designator,
                 DissectedPartition *m,
                 DissectedPartition *v,
                 const VeritySettings *verity,
@@ -1405,6 +1531,9 @@ static int verity_partition(
 
         if (!verity || !verity->root_hash)
                 return 0;
+        if (!((verity->designator < 0 && designator == PARTITION_ROOT) ||
+              (verity->designator == designator)))
+                return 0;
 
         if (!m->found || !m->node || !m->fstype)
                 return 0;
@@ -1426,8 +1555,8 @@ static int verity_partition(
 
                 root_hash_encoded = hexmem(verity->root_hash, verity->root_hash_size);
                 if (!root_hash_encoded)
-
                         return -ENOMEM;
+
                 r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
         } else
                 r = make_dm_name_and_node(m->node, "-verity", &name, &node);
@@ -1482,7 +1611,7 @@ static int verity_partition(
                  * Improvements in libcrypsetup can ensure this never happens:
                  * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
                 if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
-                        return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+                        return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
                 if (!IN_SET(r,
                             0,       /* Success */
                             -EEXIST, /* Volume is already open and ready to be used */
@@ -1508,7 +1637,7 @@ static int verity_partition(
                         r = verity_can_reuse(verity, name, &existing_cd);
                         /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
                         if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
-                                return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+                                return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
                         if (!IN_SET(r, 0, -ENODEV, -ENOENT, -EBUSY))
                                 return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node);
                         if (r == 0) {
@@ -1536,7 +1665,7 @@ static int verity_partition(
         /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time.
          * Fall back to activating it with a unique device name. */
         if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
-                return verity_partition(m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+                return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
 
         /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
         restore_deferred_remove = mfree(restore_deferred_remove);
@@ -1601,7 +1730,7 @@ int dissected_image_decrypt(
 
                 k = PARTITION_VERITY_OF(i);
                 if (k >= 0) {
-                        r = verity_partition(p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
+                        r = verity_partition(i, p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
                         if (r < 0)
                                 return r;
                 }
@@ -1727,39 +1856,84 @@ int verity_settings_load(
         _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
         size_t root_hash_size = 0, root_hash_sig_size = 0;
         _cleanup_free_ char *verity_data_path = NULL;
+        PartitionDesignator designator;
         int r;
 
         assert(verity);
         assert(image);
+        assert(verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
 
         /* If we are asked to load the root hash for a device node, exit early */
         if (is_device_path(image))
                 return 0;
 
+        designator = verity->designator;
+
         /* We only fill in what isn't already filled in */
 
         if (!verity->root_hash) {
                 _cleanup_free_ char *text = NULL;
 
                 if (root_hash_path) {
+                        /* If explicitly specified it takes precedence */
                         r = read_one_line_file(root_hash_path, &text);
                         if (r < 0)
                                 return r;
+
+                        if (designator < 0)
+                                designator = PARTITION_ROOT;
                 } else {
-                        r = getxattr_malloc(image, "user.verity.roothash", &text, true);
-                        if (r < 0) {
-                                _cleanup_free_ char *p = NULL;
+                        /* Otherwise look for xattr and separate file, and first for the data for root and if
+                         * that doesn't exist for /usr */
 
-                                if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
-                                        return r;
+                        if (designator < 0 || designator == PARTITION_ROOT) {
+                                r = getxattr_malloc(image, "user.verity.roothash", &text, true);
+                                if (r < 0) {
+                                        _cleanup_free_ char *p = NULL;
 
-                                p = build_auxiliary_path(image, ".roothash");
-                                if (!p)
-                                        return -ENOMEM;
+                                        if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
+                                                return r;
 
-                                r = read_one_line_file(p, &text);
-                                if (r < 0 && r != -ENOENT)
-                                        return r;
+                                        p = build_auxiliary_path(image, ".roothash");
+                                        if (!p)
+                                                return -ENOMEM;
+
+                                        r = read_one_line_file(p, &text);
+                                        if (r < 0 && r != -ENOENT)
+                                                return r;
+                                }
+
+                                if (text)
+                                        designator = PARTITION_ROOT;
+                        }
+
+                        if (!text && (designator < 0 || designator == PARTITION_USR)) {
+                                /* So in the "roothash" xattr/file name above the "root" of course primarily
+                                 * refers to the root of the Verity Merkle tree. But coincidentally it also
+                                 * is the hash for the *root* file system, i.e. the "root" neatly refers to
+                                 * two distinct concepts called "root". Taking benefit of this happy
+                                 * coincidence we call the file with the root hash for the /usr/ file system
+                                 * `usrhash`, because `usrroothash` or `rootusrhash` would just be too
+                                 * confusing. We thus drop the reference to the root of the Merkle tree, and
+                                 * just indicate which file system it's about. */
+                                r = getxattr_malloc(image, "user.verity.usrhash", &text, true);
+                                if (r < 0) {
+                                        _cleanup_free_ char *p = NULL;
+
+                                        if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
+                                                return r;
+
+                                        p = build_auxiliary_path(image, ".usrhash");
+                                        if (!p)
+                                                return -ENOMEM;
+
+                                        r = read_one_line_file(p, &text);
+                                        if (r < 0 && r != -ENOENT)
+                                                return r;
+                                }
+
+                                if (text)
+                                        designator = PARTITION_USR;
                         }
                 }
 
@@ -1772,24 +1946,47 @@ int verity_settings_load(
                 }
         }
 
-        if (!verity->root_hash_sig) {
-                _cleanup_free_ char *p = NULL;
+        if (verity->root_hash && !verity->root_hash_sig) {
+                if (root_hash_sig_path) {
+                        r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, (char**) &root_hash_sig, &root_hash_sig_size);
+                        if (r < 0 && r != -ENOENT)
+                                return r;
+
+                        if (designator < 0)
+                                designator = PARTITION_ROOT;
+                } else {
+                        if (designator < 0 || designator == PARTITION_ROOT) {
+                                _cleanup_free_ char *p = NULL;
+
+                                /* Follow naming convention recommended by the relevant RFC:
+                                 * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
+                                p = build_auxiliary_path(image, ".roothash.p7s");
+                                if (!p)
+                                        return -ENOMEM;
 
-                if (!root_hash_sig_path) {
-                        /* Follow naming convention recommended by the relevant RFC:
-                         * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
-                        p = build_auxiliary_path(image, ".roothash.p7s");
-                        if (!p)
-                                return -ENOMEM;
+                                r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, (char**) &root_hash_sig, &root_hash_sig_size);
+                                if (r < 0 && r != -ENOENT)
+                                        return r;
+                                if (r >= 0)
+                                        designator = PARTITION_ROOT;
+                        }
+
+                        if (!root_hash_sig && (designator < 0 || designator == PARTITION_USR)) {
+                                _cleanup_free_ char *p = NULL;
+
+                                p = build_auxiliary_path(image, ".usrhash.p7s");
+                                if (!p)
+                                        return -ENOMEM;
 
-                        root_hash_sig_path = p;
+                                r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, (char**) &root_hash_sig, &root_hash_sig_size);
+                                if (r < 0 && r != -ENOENT)
+                                        return r;
+                                if (r >= 0)
+                                        designator = PARTITION_USR;
+                        }
                 }
 
-                r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, (char**) &root_hash_sig, &root_hash_sig_size);
-                if (r < 0) {
-                        if (r != -ENOENT)
-                                return r;
-                } else if (root_hash_sig_size == 0) /* refuse empty size signatures */
+                if (root_hash_sig && root_hash_sig_size == 0) /* refuse empty size signatures */
                         return -EINVAL;
         }
 
@@ -1820,6 +2017,9 @@ int verity_settings_load(
         if (verity_data_path)
                 verity->data_path = TAKE_PTR(verity_data_path);
 
+        if (verity->designator < 0)
+                verity->designator = designator;
+
         return 1;
 }
 
@@ -2018,7 +2218,6 @@ int dissect_image_and_warn(
         }
 
         r = dissect_image(fd, verity, mount_options, flags, ret);
-
         switch (r) {
 
         case -EOPNOTSUPP:
@@ -2161,6 +2360,8 @@ int mount_image_privately_interactively(
 static const char *const partition_designator_table[] = {
         [PARTITION_ROOT] = "root",
         [PARTITION_ROOT_SECONDARY] = "root-secondary",
+        [PARTITION_USR] = "usr",
+        [PARTITION_USR_SECONDARY] = "usr-secondary",
         [PARTITION_HOME] = "home",
         [PARTITION_SRV] = "srv",
         [PARTITION_ESP] = "esp",
@@ -2168,6 +2369,8 @@ static const char *const partition_designator_table[] = {
         [PARTITION_SWAP] = "swap",
         [PARTITION_ROOT_VERITY] = "root-verity",
         [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+        [PARTITION_USR_VERITY] = "usr-verity",
+        [PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity",
         [PARTITION_TMP] = "tmp",
         [PARTITION_VAR] = "var",
 };
index 5c1f5bd7b54ebd267955268d2fff4e14965dc163..f5db7327bd6a10e6b7fd70efc406fd7617158a67 100644 (file)
@@ -31,6 +31,8 @@ struct DissectedPartition {
 typedef enum PartitionDesignator {
         PARTITION_ROOT,
         PARTITION_ROOT_SECONDARY,  /* Secondary architecture */
+        PARTITION_USR,
+        PARTITION_USR_SECONDARY,
         PARTITION_HOME,
         PARTITION_SRV,
         PARTITION_ESP,
@@ -38,6 +40,8 @@ typedef enum PartitionDesignator {
         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_USR_VERITY,
+        PARTITION_USR_SECONDARY_VERITY,
         PARTITION_TMP,
         PARTITION_VAR,
         _PARTITION_DESIGNATOR_MAX,
@@ -45,11 +49,23 @@ typedef enum PartitionDesignator {
 } PartitionDesignator;
 
 static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
-        if (p == PARTITION_ROOT)
+        switch (p) {
+
+        case PARTITION_ROOT:
                 return PARTITION_ROOT_VERITY;
-        if (p == PARTITION_ROOT_SECONDARY)
+
+        case PARTITION_ROOT_SECONDARY:
                 return PARTITION_ROOT_SECONDARY_VERITY;
-        return _PARTITION_DESIGNATOR_INVALID;
+
+        case PARTITION_USR:
+                return PARTITION_USR_VERITY;
+
+        case PARTITION_USR_SECONDARY:
+                return PARTITION_USR_SECONDARY_VERITY;
+
+        default:
+                return _PARTITION_DESIGNATOR_INVALID;
+        }
 }
 
 typedef enum DissectImageFlags {
@@ -61,9 +77,9 @@ typedef enum DissectImageFlags {
                                     DISSECT_IMAGE_DISCARD |
                                     DISSECT_IMAGE_DISCARD_ON_CRYPTO,
         DISSECT_IMAGE_GPT_ONLY            = 1 << 4,  /* Only recognize images with GPT partition tables */
-        DISSECT_IMAGE_REQUIRE_ROOT        = 1 << 5,  /* Don't accept disks without root partition */
-        DISSECT_IMAGE_MOUNT_ROOT_ONLY     = 1 << 6,  /* Mount only the root partition */
-        DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7,  /* Mount only non-root partitions */
+        DISSECT_IMAGE_REQUIRE_ROOT        = 1 << 5,  /* Don't accept disks without root partition (and if no partition table or only single generic partition, assume it's root) */
+        DISSECT_IMAGE_MOUNT_ROOT_ONLY     = 1 << 6,  /* Mount only the root and /usr partitions */
+        DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7,  /* Mount only the non-root and non-/usr partitions */
         DISSECT_IMAGE_VALIDATE_OS         = 1 << 8,  /* Refuse mounting images that aren't identifiable as OS images */
         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 */
@@ -104,8 +120,15 @@ struct VeritySettings {
 
         /* Path to the verity data file, if stored externally */
         char *data_path;
+
+        /* PARTITION_ROOT or PARTITION_USR, depending on what these Verity settings are for */
+        PartitionDesignator designator;
 };
 
+#define VERITY_SETTINGS_DEFAULT {                               \
+                .designator = _PARTITION_DESIGNATOR_INVALID     \
+        }
+
 MountOptions* mount_options_free_all(MountOptions *options);
 DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
 const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);