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;
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;
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");
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;
.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;
.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;
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);
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)