]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dissect-image: support activating luks+verity partitions
authorLennart Poettering <lennart@amutable.com>
Mon, 13 Jul 2026 13:41:21 +0000 (15:41 +0200)
committerLennart Poettering <lennart@amutable.com>
Thu, 16 Jul 2026 05:05:39 +0000 (07:05 +0200)
Let's properly activate images that have both LUKS and Verity enabled
for a partition.

src/shared/dissect-image.c

index 1bfb2492922429cc51a302f75498de08f1a8cabf..8835bca354c56a7ab3703051cbbd335b4281e1fa 100644 (file)
@@ -2868,8 +2868,9 @@ static DecryptedImage* decrypted_image_free(DecryptedImage *d) {
         if (!d)
                 return NULL;
 
-        for (size_t i = 0; i < d->n_decrypted; i++) {
-                DecryptedPartition *p = d->decrypted + i;
+        /* Detach in reverse order so that nested dm-verity and dm-crypt are detached in the right order. */
+        for (size_t i = d->n_decrypted; i > 0; i--) {
+                DecryptedPartition *p = d->decrypted + i - 1;
 
                 if (p->device && p->name && !p->relinquished) {
                         _cleanup_free_ char *node = NULL;
@@ -3005,6 +3006,13 @@ static int decrypt_partition(
         if (!FLAGS_SET(policy_flags, PARTITION_POLICY_ENCRYPTED))
                 return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Attempted to unlock partition via LUKS, but it's prohibited.");
 
+        /* If a dm-verity device was already set up on this partition, then the partition is both Verity
+         * protected and LUKS encrypted, with Verity being the outer envelope and LUKS the inner. In that
+         * case open the LUKS device on top of the verity device, so that everything we read is
+         * authenticated. Since verity devices are read-only, the LUKS layer must then be read-only, too. */
+        const char *source_node = m->decrypted_node ?: m->node;
+        bool on_verity = !!m->decrypted_node;
+
         r = dlopen_cryptsetup(LOG_DEBUG);
         if (r < 0)
                 return r;
@@ -3016,7 +3024,7 @@ static int decrypt_partition(
         if (!GREEDY_REALLOC0(d->decrypted, d->n_decrypted + 1))
                 return -ENOMEM;
 
-        r = sym_crypt_init(&cd, m->node);
+        r = sym_crypt_init(&cd, source_node);
         if (r < 0)
                 return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
 
@@ -3027,8 +3035,8 @@ static int decrypt_partition(
                 return log_debug_errno(r, "Failed to load LUKS metadata: %m");
 
         r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
-                                             ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
-                                             ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
+                                             ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) || on_verity ? CRYPT_ACTIVATE_READONLY : 0) |
+                                             ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) && !on_verity ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
         if (r < 0) {
                 log_debug_errno(r, "Failed to activate LUKS device: %m");
                 return r == -EPERM ? -EKEYREJECTED : r;
@@ -3043,7 +3051,7 @@ static int decrypt_partition(
                 .device = TAKE_PTR(cd),
         };
 
-        m->decrypted_node = TAKE_PTR(node);
+        free_and_replace(m->decrypted_node, node);
         close_and_replace(m->mount_node_fd, fd);
 
         return 0;
@@ -3542,7 +3550,7 @@ success:
                 .device = TAKE_PTR(cd),
         };
 
-        m->decrypted_node = TAKE_PTR(node);
+        free_and_replace(m->decrypted_node, node);
         close_and_replace(m->mount_node_fd, mount_node_fd);
 
         return 0;
@@ -3602,10 +3610,10 @@ int dissected_image_decrypt(
 
                 PartitionPolicyFlags fl = image_policy_get_exhaustively(policy, i);
 
-                r = decrypt_partition(m, p, passphrase, flags, fl, d);
-                if (r < 0)
-                        return r;
-
+                /* Set up Verity first, and only then LUKS: if a partition is both Verity protected and
+                 * encrypted, Verity is the outer envelope and LUKS the inner, i.e. the Verity hash data
+                 * covers the LUKS ciphertext, and decrypt_partition() then stacks the LUKS device on top of
+                 * the verity device. */
                 k = partition_verity_hash_of(i);
                 if (k >= 0) {
                         r = verity_partition(m, i, p, m->partitions + k, root, verity, flags, fl, d);
@@ -3613,6 +3621,10 @@ int dissected_image_decrypt(
                                 return r;
                 }
 
+                r = decrypt_partition(m, p, passphrase, flags, fl, d);
+                if (r < 0)
+                        return r;
+
                 if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) {
                         r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, /* bool restrict_fstypes= */ true, &p->decrypted_fstype);
                         if (r < 0 && r != -EUCLEAN)