From 6b2cf68713f8a3264731a06d454ecc10792f1c4b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 15 Nov 2023 16:06:02 +0100 Subject: [PATCH] Suspend and resume also dm-integrity device with AEAD. Currently we suspend top-level device only. With OPAL, the underlying device will start to return errors once OPAL LR is locked. If the dm-integrity device is not suspended, regular journal flush corrupts the device (journal write failure), corrupting data above it. Suspending the whole stack should fix the issue. --- lib/libdevmapper.c | 12 ++++++ lib/setup.c | 92 ++++++++++++++++++++++++++++++++++++++++++---- lib/utils_dm.h | 1 + 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index a6cf27bbb..a98f199c5 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -3041,6 +3041,18 @@ const char *dm_get_dir(void) return dm_dir(); } +int dm_get_iname(const char *name, char **iname, bool with_path) +{ + int r; + + if (with_path) + r = asprintf(iname, "%s/%s_dif", dm_get_dir(), name); + else + r = asprintf(iname, "%s_dif", name); + + return r < 0 ? -ENOMEM : 0; +} + int dm_is_dm_device(int major) { return dm_is_dm_major((uint32_t)major); diff --git a/lib/setup.c b/lib/setup.c index 965bcf2c7..12fb609f1 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -547,6 +547,41 @@ int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid) return 0; } +/* + * compares two UUIDs returned by device-mapper (striped by cryptsetup) + * used for stacked LUKS2 & INTEGRITY devices + */ +static int crypt_uuid_integrity_cmp(const char *dm_uuid, const char *dmi_uuid) +{ + int i; + char *str, *stri; + + if (!dm_uuid || !dmi_uuid) + return -EINVAL; + + /* skip beyond LUKS2_HW_OPAL prefix */ + if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL))) + dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL); + + str = strchr(dm_uuid, '-'); + if (!str) + return -EINVAL; + + stri = strchr(dmi_uuid, '-'); + if (!stri) + return -EINVAL; + + for (i = 1; str[i] && str[i] != '-'; i++) { + if (!stri[i]) + return -EINVAL; + + if (str[i] != stri[i]) + return -EINVAL; + } + + return 0; +} + /* * compares type of active device to provided string */ @@ -3891,10 +3926,10 @@ int crypt_suspend(struct crypt_device *cd, bool dm_opal_uuid; crypt_status_info ci; int r; - struct crypt_dm_active_device dmd; + struct crypt_dm_active_device dmd, dmdi = {}; uint32_t opal_segment_number = 1, dmflags = DM_SUSPEND_WIPE_KEY; struct dm_target *tgt = &dmd.segment; - char *key_desc = NULL; + char *key_desc = NULL, *iname = NULL; if (!cd || !name) return -EINVAL; @@ -3950,6 +3985,23 @@ int crypt_suspend(struct crypt_device *cd, goto out; } + /* check UUID of integrity device underneath crypt device */ + if (crypt_get_integrity_tag_size(cd)) { + r = dm_get_iname(name, &iname, false); + if (r) + goto out; + + r = dm_query_device(cd, iname, DM_ACTIVE_UUID, &dmdi); + if (r < 0) + goto out; + + r = crypt_uuid_integrity_cmp(dmd.uuid, dmdi.uuid); + if (r < 0) { + log_dbg(cd, "Integrity device uuid: %s mismatches crypt device uuid %s", dmdi.uuid, dmd.uuid); + goto out; + } + } + r = dm_status_suspended(cd, name); if (r < 0) goto out; @@ -3989,14 +4041,24 @@ int crypt_suspend(struct crypt_device *cd, goto out; } + /* Suspend integrity device underneath; keep crypt suspended if it fails */ + if (crypt_get_integrity_tag_size(cd)) { + r = dm_suspend_device(cd, iname, 0); + if (r) + log_err(cd, _("Error during suspending device %s."), iname); + } + crypt_drop_keyring_key_by_description(cd, key_desc, cd->keyring_key_type); if (dm_opal_uuid && (!crypt_data_device(cd) || opal_lock(cd, crypt_data_device(cd), opal_segment_number))) log_err(cd, _("Device %s was suspended but hardware OPAL device cannot be locked."), name); out: free(key_desc); + free(iname); dm_targets_free(cd, &dmd); + dm_targets_free(cd, &dmdi); free(CONST_CAST(void*)dmd.uuid); + free(CONST_CAST(void*)dmdi.uuid); return r; } @@ -4077,6 +4139,7 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, uint32_t opal_segment_number; key_serial_t user_vk_kid = 0; struct volume_key *p_crypt = vk, *p_opal = NULL, *zerokey = NULL, *crypt_key = NULL, *opal_key = NULL; + char *iname = NULL; assert(digest >= 0); assert(vk && crypt_volume_key_get_id(vk) == digest); @@ -4140,6 +4203,16 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, } } + if (crypt_get_integrity_tag_size(cd)) { + r = dm_get_iname(name, &iname, false); + if (r) + goto out; + + r = dm_resume_device(cd, iname, 0); + if (r) + log_err(cd, _("Error during resuming device %s."), iname); + } + if (enc_type == CRYPT_OPAL_HW_ONLY) r = dm_resume_device(cd, name, 0); else @@ -4163,6 +4236,7 @@ static int resume_luks2_by_volume_key(struct crypt_device *cd, crypt_free_volume_key(zerokey); crypt_free_volume_key(opal_key); crypt_free_volume_key(crypt_key); + free(iname); return r; } @@ -4743,15 +4817,18 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char * struct crypt_dm_active_device *dmdi) { int r; - const char *iname = NULL; - char *ipath = NULL; + char *iname = NULL, *ipath = NULL; if (!type || !name || !dmd || !dmdi) return -EINVAL; - if (asprintf(&ipath, "%s/%s_dif", dm_get_dir(), name) < 0) - return -ENOMEM; - iname = ipath + strlen(dm_get_dir()) + 1; + r = dm_get_iname(name, &iname, false); + if (r) + goto out; + + r = dm_get_iname(name, &ipath, true); + if (r) + goto out; /* drop CRYPT_ACTIVATE_REFRESH flag if any device is inactive */ r = check_devices(cd, name, iname, &dmd->flags); @@ -4764,6 +4841,7 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char * r = _create_device_with_integrity(cd, type, name, iname, ipath, dmd, dmdi); out: free(ipath); + free(iname); return r; } diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 79212a253..d5eadbdd0 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -234,6 +234,7 @@ int dm_clear_device(struct crypt_device *cd, const char *name); int dm_cancel_deferred_removal(const char *name); const char *dm_get_dir(void); +int dm_get_iname(const char *name, char **iname, bool with_path); int lookup_dm_dev_by_uuid(struct crypt_device *cd, const char *uuid, const char *type);