From 39a014f6019f0acd3270d08872df9ce30fb05327 Mon Sep 17 00:00:00 2001 From: Ondrej Kozina Date: Fri, 2 Oct 2015 17:21:10 +0200 Subject: [PATCH] dm backend with support for multi-segment devices. Support for multi-segment devices is requirement for online reencryption to work. Introducing modififed dm backend that splits data structures describing active device and individual dm target (or segment). --- lib/integrity/integrity.c | 128 ++--- lib/internal.h | 7 + lib/libdevmapper.c | 907 ++++++++++++++++++++++---------- lib/loopaes/loopaes.c | 35 +- lib/luks1/keyencryption.c | 36 +- lib/luks1/keymanage.c | 26 +- lib/luks2/luks2_json_metadata.c | 39 +- lib/setup.c | 571 +++++++++++--------- lib/tcrypt/tcrypt.c | 134 ++--- lib/tcrypt/tcrypt.h | 4 +- lib/utils_dm.h | 56 +- lib/verity/verity.c | 48 +- 12 files changed, 1219 insertions(+), 772 deletions(-) diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 3b76e0f6..394ad4b7 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -194,39 +194,19 @@ int INTEGRITY_create_dmd_device(struct crypt_device *cd, return -EINVAL; *dmd = (struct crypt_dm_active_device) { - .target = DM_INTEGRITY, - .data_device = crypt_data_device(cd), .flags = flags, - .u.integrity = { - .offset = crypt_get_data_offset(cd), - .tag_size = crypt_get_integrity_tag_size(cd), - .sector_size = crypt_get_sector_size(cd), - .vk = vk, - .journal_crypt_key = journal_crypt_key, - .journal_integrity_key = journal_mac_key, - } }; - if (dmd->data_device != crypt_metadata_device(cd)) - dmd->u.integrity.meta_device = crypt_metadata_device(cd); - r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd), - dmd->u.integrity.offset * SECTOR_SIZE, &dmd->size); + crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size); if (r < 0) return r; - if (params) { - dmd->u.integrity.journal_size = params->journal_size; - dmd->u.integrity.journal_watermark = params->journal_watermark; - dmd->u.integrity.journal_commit_time = params->journal_commit_time; - dmd->u.integrity.interleave_sectors = params->interleave_sectors; - dmd->u.integrity.buffer_sectors = params->buffer_sectors; - dmd->u.integrity.integrity = params->integrity; - dmd->u.integrity.journal_integrity = params->journal_integrity; - dmd->u.integrity.journal_crypt = params->journal_crypt; - } - - return r; + return dm_integrity_target_set(&dmd->segment, 0, dmd->size, + crypt_metadata_device(cd), crypt_data_device(cd), + crypt_get_integrity_tag_size(cd), crypt_get_data_offset(cd), + crypt_get_sector_size(cd), vk, journal_crypt_key, + journal_mac_key, params); } int INTEGRITY_activate_dmd_device(struct crypt_device *cd, @@ -235,17 +215,21 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd, { int r; uint32_t dmi_flags; + struct dm_target *tgt = &dmd->segment; + + if (!single_segment(dmd) || tgt->type != DM_INTEGRITY) + return -EINVAL; log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".", - device_path(dmd->data_device), name, dmd->u.integrity.tag_size, dmd->size); + device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size); - r = device_block_adjust(cd, dmd->data_device, DEV_EXCL, - dmd->u.integrity.offset, NULL, &dmd->flags); + r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, + tgt->u.integrity.offset, NULL, &dmd->flags); if (r) return r; - if (dmd->u.integrity.meta_device) { - r = device_block_adjust(cd, dmd->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL); + if (tgt->u.integrity.meta_device) { + r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL); if (r) return r; } @@ -273,7 +257,9 @@ int INTEGRITY_activate(struct crypt_device *cd, if (r < 0) return r; - return INTEGRITY_activate_dmd_device(cd, name, &dmd); + r = INTEGRITY_activate_dmd_device(cd, name, &dmd); + dm_targets_free(cd, &dmd); + return r; } int INTEGRITY_format(struct crypt_device *cd, @@ -284,64 +270,56 @@ int INTEGRITY_format(struct crypt_device *cd, uint32_t dmi_flags; char tmp_name[64], tmp_uuid[40]; struct crypt_dm_active_device dmdi = { - .target = DM_INTEGRITY, - .data_device = crypt_data_device(cd), .size = 8, .flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */ - .u.integrity = { - .offset = crypt_get_data_offset(cd), - .tag_size = crypt_get_integrity_tag_size(cd), - .sector_size = crypt_get_sector_size(cd), - .journal_crypt_key = journal_crypt_key, - .journal_integrity_key = journal_mac_key, - } }; + struct dm_target *tgt = &dmdi.segment; int r; uuid_t tmp_uuid_bin; - - if (dmdi.data_device != crypt_metadata_device(cd)) - dmdi.u.integrity.meta_device = crypt_metadata_device(cd); - - if (params) { - dmdi.u.integrity.journal_size = params->journal_size; - dmdi.u.integrity.journal_watermark = params->journal_watermark; - dmdi.u.integrity.journal_commit_time = params->journal_commit_time; - dmdi.u.integrity.interleave_sectors = params->interleave_sectors; - dmdi.u.integrity.buffer_sectors = params->buffer_sectors; - dmdi.u.integrity.journal_integrity = params->journal_integrity; - dmdi.u.integrity.journal_crypt = params->journal_crypt; - dmdi.u.integrity.integrity = params->integrity; - } + struct volume_key *vk = NULL; uuid_generate(tmp_uuid_bin); uuid_unparse(tmp_uuid_bin, tmp_uuid); snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid); - log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.", - device_path(dmdi.data_device), tmp_name, dmdi.u.integrity.tag_size); - - r = device_block_adjust(cd, dmdi.data_device, DEV_EXCL, dmdi.u.integrity.offset, NULL, NULL); - if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) { - log_err(cd, _("Kernel doesn't support dm-integrity mapping.")); - return -ENOTSUP; - } - if (r) - return r; - - if (dmdi.u.integrity.meta_device) { - r = device_block_adjust(cd, dmdi.u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL); - if (r) - return r; - } - /* There is no data area, we can actually use fake zeroed key */ if (params && params->integrity_key_size) - dmdi.u.integrity.vk = crypt_alloc_volume_key(params->integrity_key_size, NULL); + vk = crypt_alloc_volume_key(params->integrity_key_size, NULL); + + r = dm_integrity_target_set(tgt, 0, dmdi.size, crypt_metadata_device(cd), + crypt_data_device(cd), crypt_get_integrity_tag_size(cd), + crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk, + journal_crypt_key, journal_mac_key, params); + if (r < 0) { + crypt_free_volume_key(vk); + return r; + } + + log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.", + device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size); + + r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, tgt->u.integrity.offset, NULL, NULL); + if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) { + log_err(cd, _("Kernel doesn't support dm-integrity mapping.")); + r = -ENOTSUP; + } + if (r) { + dm_targets_free(cd, &dmdi); + return r; + } + + if (tgt->u.integrity.meta_device) { + r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL); + if (r) { + dm_targets_free(cd, &dmdi); + return r; + } + } r = dm_create_device(cd, tmp_name, "INTEGRITY", &dmdi); - - crypt_free_volume_key(dmdi.u.integrity.vk); + crypt_free_volume_key(vk); + dm_targets_free(cd, &dmdi); if (r) return r; diff --git a/lib/internal.h b/lib/internal.h index 5b96302b..4e823a86 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -65,6 +65,13 @@ # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif +#define MOVE_REF(x, y) \ + do { \ + typeof (x) *_px = &(x), *_py = &(y); \ + *_px = *_py; \ + *_py = NULL; \ + } while (0) + struct crypt_device; struct volume_key { diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 64776be9..1f778716 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -42,6 +42,7 @@ #define DM_CRYPT_TARGET "crypt" #define DM_VERITY_TARGET "verity" #define DM_INTEGRITY_TARGET "integrity" +#define DM_LINEAR_TARGET "linear" #define RETRY_COUNT 5 /* Set if DM target versions were probed */ @@ -221,7 +222,7 @@ static int _dm_check_versions(struct crypt_device *cd, dm_target_type target_typ unsigned dm_maj, dm_min, dm_patch; int r = 0; - if ((target_type == DM_CRYPT && _dm_crypt_checked) || + if (((target_type == DM_CRYPT || target_type == DM_LINEAR) && _dm_crypt_checked) || (target_type == DM_VERITY && _dm_verity_checked) || (target_type == DM_INTEGRITY && _dm_integrity_checked) || (_dm_crypt_checked && _dm_verity_checked && _dm_integrity_checked)) @@ -295,7 +296,7 @@ int dm_flags(struct crypt_device *cd, dm_target_type target, uint32_t *flags) _dm_crypt_checked && _dm_verity_checked && _dm_integrity_checked) return 0; - if ((target == DM_CRYPT && _dm_crypt_checked) || + if (((target == DM_CRYPT || target == DM_LINEAR) && _dm_crypt_checked) || (target == DM_VERITY && _dm_verity_checked) || (target == DM_INTEGRITY && _dm_integrity_checked)) return 0; @@ -380,9 +381,9 @@ static void hex_key(char *hexkey, size_t key_size, const char *key) sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]); } -static size_t int_log10(size_t x) +static size_t int_log10(uint64_t x) { - size_t r = 0; + uint64_t r = 0; for (x /= 10; x > 0; x /= 10) r++; return r; @@ -520,16 +521,16 @@ static int cipher_dm2c(char **org_c, char **org_i, const char *c_dm, const char } /* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt */ -static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t flags) +static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags) { int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0; char *params, *hexkey; char sector_feature[32], features[512], integrity_dm[256], cipher_dm[256]; - if (!dmd) + if (!tgt) return NULL; - r = cipher_c2dm(dmd->u.crypt.cipher, dmd->u.crypt.integrity, dmd->u.crypt.tag_size, + r = cipher_c2dm(tgt->u.crypt.cipher, tgt->u.crypt.integrity, tgt->u.crypt.tag_size, cipher_dm, sizeof(cipher_dm), integrity_dm, sizeof(integrity_dm)); if (r < 0) return NULL; @@ -540,12 +541,12 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl num_options++; if (flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) num_options++; - if (dmd->u.crypt.integrity) + if (tgt->u.crypt.integrity) num_options++; - if (dmd->u.crypt.sector_size != SECTOR_SIZE) { + if (tgt->u.crypt.sector_size != SECTOR_SIZE) { num_options++; - snprintf(sector_feature, sizeof(sector_feature), " sector_size:%u", dmd->u.crypt.sector_size); + snprintf(sector_feature, sizeof(sector_feature), " sector_size:%u", tgt->u.crypt.sector_size); } else *sector_feature = '\0'; @@ -562,10 +563,10 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl null_cipher = 1; if (flags & CRYPT_ACTIVATE_KEYRING_KEY) { - keystr_len = strlen(dmd->u.crypt.vk->key_description) + int_log10(dmd->u.crypt.vk->keylength) + 10; + keystr_len = strlen(tgt->u.crypt.vk->key_description) + int_log10(tgt->u.crypt.vk->keylength) + 10; hexkey = crypt_safe_alloc(keystr_len); } else - hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1)); + hexkey = crypt_safe_alloc(null_cipher ? 2 : (tgt->u.crypt.vk->keylength * 2 + 1)); if (!hexkey) return NULL; @@ -573,24 +574,24 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl if (null_cipher) strncpy(hexkey, "-", 2); else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) { - r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key_description); + r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", tgt->u.crypt.vk->keylength, tgt->u.crypt.vk->key_description); if (r < 0 || r >= keystr_len) { params = NULL; goto out; } } else - hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key); + hex_key(hexkey, tgt->u.crypt.vk->keylength, tgt->u.crypt.vk->key); max_size = strlen(hexkey) + strlen(cipher_dm) + - strlen(device_block_path(dmd->data_device)) + + strlen(device_block_path(tgt->data_device)) + strlen(features) + 64; params = crypt_safe_alloc(max_size); if (!params) goto out; r = snprintf(params, max_size, "%s %s %" PRIu64 " %s %" PRIu64 "%s", - cipher_dm, hexkey, dmd->u.crypt.iv_offset, - device_block_path(dmd->data_device), dmd->u.crypt.offset, + cipher_dm, hexkey, tgt->u.crypt.iv_offset, + device_block_path(tgt->data_device), tgt->u.crypt.offset, features); if (r < 0 || r >= max_size) { crypt_safe_free(params); @@ -602,16 +603,18 @@ out: } /* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity */ -static char *get_dm_verity_params(struct crypt_params_verity *vp, - struct crypt_dm_active_device *dmd, uint32_t flags) +static char *get_dm_verity_params(const struct dm_target *tgt, uint32_t flags) { int max_size, r, num_options = 0; + struct crypt_params_verity *vp; char *params = NULL, *hexroot = NULL, *hexsalt = NULL; char features[256], fec_features[256]; - if (!vp || !dmd) + if (!tgt || !tgt->u.verity.vp) return NULL; + vp = tgt->u.verity.vp; + /* These flags are not compatible */ if ((flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) && (flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION)) @@ -626,12 +629,12 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, if (flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) num_options++; - if (dmd->u.verity.fec_device) { + if (tgt->u.verity.fec_device) { num_options += 8; snprintf(fec_features, sizeof(fec_features)-1, " use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 " fec_roots %" PRIu32, - device_block_path(dmd->u.verity.fec_device), dmd->u.verity.fec_offset, - vp->data_size + dmd->u.verity.hash_blocks, vp->fec_roots); + device_block_path(tgt->u.verity.fec_device), tgt->u.verity.fec_offset, + vp->data_size + tgt->u.verity.hash_blocks, vp->fec_roots); } else *fec_features = '\0'; @@ -644,10 +647,10 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, else *features = '\0'; - hexroot = crypt_safe_alloc(dmd->u.verity.root_hash_size * 2 + 1); + hexroot = crypt_safe_alloc(tgt->u.verity.root_hash_size * 2 + 1); if (!hexroot) goto out; - hex_key(hexroot, dmd->u.verity.root_hash_size, dmd->u.verity.root_hash); + hex_key(hexroot, tgt->u.verity.root_hash_size, tgt->u.verity.root_hash); hexsalt = crypt_safe_alloc(vp->salt_size ? vp->salt_size * 2 + 1 : 2); if (!hexsalt) @@ -658,8 +661,8 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, strncpy(hexsalt, "-", 2); max_size = strlen(hexroot) + strlen(hexsalt) + - strlen(device_block_path(dmd->data_device)) + - strlen(device_block_path(dmd->u.verity.hash_device)) + + strlen(device_block_path(tgt->data_device)) + + strlen(device_block_path(tgt->u.verity.hash_device)) + strlen(vp->hash_name) + strlen(features) + strlen(fec_features) + 128; params = crypt_safe_alloc(max_size); @@ -668,10 +671,10 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, r = snprintf(params, max_size, "%u %s %s %u %u %" PRIu64 " %" PRIu64 " %s %s %s%s%s", - vp->hash_type, device_block_path(dmd->data_device), - device_block_path(dmd->u.verity.hash_device), + vp->hash_type, device_block_path(tgt->data_device), + device_block_path(tgt->u.verity.hash_device), vp->data_block_size, vp->hash_block_size, - vp->data_size, dmd->u.verity.hash_offset, + vp->data_size, tgt->u.verity.hash_offset, vp->hash_name, hexroot, hexsalt, features, fec_features); if (r < 0 || r >= max_size) { crypt_safe_free(params); @@ -681,124 +684,122 @@ out: crypt_safe_free(hexroot); crypt_safe_free(hexsalt); return params; - } -static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_t flags) +static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags) { int r, max_size, num_options = 0; char *params, *hexkey, mode; char features[512], feature[256]; - if (!dmd) + if (!tgt) return NULL; - max_size = strlen(device_block_path(dmd->data_device)) + - (dmd->u.integrity.meta_device ? strlen(device_block_path(dmd->u.integrity.meta_device)) : 0) + - (dmd->u.integrity.vk ? dmd->u.integrity.vk->keylength * 2 : 0) + - (dmd->u.integrity.journal_integrity_key ? dmd->u.integrity.journal_integrity_key->keylength * 2 : 0) + - (dmd->u.integrity.journal_crypt_key ? dmd->u.integrity.journal_crypt_key->keylength * 2 : 0) + - (dmd->u.integrity.integrity ? strlen(dmd->u.integrity.integrity) : 0) + - (dmd->u.integrity.journal_integrity ? strlen(dmd->u.integrity.journal_integrity) : 0) + - (dmd->u.integrity.journal_crypt ? strlen(dmd->u.integrity.journal_crypt) : 0) + - 128; + max_size = strlen(device_block_path(tgt->data_device)) + + (tgt->u.integrity.meta_device ? strlen(device_block_path(tgt->u.integrity.meta_device)) : 0) + + (tgt->u.integrity.vk ? tgt->u.integrity.vk->keylength * 2 : 0) + + (tgt->u.integrity.journal_integrity_key ? tgt->u.integrity.journal_integrity_key->keylength * 2 : 0) + + (tgt->u.integrity.journal_crypt_key ? tgt->u.integrity.journal_crypt_key->keylength * 2 : 0) + + (tgt->u.integrity.integrity ? strlen(tgt->u.integrity.integrity) : 0) + + (tgt->u.integrity.journal_integrity ? strlen(tgt->u.integrity.journal_integrity) : 0) + + (tgt->u.integrity.journal_crypt ? strlen(tgt->u.integrity.journal_crypt) : 0) + 128; params = crypt_safe_alloc(max_size); if (!params) return NULL; *features = '\0'; - if (dmd->u.integrity.journal_size) { + if (tgt->u.integrity.journal_size) { num_options++; snprintf(feature, sizeof(feature), "journal_sectors:%u ", - (unsigned)(dmd->u.integrity.journal_size / SECTOR_SIZE)); + (unsigned)(tgt->u.integrity.journal_size / SECTOR_SIZE)); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.journal_watermark) { + if (tgt->u.integrity.journal_watermark) { num_options++; snprintf(feature, sizeof(feature), "journal_watermark:%u ", - dmd->u.integrity.journal_watermark); + tgt->u.integrity.journal_watermark); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.journal_commit_time) { + if (tgt->u.integrity.journal_commit_time) { num_options++; snprintf(feature, sizeof(feature), "commit_time:%u ", - dmd->u.integrity.journal_commit_time); + tgt->u.integrity.journal_commit_time); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.interleave_sectors) { + if (tgt->u.integrity.interleave_sectors) { num_options++; snprintf(feature, sizeof(feature), "interleave_sectors:%u ", - dmd->u.integrity.interleave_sectors); + tgt->u.integrity.interleave_sectors); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.sector_size) { + if (tgt->u.integrity.sector_size) { num_options++; snprintf(feature, sizeof(feature), "block_size:%u ", - dmd->u.integrity.sector_size); + tgt->u.integrity.sector_size); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.buffer_sectors) { + if (tgt->u.integrity.buffer_sectors) { num_options++; snprintf(feature, sizeof(feature), "buffer_sectors:%u ", - dmd->u.integrity.buffer_sectors); + tgt->u.integrity.buffer_sectors); strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.integrity) { + if (tgt->u.integrity.integrity) { num_options++; - if (dmd->u.integrity.vk) { - hexkey = crypt_safe_alloc(dmd->u.integrity.vk->keylength * 2 + 1); + if (tgt->u.integrity.vk) { + hexkey = crypt_safe_alloc(tgt->u.integrity.vk->keylength * 2 + 1); if (!hexkey) { crypt_safe_free(params); return NULL; } - hex_key(hexkey, dmd->u.integrity.vk->keylength, dmd->u.integrity.vk->key); + hex_key(hexkey, tgt->u.integrity.vk->keylength, tgt->u.integrity.vk->key); } else hexkey = NULL; snprintf(feature, sizeof(feature), "internal_hash:%s%s%s ", - dmd->u.integrity.integrity, hexkey ? ":" : "", hexkey ?: ""); + tgt->u.integrity.integrity, hexkey ? ":" : "", hexkey ?: ""); strncat(features, feature, sizeof(features) - strlen(features) - 1); crypt_safe_free(hexkey); } - if (dmd->u.integrity.journal_integrity) { + if (tgt->u.integrity.journal_integrity) { num_options++; - if (dmd->u.integrity.journal_integrity_key) { - hexkey = crypt_safe_alloc(dmd->u.integrity.journal_integrity_key->keylength * 2 + 1); + if (tgt->u.integrity.journal_integrity_key) { + hexkey = crypt_safe_alloc(tgt->u.integrity.journal_integrity_key->keylength * 2 + 1); if (!hexkey) { crypt_safe_free(params); return NULL; } - hex_key(hexkey, dmd->u.integrity.journal_integrity_key->keylength, - dmd->u.integrity.journal_integrity_key->key); + hex_key(hexkey, tgt->u.integrity.journal_integrity_key->keylength, + tgt->u.integrity.journal_integrity_key->key); } else hexkey = NULL; snprintf(feature, sizeof(feature), "journal_mac:%s%s%s ", - dmd->u.integrity.journal_integrity, hexkey ? ":" : "", hexkey ?: ""); + tgt->u.integrity.journal_integrity, hexkey ? ":" : "", hexkey ?: ""); strncat(features, feature, sizeof(features) - strlen(features) - 1); crypt_safe_free(hexkey); } - if (dmd->u.integrity.journal_crypt) { + if (tgt->u.integrity.journal_crypt) { num_options++; - if (dmd->u.integrity.journal_crypt_key) { - hexkey = crypt_safe_alloc(dmd->u.integrity.journal_crypt_key->keylength * 2 + 1); + if (tgt->u.integrity.journal_crypt_key) { + hexkey = crypt_safe_alloc(tgt->u.integrity.journal_crypt_key->keylength * 2 + 1); if (!hexkey) { crypt_safe_free(params); return NULL; } - hex_key(hexkey, dmd->u.integrity.journal_crypt_key->keylength, - dmd->u.integrity.journal_crypt_key->key); + hex_key(hexkey, tgt->u.integrity.journal_crypt_key->keylength, + tgt->u.integrity.journal_crypt_key->key); } else hexkey = NULL; snprintf(feature, sizeof(feature), "journal_crypt:%s%s%s ", - dmd->u.integrity.journal_crypt, hexkey ? ":" : "", hexkey ?: ""); + tgt->u.integrity.journal_crypt, hexkey ? ":" : "", hexkey ?: ""); strncat(features, feature, sizeof(features) - strlen(features) - 1); crypt_safe_free(hexkey); } @@ -809,10 +810,10 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_ strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (dmd->u.integrity.meta_device) { + if (tgt->u.integrity.meta_device) { num_options++; snprintf(feature, sizeof(feature), "meta_device:%s ", - device_block_path(dmd->u.integrity.meta_device)); + device_block_path(tgt->u.integrity.meta_device)); strncat(features, feature, sizeof(features) - strlen(features) - 1); } @@ -824,8 +825,8 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_ mode = 'J'; r = snprintf(params, max_size, "%s %" PRIu64 " %d %c %d %s", - device_block_path(dmd->data_device), dmd->u.integrity.offset, - dmd->u.integrity.tag_size, mode, + device_block_path(tgt->data_device), tgt->u.integrity.offset, + tgt->u.integrity.tag_size, mode, num_options, *features ? features : ""); if (r < 0 || r >= max_size) { crypt_safe_free(params); @@ -835,6 +836,27 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_ return params; } +static char *get_dm_linear_params(const struct dm_target *tgt, uint32_t flags) +{ + char *params; + int r; + int max_size = strlen(device_block_path(tgt->data_device)) + int_log10(tgt->u.linear.offset) + 3; + + params = crypt_safe_alloc(max_size); + if (!params) + return NULL; + + r = snprintf(params, max_size, "%s %" PRIu64, + device_block_path(tgt->data_device), tgt->u.linear.offset); + + if (r < 0 || r >= max_size) { + crypt_safe_free(params); + params = NULL; + } + + return params; +} + /* DM helpers */ static int _dm_remove(const char *name, int udev_wait, int deferred) { @@ -928,7 +950,7 @@ error: int dm_error_device(struct crypt_device *cd, const char *name) { int r; - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; if (!name) return -EINVAL; @@ -941,6 +963,8 @@ int dm_error_device(struct crypt_device *cd, const char *name) else r = -EINVAL; + dm_targets_free(cd, &dmd); + dm_exit_context(); return r; @@ -1080,39 +1104,99 @@ int lookup_dm_dev_by_uuid(struct crypt_device *cd, const char *uuid, const char return r; } +static int _add_dm_targets(struct dm_task *dmt, struct crypt_dm_active_device *dmd) +{ + const char *target; + struct dm_target *tgt = &dmd->segment; + + do { + switch (tgt->type) { + case DM_CRYPT: + target = DM_CRYPT_TARGET; + break; + case DM_VERITY: + target = DM_VERITY_TARGET; + break; + case DM_INTEGRITY: + target = DM_INTEGRITY_TARGET; + break; + case DM_LINEAR: + target = DM_LINEAR_TARGET; + break; + default: + return -ENOTSUP; + } + + if (!dm_task_add_target(dmt, tgt->offset, tgt->size, target, tgt->params)) + return -EINVAL; + + tgt = tgt->next; + } while (tgt); + + return 0; +} + +static void _destroy_dm_targets_params(struct crypt_dm_active_device *dmd) +{ + struct dm_target *t = &dmd->segment; + + do { + crypt_safe_free(t->params); + t->params = NULL; + t = t->next; + } while (t); +} + +static int _create_dm_targets_params(struct crypt_dm_active_device *dmd) +{ + int r; + struct dm_target *tgt = &dmd->segment; + + do { + if (tgt->type == DM_CRYPT) + tgt->params = get_dm_crypt_params(tgt, dmd->flags); + else if (tgt->type == DM_VERITY) + tgt->params = get_dm_verity_params(tgt, dmd->flags); + else if (tgt->type == DM_INTEGRITY) + tgt->params = get_dm_integrity_params(tgt, dmd->flags); + else if (tgt->type == DM_LINEAR) + tgt->params = get_dm_linear_params(tgt, dmd->flags); + else { + r = -ENOTSUP; + goto err; + } + + if (!tgt->params) { + r = -EINVAL; + goto err; + } + tgt = tgt->next; + } while (tgt); + + return 0; +err: + _destroy_dm_targets_params(dmd); + return r; +} + static int _dm_create_device(struct crypt_device *cd, const char *name, const char *type, - struct device *device, uint32_t flags, - const char *uuid, uint64_t size, - dm_target_type target, char *params) + const char *uuid, struct crypt_dm_active_device *dmd) { struct dm_task *dmt = NULL; struct dm_info dmi; char dev_uuid[DM_UUID_LEN] = {0}; - const char *target_name; int r = -EINVAL; - uint32_t dmt_flags, cookie = 0, read_ahead = 0; + uint32_t cookie = 0, dmt_flags = 0, read_ahead = 0; uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; /* Only need DM_SECURE_SUPPORTED, no target specific fail matters */ - dm_flags(cd, target, &dmt_flags); + dm_flags(cd, dmd->segment.type, &dmt_flags); - if (target == DM_CRYPT) - target_name = DM_CRYPT_TARGET; - else if (target == DM_VERITY) - target_name = DM_VERITY_TARGET; - else if (target == DM_INTEGRITY) - target_name = DM_INTEGRITY_TARGET; - else - return -EINVAL; - - if (!params) - return -EINVAL; - - if (flags & CRYPT_ACTIVATE_PRIVATE) + if (dmd->flags & CRYPT_ACTIVATE_PRIVATE) udev_flags |= CRYPT_TEMP_UDEV_FLAGS; /* All devices must have DM_UUID, only resize on old device is exception */ - if (!dm_prepare_uuid(cd, name, type, uuid, dev_uuid, sizeof(dev_uuid))) + if (!dm_prepare_uuid(cd, name, type, dmd->uuid, dev_uuid, sizeof(dev_uuid))) goto out; if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) @@ -1126,14 +1210,21 @@ static int _dm_create_device(struct crypt_device *cd, const char *name, const ch if ((dmt_flags & DM_SECURE_SUPPORTED) && !dm_task_secure_data(dmt)) goto out; - if ((flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) + if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) goto out; - if (!dm_task_add_target(dmt, 0, size, target_name, params)) + r = _create_dm_targets_params(dmd); + if (r) goto out; + r = _add_dm_targets(dmt, dmd); + if (r) + goto out; + + r = -EINVAL; + #ifdef DM_READ_AHEAD_MINIMUM_FLAG - if (device_read_ahead(device, &read_ahead) && + if (device_read_ahead(dmd->segment.data_device, &read_ahead) && !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG)) goto out; #endif @@ -1164,7 +1255,9 @@ out: dm_task_update_nodes(); /* If code just loaded target module, update versions */ - _dm_check_versions(cd, target); + _dm_check_versions(cd, dmd->segment.type); + + _destroy_dm_targets_params(dmd); return r; } @@ -1201,25 +1294,13 @@ out: return r; } -static int _dm_reload_device(struct crypt_device *cd, - const char *name, struct device *device, - uint32_t flags, uint64_t size, - dm_target_type target, const char *params) +static int _dm_reload_device(struct crypt_device *cd, const char *name, + struct crypt_dm_active_device *dmd) { - const char *target_name; int r = -EINVAL; struct dm_task *dmt = NULL; uint32_t dmt_flags = 0, read_ahead = 0; - if (target == DM_CRYPT) - target_name = DM_CRYPT_TARGET; - else if (target == DM_VERITY) - target_name = DM_VERITY_TARGET; - else if (target == DM_INTEGRITY) - target_name = DM_INTEGRITY_TARGET; - else - return -EINVAL; - /* All devices must have DM_UUID, only resize on old device is exception */ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD))) goto out; @@ -1227,38 +1308,132 @@ static int _dm_reload_device(struct crypt_device *cd, if (!dm_task_set_name(dmt, name)) goto out; - if (dm_flags(cd, target, &dmt_flags)) + if (dm_flags(cd, dmd->segment.type, &dmt_flags)) goto out; if ((dmt_flags & DM_SECURE_SUPPORTED) && !dm_task_secure_data(dmt)) goto out; - - /* FIXME: resume only ?*/ - if ((flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) + if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) goto out; - if (!dm_task_add_target(dmt, 0, size, target_name, params)) + r = _create_dm_targets_params(dmd); + if (r) goto out; + r = _add_dm_targets(dmt, dmd); + if (r) + goto out; + + r = -EINVAL; + #ifdef DM_READ_AHEAD_MINIMUM_FLAG - if (device_read_ahead(device, &read_ahead) && + if (device_read_ahead(dmd->segment.data_device, &read_ahead) && !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG)) goto out; #endif if (dm_task_run(dmt)) r = 0; - out: if (dmt) dm_task_destroy(dmt); /* If code just loaded target module, update versions */ - _dm_check_versions(cd, target); + _dm_check_versions(cd, dmd->segment.type); + + _destroy_dm_targets_params(dmd); return r; } +static void crypt_free_verity_params(struct crypt_params_verity *vp) +{ + if (!vp) + return; + + free(CONST_CAST(void*)vp->hash_name); + free(CONST_CAST(void*)vp->data_device); + free(CONST_CAST(void*)vp->hash_device); + free(CONST_CAST(void*)vp->fec_device); + free(CONST_CAST(void*)vp->salt); + free(vp); +} + +static void _dm_target_free_query_path(struct crypt_device *cd, struct dm_target *tgt) +{ + switch(tgt->type) { + case DM_CRYPT: + crypt_free_volume_key(tgt->u.crypt.vk); + free(CONST_CAST(void*)tgt->u.crypt.cipher); + break; + case DM_INTEGRITY: + free(CONST_CAST(void*)tgt->u.integrity.integrity); + crypt_free_volume_key(tgt->u.integrity.vk); + + free(CONST_CAST(void*)tgt->u.integrity.journal_integrity); + crypt_free_volume_key(tgt->u.integrity.journal_integrity_key); + + free(CONST_CAST(void*)tgt->u.integrity.journal_crypt); + crypt_free_volume_key(tgt->u.integrity.journal_crypt_key); + + device_free(cd, tgt->u.integrity.meta_device); + break; + case DM_VERITY: + crypt_free_verity_params(tgt->u.verity.vp); + device_free(cd, tgt->u.verity.hash_device); + free(CONST_CAST(void*)tgt->u.verity.root_hash); + /* fall through */ + case DM_LINEAR: + break; + default: + log_err(NULL, "Unknown dm target type."); + return; + } + + device_free(cd, tgt->data_device); +} + +static void _dm_target_erase(struct crypt_device *cd, struct dm_target *tgt) +{ + if (tgt->direction == TARGET_QUERY) + _dm_target_free_query_path(cd, tgt); + + if (tgt->type == DM_CRYPT) + free(CONST_CAST(void*)tgt->u.crypt.integrity); +} + +void dm_targets_free(struct crypt_device *cd, struct crypt_dm_active_device *dmd) +{ + struct dm_target *t = &dmd->segment, *next = t->next; + + _dm_target_erase(cd, t); + + while (next) { + t = next; + next = t->next; + _dm_target_erase(cd, t); + free(t); + } + + memset(&dmd->segment, 0, sizeof(dmd->segment)); +} + +int dm_targets_allocate(struct dm_target *first, unsigned count) +{ + if (!first || first->next || !count) + return -EINVAL; + + while (--count) { + first->next = malloc(sizeof(*first)); + if (!first->next) + return -ENOMEM; + memset(first->next, 0, sizeof(*first)); + first = first->next; + } + + return 0; +} + static int check_retry(struct crypt_device *cd, uint32_t *dmd_flags, uint32_t dmt_flags) { int ret = 0; @@ -1294,69 +1469,50 @@ int dm_create_device(struct crypt_device *cd, const char *name, const char *type, struct crypt_dm_active_device *dmd) { - char *table_params = NULL; - uint32_t dmd_flags, dmt_flags; + uint32_t dmt_flags = 0; int r = -EINVAL; - if (!type) + if (!type || !dmd) return -EINVAL; - if (dm_init_context(cd, dmd->target)) + if (dm_init_context(cd, dmd->segment.type)) return -ENOTSUP; - dmd_flags = dmd->flags; - dm_flags(cd, dmd->target, &dmt_flags); + r = _dm_create_device(cd, name, type, dmd->uuid, dmd); - if (dmd->target == DM_CRYPT) - table_params = get_dm_crypt_params(dmd, dmd_flags); - else if (dmd->target == DM_VERITY) - table_params = get_dm_verity_params(dmd->u.verity.vp, dmd, dmd_flags); - else if (dmd->target == DM_INTEGRITY) - table_params = get_dm_integrity_params(dmd, dmd_flags); - else + if (r < 0 && dm_flags(cd, dmd->segment.type, &dmt_flags)) goto out; - r = _dm_create_device(cd, name, type, dmd->data_device, dmd_flags, - dmd->uuid, dmd->size, dmd->target, table_params); - - if (r < 0 && dm_flags(cd, dmd->target, &dmt_flags)) - goto out; - - if (r && dmd->target == DM_CRYPT && check_retry(cd, &dmd_flags, dmt_flags)) { - crypt_safe_free(table_params); - table_params = get_dm_crypt_params(dmd, dmd_flags); - r = _dm_create_device(cd, name, type, dmd->data_device, dmd_flags, - dmd->uuid, dmd->size, dmd->target, table_params); - } + if (r && (dmd->segment.type == DM_CRYPT || dmd->segment.type == DM_LINEAR) && check_retry(cd, &dmd->flags, dmt_flags)) + r = _dm_create_device(cd, name, type, dmd->uuid, dmd); if (r == -EINVAL && - dmd_flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) && + dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) && !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) log_err(cd, _("Requested dm-crypt performance options are not supported.")); - if (r == -EINVAL && dmd_flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION| + if (r == -EINVAL && dmd->flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION| CRYPT_ACTIVATE_RESTART_ON_CORRUPTION| CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS| CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) && !(dmt_flags & DM_VERITY_ON_CORRUPTION_SUPPORTED)) log_err(cd, _("Requested dm-verity data corruption handling options are not supported.")); - if (r == -EINVAL && dmd->target == DM_VERITY && dmd->u.verity.fec_device && - !(dmt_flags & DM_VERITY_FEC_SUPPORTED)) + if (r == -EINVAL && dmd->segment.type == DM_VERITY && + dmd->segment.u.verity.fec_device && !(dmt_flags & DM_VERITY_FEC_SUPPORTED)) log_err(cd, _("Requested dm-verity FEC options are not supported.")); - if (r == -EINVAL && dmd->target == DM_CRYPT) { - if (dmd->u.crypt.integrity && !(dmt_flags & DM_INTEGRITY_SUPPORTED)) + if (r == -EINVAL && dmd->segment.type == DM_CRYPT) { + if (dmd->segment.u.crypt.integrity && !(dmt_flags & DM_INTEGRITY_SUPPORTED)) log_err(cd, _("Requested data integrity options are not supported.")); - if (dmd->u.crypt.sector_size != SECTOR_SIZE && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED)) + if (dmd->segment.u.crypt.sector_size != SECTOR_SIZE && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED)) log_err(cd, _("Requested sector_size option is not supported.")); } - if (r == -EINVAL && dmd->target == DM_INTEGRITY && (dmd_flags & CRYPT_ACTIVATE_RECALCULATE) && + if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) && !(dmt_flags & DM_INTEGRITY_RECALC_SUPPORTED)) log_err(cd, _("Requested automatic recalculation of integrity tags is not supported.")); out: - crypt_safe_free(table_params); dm_exit_context(); return r; } @@ -1364,34 +1520,24 @@ out: int dm_reload_device(struct crypt_device *cd, const char *name, struct crypt_dm_active_device *dmd, unsigned resume) { - char *table_params = NULL; + int r; uint32_t dmt_flags; - int r = -EINVAL; - if (dm_init_context(cd, dmd->target)) + if (!dmd) + return -EINVAL; + + if (dm_init_context(cd, dmd->segment.type)) return -ENOTSUP; - dm_flags(cd, dmd->target, &dmt_flags); + if (dm_flags(cd, DM_INTEGRITY, &dmt_flags) || !(dmt_flags & DM_INTEGRITY_RECALC_SUPPORTED)) + dmd->flags &= ~CRYPT_ACTIVATE_RECALCULATE; - if (dmd->target == DM_CRYPT) - table_params = get_dm_crypt_params(dmd, dmd->flags); - else if (dmd->target == DM_VERITY) - table_params = get_dm_verity_params(dmd->u.verity.vp, dmd, dmd->flags); - else if (dmd->target == DM_INTEGRITY) - table_params = get_dm_integrity_params(dmd, dmd->flags); - else - goto out; + r = _dm_reload_device(cd, name, dmd); - if (!table_params) - goto out; - - r = _dm_reload_device(cd, name, dmd->data_device, dmd->flags, dmd->size, - dmd->target, table_params); - - if (r == -EINVAL && dmd->target == DM_CRYPT) { + if (r == -EINVAL && (dmd->segment.type == DM_CRYPT || dmd->segment.type == DM_LINEAR)) { if ((dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)) && - !dm_flags(cd, DM_CRYPT, &dmt_flags) && !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) - log_err(cd, _("Requested dm-crypt performance options are not supported.")); + !dm_flags(cd, DM_CRYPT, &dmt_flags) && !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) + log_err(cd, _("Requested dmcrypt performance options are not supported.")); if ((dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) && !dm_flags(cd, DM_CRYPT, &dmt_flags) && !(dmt_flags & DM_DISCARDS_SUPPORTED)) log_err(cd, _("Discard/TRIM is not supported.")); @@ -1399,7 +1545,7 @@ int dm_reload_device(struct crypt_device *cd, const char *name, if (!r && resume) r = _dm_resume_device(name, dmd->flags); -out: + dm_exit_context(); return r; } @@ -1433,7 +1579,7 @@ static int dm_status_dmi(const char *name, struct dm_info *dmi, next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); - if (!target_type || start != 0 || next) + if (!target_type || start != 0) goto out; if (target && strcmp(target_type, target)) @@ -1442,7 +1588,8 @@ static int dm_status_dmi(const char *name, struct dm_info *dmi, /* for target == NULL check all supported */ if (!target && (strcmp(target_type, DM_CRYPT_TARGET) && strcmp(target_type, DM_VERITY_TARGET) && - strcmp(target_type, DM_INTEGRITY_TARGET))) + strcmp(target_type, DM_INTEGRITY_TARGET) && + strcmp(target_type, DM_LINEAR_TARGET))) goto out; r = 0; out: @@ -1550,11 +1697,9 @@ int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint } /* FIXME use hex wrapper, user val wrappers for line parsing */ -static int _dm_query_crypt(struct crypt_device *cd, - uint32_t get_flags, - struct dm_info *dmi, - char *params, - struct crypt_dm_active_device *dmd) +static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags, + char *params, struct dm_target *tgt, + uint32_t *act_flags) { uint64_t val64; char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc; @@ -1565,9 +1710,9 @@ static int _dm_query_crypt(struct crypt_device *cd, char *cipher = NULL, *integrity = NULL; struct volume_key *vk = NULL; - memset(dmd, 0, sizeof(*dmd)); - dmd->target = DM_CRYPT; - dmd->u.crypt.sector_size = SECTOR_SIZE; + tgt->type = DM_CRYPT; + tgt->direction = TARGET_QUERY; + tgt->u.crypt.sector_size = SECTOR_SIZE; r = -EINVAL; @@ -1583,7 +1728,7 @@ static int _dm_query_crypt(struct crypt_device *cd, goto err; params++; - dmd->u.crypt.iv_offset = val64; + tgt->u.crypt.iv_offset = val64; /* device */ rdevice = strsep(¶ms, " "); @@ -1601,9 +1746,9 @@ static int _dm_query_crypt(struct crypt_device *cd, if (!params) goto err; val64 = strtoull(params, ¶ms, 10); - dmd->u.crypt.offset = val64; + tgt->u.crypt.offset = val64; - dmd->u.crypt.tag_size = 0; + tgt->u.crypt.tag_size = 0; /* Features section, available since crypt target version 1.11 */ if (*params) { @@ -1622,19 +1767,19 @@ static int _dm_query_crypt(struct crypt_device *cd, goto err; arg = strsep(¶ms, " "); if (!strcasecmp(arg, "allow_discards")) - dmd->flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; else if (!strcasecmp(arg, "same_cpu_crypt")) - dmd->flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT; + *act_flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT; else if (!strcasecmp(arg, "submit_from_crypt_cpus")) - dmd->flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS; + *act_flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS; else if (sscanf(arg, "integrity:%u:", &val) == 1) { - dmd->u.crypt.tag_size = val; + tgt->u.crypt.tag_size = val; rintegrity = strchr(arg + strlen("integrity:"), ':'); if (!rintegrity) goto err; rintegrity++; } else if (sscanf(arg, "sector_size:%u", &val) == 1) { - dmd->u.crypt.sector_size = val; + tgt->u.crypt.sector_size = val; } else /* unknown option */ goto err; } @@ -1655,12 +1800,8 @@ static int _dm_query_crypt(struct crypt_device *cd, r = -EINVAL; - /* Never allow to return empty key */ - if ((get_flags & DM_ACTIVE_CRYPT_KEY) && dmi->suspended) - goto err; - if (key_[0] == ':') - dmd->flags |= CRYPT_ACTIVATE_KEYRING_KEY; + *act_flags |= CRYPT_ACTIVATE_KEYRING_KEY; if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) { /* we will trust kernel the key_string is in expected format */ @@ -1705,13 +1846,13 @@ static int _dm_query_crypt(struct crypt_device *cd, memset(key_, 0, strlen(key_)); if (cipher) - dmd->u.crypt.cipher = cipher; + tgt->u.crypt.cipher = cipher; if (integrity) - dmd->u.crypt.integrity = integrity; + tgt->u.crypt.integrity = integrity; if (data_device) - dmd->data_device = data_device; + tgt->data_device = data_device; if (vk) - dmd->u.crypt.vk = vk; + tgt->u.crypt.vk = vk; return 0; err: free(cipher); @@ -1721,11 +1862,11 @@ err: return r; } -static int _dm_query_verity(struct crypt_device *cd, - uint32_t get_flags, - struct dm_info *dmi, - char *params, - struct crypt_dm_active_device *dmd) +static int _dm_target_query_verity(struct crypt_device *cd, + uint32_t get_flags, + char *params, + struct dm_target *tgt, + uint32_t *act_flags) { struct crypt_params_verity *vp = NULL; uint32_t val32; @@ -1737,13 +1878,16 @@ static int _dm_query_verity(struct crypt_device *cd, struct device *data_device = NULL, *hash_device = NULL, *fec_device = NULL; char *hash_name = NULL, *root_hash = NULL, *salt = NULL, *fec_dev_str = NULL; - if (get_flags & DM_ACTIVE_VERITY_PARAMS) - vp = dmd->u.verity.vp; + if (get_flags & DM_ACTIVE_VERITY_PARAMS) { + vp = malloc(sizeof(*vp)); + if (!vp) + return -ENOMEM; + memset(vp, 0, sizeof(*vp)); + } - memset(dmd, 0, sizeof(*dmd)); - - dmd->target = DM_VERITY; - dmd->u.verity.vp = vp; + tgt->type = DM_VERITY; + tgt->direction = TARGET_QUERY; + tgt->u.verity.vp = vp; /* version */ val32 = strtoul(params, ¶ms, 10); @@ -1809,7 +1953,7 @@ static int _dm_query_verity(struct crypt_device *cd, val64 = strtoull(params, ¶ms, 10); if (*params != ' ') goto err; - dmd->u.verity.hash_offset = val64; + tgt->u.verity.hash_offset = val64; params++; /* hash algorithm */ @@ -1833,7 +1977,7 @@ static int _dm_query_verity(struct crypt_device *cd, r = len; goto err; } - dmd->u.verity.root_hash_size = len; + tgt->u.verity.root_hash_size = len; if (get_flags & DM_ACTIVE_VERITY_ROOT_HASH) root_hash = str2; else @@ -1873,13 +2017,13 @@ static int _dm_query_verity(struct crypt_device *cd, goto err; arg = strsep(¶ms, " "); if (!strcasecmp(arg, "ignore_corruption")) - dmd->flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION; + *act_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION; else if (!strcasecmp(arg, "restart_on_corruption")) - dmd->flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; + *act_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; else if (!strcasecmp(arg, "ignore_zero_blocks")) - dmd->flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; + *act_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; else if (!strcasecmp(arg, "check_at_most_once")) - dmd->flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE; + *act_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE; else if (!strcasecmp(arg, "use_fec_from_device")) { str = strsep(¶ms, " "); str2 = crypt_lookup_dev(str); @@ -1900,7 +2044,7 @@ static int _dm_query_verity(struct crypt_device *cd, val64 = strtoull(params, ¶ms, 10); if (*params) params++; - dmd->u.verity.fec_offset = val64; + tgt->u.verity.fec_offset = val64; if (vp) vp->fec_area_offset = val64 * vp->hash_block_size; i++; @@ -1908,7 +2052,7 @@ static int _dm_query_verity(struct crypt_device *cd, val64 = strtoull(params, ¶ms, 10); if (*params) params++; - dmd->u.verity.fec_blocks = val64; + tgt->u.verity.fec_blocks = val64; i++; } else if (!strcasecmp(arg, "fec_roots")) { val32 = strtoul(params, ¶ms, 10); @@ -1929,13 +2073,13 @@ static int _dm_query_verity(struct crypt_device *cd, } if (data_device) - dmd->data_device = data_device; + tgt->data_device = data_device; if (hash_device) - dmd->u.verity.hash_device = hash_device; + tgt->u.verity.hash_device = hash_device; if (fec_device) - dmd->u.verity.fec_device = fec_device; + tgt->u.verity.fec_device = fec_device; if (root_hash) - dmd->u.verity.root_hash = root_hash; + tgt->u.verity.root_hash = root_hash; if (vp && hash_name) vp->hash_name = hash_name; if (vp && salt) @@ -1951,14 +2095,15 @@ err: free(hash_name); free(salt); free(fec_dev_str); + free(vp); return r; } -static int _dm_query_integrity(struct crypt_device *cd, - uint32_t get_flags, - struct dm_info *dmi, - char *params, - struct crypt_dm_active_device *dmd) +static int _dm_target_query_integrity(struct crypt_device *cd, + uint32_t get_flags, + char *params, + struct dm_target *tgt, + uint32_t *act_flags) { uint32_t val32; uint64_t val64; @@ -1970,9 +2115,8 @@ static int _dm_query_integrity(struct crypt_device *cd, char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL; struct volume_key *vk = NULL; - memset(dmd, 0, sizeof(*dmd)); - - dmd->target = DM_INTEGRITY; + tgt->type = DM_INTEGRITY; + tgt->direction = TARGET_QUERY; /* data device */ str = strsep(¶ms, " "); @@ -1992,11 +2136,11 @@ static int _dm_query_integrity(struct crypt_device *cd, val64 = strtoull(params, ¶ms, 10); if (!*params || *params != ' ') goto err; - dmd->u.integrity.offset = val64; + tgt->u.integrity.offset = val64; /* tag size*/ val32 = strtoul(params, ¶ms, 10); - dmd->u.integrity.tag_size = val32; + tgt->u.integrity.tag_size = val32; if (!*params || *params != ' ') goto err; @@ -2005,11 +2149,11 @@ static int _dm_query_integrity(struct crypt_device *cd, if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R')) goto err; if (c == 'D') - dmd->flags |= CRYPT_ACTIVATE_NO_JOURNAL; + *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; if (c == 'R') - dmd->flags |= CRYPT_ACTIVATE_RECOVERY; + *act_flags |= CRYPT_ACTIVATE_RECOVERY; - dmd->u.integrity.sector_size = SECTOR_SIZE; + tgt->u.integrity.sector_size = SECTOR_SIZE; /* Features section */ if (params) { @@ -2026,17 +2170,17 @@ static int _dm_query_integrity(struct crypt_device *cd, goto err; arg = strsep(¶ms, " "); if (sscanf(arg, "journal_sectors:%u", &val) == 1) - dmd->u.integrity.journal_size = val * SECTOR_SIZE; + tgt->u.integrity.journal_size = val * SECTOR_SIZE; else if (sscanf(arg, "journal_watermark:%u", &val) == 1) - dmd->u.integrity.journal_watermark = val; + tgt->u.integrity.journal_watermark = val; else if (sscanf(arg, "commit_time:%u", &val) == 1) - dmd->u.integrity.journal_commit_time = val; + tgt->u.integrity.journal_commit_time = val; else if (sscanf(arg, "interleave_sectors:%u", &val) == 1) - dmd->u.integrity.interleave_sectors = val; + tgt->u.integrity.interleave_sectors = val; else if (sscanf(arg, "block_size:%u", &val) == 1) - dmd->u.integrity.sector_size = val; + tgt->u.integrity.sector_size = val; else if (sscanf(arg, "buffer_sectors:%u", &val) == 1) - dmd->u.integrity.buffer_sectors = val; + tgt->u.integrity.buffer_sectors = val; else if (!strncmp(arg, "internal_hash:", 14) && !integrity) { str = &arg[14]; arg = strsep(&str, ":"); @@ -2098,7 +2242,7 @@ static int _dm_query_integrity(struct crypt_device *cd, } } } else if (!strcmp(arg, "recalculate")) { - dmd->flags |= CRYPT_ACTIVATE_RECALCULATE; + *act_flags |= CRYPT_ACTIVATE_RECALCULATE; } else /* unknown option */ goto err; } @@ -2111,17 +2255,17 @@ static int _dm_query_integrity(struct crypt_device *cd, } if (data_device) - dmd->data_device = data_device; + tgt->data_device = data_device; if (meta_device) - dmd->u.integrity.meta_device = meta_device; + tgt->u.integrity.meta_device = meta_device; if (integrity) - dmd->u.integrity.integrity = integrity; + tgt->u.integrity.integrity = integrity; if (journal_crypt) - dmd->u.integrity.journal_crypt = journal_crypt; + tgt->u.integrity.journal_crypt = journal_crypt; if (journal_integrity) - dmd->u.integrity.journal_integrity = journal_integrity; + tgt->u.integrity.journal_integrity = journal_integrity; if (vk) - dmd->u.integrity.vk = vk; + tgt->u.integrity.vk = vk; return 0; err: device_free(cd, data_device); @@ -2133,9 +2277,78 @@ err: return r; } +static int _dm_target_query_linear(struct crypt_device *cd, struct dm_target *tgt, + uint32_t get_flags, char *params) +{ + uint64_t val64; + char *rdevice, *arg; + int r; + struct device *device = NULL; + + /* device */ + rdevice = strsep(¶ms, " "); + if (get_flags & DM_ACTIVE_DEVICE) { + arg = crypt_lookup_dev(rdevice); + r = device_alloc(cd, &device, arg); + free(arg); + if (r < 0 && r != -ENOTBLK) + return r; + } + + r = -EINVAL; + + /*offset */ + if (!params) + goto err; + val64 = strtoull(params, ¶ms, 10); + + /* params should be empty now */ + if (*params) + goto err; + + tgt->type = DM_LINEAR; + tgt->direction = TARGET_QUERY; + tgt->data_device = device; + tgt->u.linear.offset = val64; + + return 0; +err: + device_free(cd, device); + return r; +} + +/* + * on error retval has to be negative + * + * also currently any _dm_target_query fn does not perform cleanup on error + */ +static int dm_target_query(struct crypt_device *cd, struct dm_target *tgt, const uint64_t *start, + const uint64_t *length, const char *target_type, + char *params, uint32_t get_flags, uint32_t *act_flags) +{ + int r = -EINVAL; + + if (!strcmp(target_type, DM_CRYPT_TARGET)) + r = _dm_target_query_crypt(cd, get_flags, params, tgt, act_flags); + else if (!strcmp(target_type, DM_VERITY_TARGET)) + r = _dm_target_query_verity(cd, get_flags, params, tgt, act_flags); + else if (!strcmp(target_type, DM_INTEGRITY_TARGET)) + r = _dm_target_query_integrity(cd, get_flags, params, tgt, act_flags); + else if (!strcmp(target_type, DM_LINEAR_TARGET)) + r = _dm_target_query_linear(cd, tgt, get_flags, params); + + if (!r) { + tgt->offset = *start; + tgt->size = *length; + } + + return r; +} + int dm_query_device(struct crypt_device *cd, const char *name, uint32_t get_flags, struct crypt_dm_active_device *dmd) { + struct dm_target *t; struct dm_task *dmt; struct dm_info dmi; uint64_t start, length; @@ -2147,6 +2360,13 @@ int dm_query_device(struct crypt_device *cd, const char *name, if (dm_init_context(cd, DM_UNKNOWN)) return -ENOTSUP; + if (!dmd) + return -EINVAL; + + t = &dmd->segment; + + memset(dmd, 0, sizeof(*dmd)); + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) goto out; dm_flags(cd, DM_UNKNOWN, &dmt_flags); @@ -2167,33 +2387,41 @@ int dm_query_device(struct crypt_device *cd, const char *name, goto out; } - next = dm_get_next_target(dmt, next, &start, &length, - &target_type, ¶ms); - - if (!target_type || start != 0 || next) - goto out; - - if (!strcmp(target_type, DM_CRYPT_TARGET)) { - r = _dm_query_crypt(cd, get_flags, &dmi, params, dmd); - } else if (!strcmp(target_type, DM_VERITY_TARGET)) { - r = _dm_query_verity(cd, get_flags, &dmi, params, dmd); - if (r < 0) - goto out; - r = _dm_status_verity_ok(cd, name); - if (r < 0) - goto out; - if (r == 0) - dmd->flags |= CRYPT_ACTIVATE_CORRUPTED; - r = 0; - } else if (!strcmp(target_type, DM_INTEGRITY_TARGET)) { - r = _dm_query_integrity(cd, get_flags, &dmi, params, dmd); - } else + if (dmi.target_count <= 0) { r = -EINVAL; + goto out; + } - if (r < 0) + /* Never allow to return empty key */ + if ((get_flags & DM_ACTIVE_CRYPT_KEY) && dmi.suspended) { + log_dbg(cd, "Cannot read volume key while suspended."); + r = -EINVAL; + goto out; + } + + r = dm_targets_allocate(&dmd->segment, dmi.target_count); + if (r) goto out; - dmd->size = length; + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + r = dm_target_query(cd, t, &start, &length, target_type, params, get_flags, &dmd->flags); + if (!r && t->type == DM_VERITY) { + r = _dm_status_verity_ok(cd, name); + if (r == 0) + dmd->flags |= CRYPT_ACTIVATE_CORRUPTED; + } + + if (r < 0) { + log_err(cd, _("Failed to query dm-%s segment."), target_type); + goto out; + } + + dmd->size += length; + t = t->next; + } while (next); if (dmi.read_only) dmd->flags |= CRYPT_ACTIVATE_READONLY; @@ -2218,6 +2446,9 @@ out: if (dmt) dm_task_destroy(dmt); + if (r < 0) + dm_targets_free(cd, dmd); + dm_exit_context(); return r; } @@ -2366,3 +2597,125 @@ int dm_is_dm_kernel_name(const char *name) { return strncmp(name, "dm-", 3) ? 0 : 1; } + +int dm_crypt_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, struct volume_key *vk, const char *cipher, + size_t iv_offset, size_t data_offset, const char *integrity, uint32_t tag_size, + uint32_t sector_size) +{ + int r = -EINVAL; + + /* free on error */ + char *dm_integrity = NULL; + + if (tag_size) { + /* Space for IV metadata only */ + dm_integrity = strdup(integrity ?: "none"); + if (!dm_integrity) { + r = -ENOMEM; + goto err; + } + } + + tgt->data_device = data_device; + + tgt->type = DM_CRYPT; + tgt->u.crypt.vk = vk; + tgt->offset = seg_offset; + tgt->size = seg_size; + + tgt->u.crypt.cipher = cipher; + tgt->u.crypt.integrity = dm_integrity; + tgt->u.crypt.iv_offset = iv_offset; + tgt->u.crypt.offset = data_offset; + tgt->u.crypt.tag_size = tag_size; + tgt->u.crypt.sector_size = sector_size; + + return 0; +err: + free(dm_integrity); + + return r; +} + +int dm_verity_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, struct device *hash_device, struct device *fec_device, + const char *root_hash, uint32_t root_hash_size, uint64_t hash_offset_block, + uint64_t hash_blocks, struct crypt_params_verity *vp) +{ + if (!data_device || !hash_device || !vp) + return -EINVAL; + + tgt->type = DM_VERITY; + tgt->direction = TARGET_SET; + tgt->offset = seg_offset; + tgt->size = seg_size; + tgt->data_device = data_device; + + tgt->u.verity.hash_device = hash_device; + tgt->u.verity.fec_device = fec_device; + tgt->u.verity.root_hash = root_hash; + tgt->u.verity.root_hash_size = root_hash_size; + tgt->u.verity.hash_offset = hash_offset_block; + tgt->u.verity.fec_offset = vp->fec_area_offset / vp->hash_block_size; + tgt->u.verity.hash_blocks = hash_blocks; + tgt->u.verity.vp = vp; + + return 0; +} + +int dm_integrity_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *meta_device, + struct device *data_device, uint64_t tag_size, uint64_t offset, + uint32_t sector_size, struct volume_key *vk, + struct volume_key *journal_crypt_key, struct volume_key *journal_mac_key, + const struct crypt_params_integrity *ip) +{ + if (!data_device) + return -EINVAL; + + tgt->type = DM_INTEGRITY; + tgt->direction = TARGET_SET; + tgt->offset = seg_offset; + tgt->size = seg_size; + tgt->data_device = data_device; + if (meta_device != data_device) + tgt->u.integrity.meta_device = meta_device; + tgt->u.integrity.tag_size = tag_size; + tgt->u.integrity.offset = offset; + tgt->u.integrity.sector_size = sector_size; + + tgt->u.integrity.vk = vk; + tgt->u.integrity.journal_crypt_key = journal_crypt_key; + tgt->u.integrity.journal_integrity_key = journal_mac_key; + + if (ip) { + tgt->u.integrity.journal_size = ip->journal_size; + tgt->u.integrity.journal_watermark = ip->journal_watermark; + tgt->u.integrity.journal_commit_time = ip->journal_commit_time; + tgt->u.integrity.interleave_sectors = ip->interleave_sectors; + tgt->u.integrity.buffer_sectors = ip->buffer_sectors; + tgt->u.integrity.journal_integrity = ip->journal_integrity; + tgt->u.integrity.journal_crypt = ip->journal_crypt; + tgt->u.integrity.integrity = ip->integrity; + } + + return 0; +} + +int dm_linear_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, size_t data_offset) +{ + if (!data_device) + return -EINVAL; + + tgt->type = DM_LINEAR; + tgt->direction = TARGET_SET; + tgt->offset = seg_offset; + tgt->size = seg_size; + tgt->data_device = data_device; + + tgt->u.linear.offset = data_offset; + + return 0; +} diff --git a/lib/loopaes/loopaes.c b/lib/loopaes/loopaes.c index a5166e8e..4c26a331 100644 --- a/lib/loopaes/loopaes.c +++ b/lib/loopaes/loopaes.c @@ -203,25 +203,15 @@ int LOOPAES_activate(struct crypt_device *cd, struct volume_key *vk, uint32_t flags) { - char *cipher = NULL; - uint32_t req_flags, dmc_flags; int r; + uint32_t req_flags, dmc_flags; + char *cipher = NULL; struct crypt_dm_active_device dmd = { - .target = DM_CRYPT, - .size = 0, - .flags = flags, - .data_device = crypt_data_device(cd), - .u.crypt = { - .cipher = NULL, - .vk = vk, - .offset = crypt_get_data_offset(cd), - .iv_offset = crypt_get_iv_offset(cd), - .sector_size = crypt_get_sector_size(cd), - } + .flags = flags, }; - r = device_block_adjust(cd, dmd.data_device, DEV_EXCL, - dmd.u.crypt.offset, &dmd.size, &dmd.flags); + r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL, + crypt_get_data_offset(cd), &dmd.size, &dmd.flags); if (r) return r; @@ -235,9 +225,18 @@ int LOOPAES_activate(struct crypt_device *cd, if (r < 0) return -ENOMEM; - dmd.u.crypt.cipher = cipher; + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), + vk, cipher, crypt_get_iv_offset(cd), + crypt_get_data_offset(cd), crypt_get_integrity(cd), + crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + + if (r) { + free(cipher); + return r; + } + log_dbg(cd, "Trying to activate loop-AES device %s using cipher %s.", - name, dmd.u.crypt.cipher); + name, cipher); r = dm_create_device(cd, name, CRYPT_LOOPAES, &dmd); @@ -247,6 +246,8 @@ int LOOPAES_activate(struct crypt_device *cd, r = -ENOTSUP; } + dm_targets_free(cd, &dmd); free(cipher); + return r; } diff --git a/lib/luks1/keyencryption.c b/lib/luks1/keyencryption.c index 0551eaa0..7b134942 100644 --- a/lib/luks1/keyencryption.c +++ b/lib/luks1/keyencryption.c @@ -58,25 +58,15 @@ static int LUKS_endec_template(char *src, size_t srcLength, char name[PATH_MAX], path[PATH_MAX]; char cipher_spec[MAX_CIPHER_LEN * 3]; struct crypt_dm_active_device dmd = { - .target = DM_CRYPT, - .uuid = NULL, - .flags = CRYPT_ACTIVATE_PRIVATE, - .data_device = crypt_metadata_device(ctx), - .u.crypt = { - .cipher = cipher_spec, - .vk = vk, - .offset = sector, - .iv_offset = 0, - .sector_size = SECTOR_SIZE, - } + .flags = CRYPT_ACTIVATE_PRIVATE, }; int r, devfd = -1; size_t bsize, keyslot_alignment, alignment; log_dbg(ctx, "Using dmcrypt to access keyslot area."); - bsize = device_block_size(ctx, dmd.data_device); - alignment = device_alignment(dmd.data_device); + bsize = device_block_size(ctx, crypt_metadata_device(ctx)); + alignment = device_alignment(crypt_metadata_device(ctx)); if (!bsize || !alignment) return -EINVAL; @@ -96,26 +86,33 @@ static int LUKS_endec_template(char *src, size_t srcLength, if (snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", cipher, cipher_mode) < 0) return -ENOMEM; - r = device_block_adjust(ctx, dmd.data_device, DEV_OK, - dmd.u.crypt.offset, &dmd.size, &dmd.flags); + r = device_block_adjust(ctx, crypt_metadata_device(ctx), DEV_OK, + sector, &dmd.size, &dmd.flags); if (r < 0) { log_err(ctx, _("Device %s doesn't exist or access denied."), - device_path(dmd.data_device)); + device_path(crypt_metadata_device(ctx))); return -EIO; } if (mode != O_RDONLY && dmd.flags & CRYPT_ACTIVATE_READONLY) { log_err(ctx, _("Cannot write to device %s, permission denied."), - device_path(dmd.data_device)); + device_path(crypt_metadata_device(ctx))); return -EACCES; } + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, + crypt_metadata_device(ctx), vk, cipher_spec, 0, sector, + NULL, 0, SECTOR_SIZE); + if (r) + goto out; + r = dm_create_device(ctx, name, "TEMP", &dmd); if (r < 0) { if (r != -EACCES && r != -ENOTSUP) - _error_hint(ctx, device_path(dmd.data_device), + _error_hint(ctx, device_path(crypt_metadata_device(ctx)), cipher, cipher_mode, vk->keylength * 8); - return -EIO; + r = -EIO; + goto out; } devfd = open(path, mode | O_DIRECT | O_SYNC); @@ -132,6 +129,7 @@ static int LUKS_endec_template(char *src, size_t srcLength, } else r = 0; out: + dm_targets_free(ctx, &dmd); if (devfd != -1) close(devfd); dm_remove_device(ctx, name, CRYPT_DEACTIVATE_FORCE); diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index 08bced07..91d83fcc 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -1156,22 +1156,22 @@ int LUKS1_activate(struct crypt_device *cd, struct volume_key *vk, uint32_t flags) { + int r; struct crypt_dm_active_device dmd = { - .target = DM_CRYPT, - .uuid = crypt_get_uuid(cd), - .flags = flags, - .size = 0, - .data_device = crypt_data_device(cd), - .u.crypt = { - .cipher = crypt_get_cipher_spec(cd), - .vk = vk, - .offset = crypt_get_data_offset(cd), - .iv_offset = 0, - .sector_size = crypt_get_sector_size(cd), - } + .flags = flags, + .uuid = crypt_get_uuid(cd), }; - return create_or_reload_device(cd, name, CRYPT_LUKS1, &dmd); + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), + vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), + crypt_get_data_offset(cd), crypt_get_integrity(cd), + crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + if (!r) + r = create_or_reload_device(cd, name, CRYPT_LUKS1, &dmd); + + dm_targets_free(cd, &dmd); + + return r; } int LUKS_wipe_header_areas(struct luks_phdr *hdr, diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c index d674fb5d..9b78738c 100644 --- a/lib/luks2/luks2_json_metadata.c +++ b/lib/luks2/luks2_json_metadata.c @@ -1861,33 +1861,28 @@ int LUKS2_activate(struct crypt_device *cd, { int r; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); - struct crypt_dm_active_device dmdi, dmd = { - .target = DM_CRYPT, + struct crypt_dm_active_device dmdi = {}, dmd = { .uuid = crypt_get_uuid(cd), - .size = 0, - .data_device = crypt_data_device(cd), - .u.crypt = { - .vk = vk, - .offset = crypt_get_data_offset(cd), - .cipher = LUKS2_get_cipher(hdr, CRYPT_DEFAULT_SEGMENT), - .integrity = crypt_get_integrity(cd), - .iv_offset = 0, - .tag_size = crypt_get_integrity_tag_size(cd), - .sector_size = crypt_get_sector_size(cd) - } }; /* do not allow activation when particular requirements detected */ if ((r = LUKS2_unmet_requirements(cd, hdr, 0, 0))) return r; + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), + vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), + crypt_get_data_offset(cd), crypt_get_integrity(cd) ?: "none", + crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + if (r < 0) + return r; + /* Add persistent activation flags */ if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT)) LUKS2_config_get_flags(cd, hdr, &dmd.flags); dmd.flags |= flags; - if (dmd.u.crypt.tag_size) { + if (crypt_get_integrity_tag_size(cd)) { if (!LUKS2_integrity_compatible(hdr)) { log_err(cd, "Unsupported device integrity configuration."); return -EINVAL; @@ -1897,17 +1892,17 @@ int LUKS2_activate(struct crypt_device *cd, if (r) return r; - /* Space for IV metadata only */ - if (!dmd.u.crypt.integrity) - dmd.u.crypt.integrity = "none"; + dmd.segment.u.crypt.offset = 0; + dmd.segment.size = dmdi.segment.size; - dmd.u.crypt.offset = 0; - dmd.size = dmdi.size; + r = create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi); + } else + r = create_or_reload_device(cd, name, CRYPT_LUKS2, &dmd); - return create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi); - } + dm_targets_free(cd, &dmd); + dm_targets_free(cd, &dmdi); - return create_or_reload_device(cd, name, CRYPT_LUKS2, &dmd); + return r; } int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet) diff --git a/lib/setup.c b/lib/setup.c index 50ba741a..e13b5db9 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -452,7 +452,7 @@ static int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid) */ static int crypt_uuid_type_cmp(struct crypt_device *cd, const char *type) { - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; size_t len; int r; @@ -483,24 +483,26 @@ int PLAIN_activate(struct crypt_device *cd, uint64_t size, uint32_t flags) { + int r; struct crypt_dm_active_device dmd = { - .target = DM_CRYPT, - .size = size, - .flags = flags, - .data_device = crypt_data_device(cd), - .u.crypt = { - .cipher = crypt_get_cipher_spec(cd), - .vk = vk, - .offset = crypt_get_data_offset(cd), - .iv_offset = crypt_get_iv_offset(cd), - .sector_size = crypt_get_sector_size(cd), - } + .flags = flags, + .size = size, }; log_dbg(cd, "Trying to activate PLAIN device %s using cipher %s.", - name, dmd.u.crypt.cipher); + name, crypt_get_cipher_spec(cd)); - return create_or_reload_device(cd, name, CRYPT_PLAIN, &dmd); + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), + vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd), + crypt_get_data_offset(cd), crypt_get_integrity(cd), + crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd)); + if (r < 0) + return r; + + r = create_or_reload_device(cd, name, CRYPT_PLAIN, &dmd); + + dm_targets_free(cd, &dmd); + return r; } int crypt_confirm(struct crypt_device *cd, const char *msg) @@ -980,9 +982,10 @@ int crypt_load(struct crypt_device *cd, */ static int _init_by_name_crypt_none(struct crypt_device *cd) { - char _mode[MAX_CIPHER_LEN]; - struct crypt_dm_active_device dmd = {}; int r; + char _mode[MAX_CIPHER_LEN]; + struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; if (cd->type || !cd->u.none.active_name) return -EINVAL; @@ -990,8 +993,12 @@ static int _init_by_name_crypt_none(struct crypt_device *cd) r = dm_query_device(cd, cd->u.none.active_name, DM_ACTIVE_CRYPT_CIPHER | DM_ACTIVE_CRYPT_KEYSIZE, &dmd); + if (r < 0) + return r; + if (!single_segment(&dmd) || tgt->type != DM_CRYPT) + r = -EINVAL; if (r >= 0) - r = crypt_parse_name_and_mode(dmd.u.crypt.cipher, + r = crypt_parse_name_and_mode(tgt->u.crypt.cipher, cd->u.none.cipher, NULL, _mode); @@ -999,12 +1006,10 @@ static int _init_by_name_crypt_none(struct crypt_device *cd) snprintf(cd->u.none.cipher_spec, sizeof(cd->u.none.cipher_spec), "%s-%s", cd->u.none.cipher, _mode); cd->u.none.cipher_mode = cd->u.none.cipher_spec + strlen(cd->u.none.cipher) + 1; - cd->u.none.key_size = dmd.u.crypt.vk->keylength; + cd->u.none.key_size = tgt->u.crypt.vk->keylength; } - crypt_free_volume_key(dmd.u.crypt.vk); - free(CONST_CAST(void*)dmd.u.crypt.cipher); - free(CONST_CAST(void*)dmd.u.crypt.integrity); + dm_targets_free(cd, &dmd); return r; } @@ -1059,10 +1064,11 @@ static void crypt_free_type(struct crypt_device *cd) static int _init_by_name_crypt(struct crypt_device *cd, const char *name) { - struct crypt_dm_active_device dmd = {}, dmdi = {}; char *cipher_spec = NULL, cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; const char *namei; int key_nums, r; + struct crypt_dm_active_device dmd, dmdi = {}; + struct dm_target *tgt = &dmd.segment, *tgti = &dmdi.segment; r = dm_query_device(cd, name, DM_ACTIVE_DEVICE | @@ -1070,24 +1076,34 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) DM_ACTIVE_CRYPT_CIPHER | DM_ACTIVE_CRYPT_KEYSIZE, &dmd); if (r < 0) - goto out; + return r; - r = crypt_parse_name_and_mode(dmd.u.crypt.cipher, cipher, + if (!single_segment(&dmd) || tgt->type != DM_CRYPT) { + log_dbg(cd, "Unsupported device table detected in %s.", name); + r = -EINVAL; + goto out; + } + + r = crypt_parse_name_and_mode(tgt->u.crypt.cipher, cipher, &key_nums, cipher_mode); if (r < 0) { log_dbg(cd, "Cannot parse cipher and mode from active device."); goto out; } - if (dmd.u.crypt.integrity && (namei = device_dm_name(dmd.data_device))) { + if (tgt->u.crypt.integrity && (namei = device_dm_name(tgt->data_device))) { r = dm_query_device(cd, namei, DM_ACTIVE_DEVICE, &dmdi); if (r < 0) goto out; - if (dmdi.target == DM_INTEGRITY && !cd->metadata_device) { + if (!single_segment(&dmdi) || tgti->type != DM_INTEGRITY) { + log_dbg(cd, "Unsupported device table detected in %s.", namei); + r = -EINVAL; + goto out; + } + if (!cd->metadata_device) { device_free(cd, cd->device); - cd->device = dmdi.data_device; - } else - device_free(cd, dmdi.data_device); + MOVE_REF(cd->device, tgti->data_device); + } } if (asprintf(&cipher_spec, "%s-%s", cipher, cipher_mode) < 0) { @@ -1098,24 +1114,22 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) if (isPLAIN(cd->type)) { cd->u.plain.hdr.hash = NULL; /* no way to get this */ - cd->u.plain.hdr.offset = dmd.u.crypt.offset; - cd->u.plain.hdr.skip = dmd.u.crypt.iv_offset; - cd->u.plain.hdr.sector_size = dmd.u.crypt.sector_size; - cd->u.plain.key_size = dmd.u.crypt.vk->keylength; + cd->u.plain.hdr.offset = tgt->u.crypt.offset; + cd->u.plain.hdr.skip = tgt->u.crypt.iv_offset; + cd->u.plain.hdr.sector_size = tgt->u.crypt.sector_size; + cd->u.plain.key_size = tgt->u.crypt.vk->keylength; cd->u.plain.cipher = strdup(cipher); - cd->u.plain.cipher_spec = cipher_spec; - cd->u.plain.cipher_mode = cipher_spec + strlen(cipher) + 1; - cipher_spec = NULL; + MOVE_REF(cd->u.plain.cipher_spec, cipher_spec); + cd->u.plain.cipher_mode = cd->u.plain.cipher_spec + strlen(cipher) + 1; } else if (isLOOPAES(cd->type)) { - cd->u.loopaes.hdr.offset = dmd.u.crypt.offset; + cd->u.loopaes.hdr.offset = tgt->u.crypt.offset; cd->u.loopaes.cipher = strdup(cipher); - cd->u.loopaes.cipher_spec = cipher_spec; - cd->u.loopaes.cipher_mode = cipher_spec + strlen(cipher) + 1; - cipher_spec = NULL; + MOVE_REF(cd->u.loopaes.cipher_spec, cipher_spec); + cd->u.loopaes.cipher_mode = cd->u.loopaes.cipher_spec + strlen(cipher) + 1; /* version 3 uses last key for IV */ - if (dmd.u.crypt.vk->keylength % key_nums) + if (tgt->u.crypt.vk->keylength % key_nums) key_nums++; - cd->u.loopaes.key_size = dmd.u.crypt.vk->keylength / key_nums; + cd->u.loopaes.key_size = tgt->u.crypt.vk->keylength / key_nums; } else if (isLUKS1(cd->type) || isLUKS2(cd->type)) { if (crypt_metadata_device(cd)) { r = _crypt_load_luks(cd, cd->type, 0, 0); @@ -1140,14 +1154,12 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) r = 0; } } else if (isTCRYPT(cd->type)) { - r = TCRYPT_init_by_name(cd, name, &dmd, &cd->device, + r = TCRYPT_init_by_name(cd, name, dmd.uuid, tgt, &cd->device, &cd->u.tcrypt.params, &cd->u.tcrypt.hdr); } out: - crypt_free_volume_key(dmd.u.crypt.vk); - device_free(cd, dmd.data_device); - free(CONST_CAST(void*)dmd.u.crypt.cipher); - free(CONST_CAST(void*)dmd.u.crypt.integrity); + dm_targets_free(cd, &dmd); + dm_targets_free(cd, &dmdi); free(CONST_CAST(void*)dmd.uuid); free(cipher_spec); return r; @@ -1155,104 +1167,92 @@ out: static int _init_by_name_verity(struct crypt_device *cd, const char *name) { - struct crypt_params_verity params = {}; - struct crypt_dm_active_device dmd = { - .target = DM_VERITY, - .u.verity.vp = ¶ms, - }; - int r, verity_type = 0; + struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; + int r; r = dm_query_device(cd, name, DM_ACTIVE_DEVICE | DM_ACTIVE_VERITY_HASH_DEVICE | DM_ACTIVE_VERITY_PARAMS, &dmd); if (r < 0) + return r; + if (!single_segment(&dmd) || tgt->type != DM_VERITY) { + log_dbg(cd, "Unsupported device table detected in %s.", name); + r = -EINVAL; goto out; + } if (r > 0) r = 0; if (isVERITY(cd->type)) { cd->u.verity.uuid = NULL; // FIXME cd->u.verity.hdr.flags = CRYPT_VERITY_NO_HEADER; //FIXME - cd->u.verity.hdr.data_size = params.data_size; - cd->u.verity.root_hash_size = dmd.u.verity.root_hash_size; - cd->u.verity.root_hash = NULL; - cd->u.verity.hdr.hash_name = params.hash_name; + cd->u.verity.hdr.data_size = tgt->u.verity.vp->data_size; + cd->u.verity.root_hash_size = tgt->u.verity.root_hash_size; + MOVE_REF(cd->u.verity.hdr.hash_name, tgt->u.verity.vp->hash_name); cd->u.verity.hdr.data_device = NULL; cd->u.verity.hdr.hash_device = NULL; - cd->u.verity.hdr.data_block_size = params.data_block_size; - cd->u.verity.hdr.hash_block_size = params.hash_block_size; - cd->u.verity.hdr.hash_area_offset = dmd.u.verity.hash_offset; - cd->u.verity.hdr.fec_area_offset = dmd.u.verity.fec_offset; - cd->u.verity.hdr.hash_type = params.hash_type; - cd->u.verity.hdr.flags = params.flags; - cd->u.verity.hdr.salt_size = params.salt_size; - cd->u.verity.hdr.salt = params.salt; - cd->u.verity.hdr.fec_device = params.fec_device; - cd->u.verity.hdr.fec_roots = params.fec_roots; - cd->u.verity.fec_device = dmd.u.verity.fec_device; - cd->metadata_device = dmd.u.verity.hash_device; - verity_type = 1; + cd->u.verity.hdr.data_block_size = tgt->u.verity.vp->data_block_size; + cd->u.verity.hdr.hash_block_size = tgt->u.verity.vp->hash_block_size; + cd->u.verity.hdr.hash_area_offset = tgt->u.verity.hash_offset; + cd->u.verity.hdr.fec_area_offset = tgt->u.verity.fec_offset; + cd->u.verity.hdr.hash_type = tgt->u.verity.vp->hash_type; + cd->u.verity.hdr.flags = tgt->u.verity.vp->flags; + cd->u.verity.hdr.salt_size = tgt->u.verity.vp->salt_size; + MOVE_REF(cd->u.verity.hdr.salt, tgt->u.verity.vp->salt); + MOVE_REF(cd->u.verity.hdr.fec_device, tgt->u.verity.vp->fec_device); + cd->u.verity.hdr.fec_roots = tgt->u.verity.vp->fec_roots; + MOVE_REF(cd->u.verity.fec_device, tgt->u.verity.fec_device); + MOVE_REF(cd->metadata_device, tgt->u.verity.hash_device); } out: - if (!verity_type) { - free(CONST_CAST(void*)params.hash_name); - free(CONST_CAST(void*)params.salt); - free(CONST_CAST(void*)params.fec_device); - } - device_free(cd, dmd.data_device); + dm_targets_free(cd, &dmd); return r; } static int _init_by_name_integrity(struct crypt_device *cd, const char *name) { - struct crypt_dm_active_device dmd = { - .target = DM_INTEGRITY, - }; - int r, integrity_type = 0; + struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; + int r; r = dm_query_device(cd, name, DM_ACTIVE_DEVICE | DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_INTEGRITY_PARAMS, &dmd); if (r < 0) + return r; + if (!single_segment(&dmd) || tgt->type != DM_INTEGRITY) { + log_dbg(cd, "Unsupported device table detected in %s.", name); + r = -EINVAL; goto out; + } if (r > 0) r = 0; if (isINTEGRITY(cd->type)) { - cd->u.integrity.params.tag_size = dmd.u.integrity.tag_size; - cd->u.integrity.params.sector_size = dmd.u.integrity.sector_size; - cd->u.integrity.params.journal_size = dmd.u.integrity.journal_size; - cd->u.integrity.params.journal_watermark = dmd.u.integrity.journal_watermark; - cd->u.integrity.params.journal_commit_time = dmd.u.integrity.journal_commit_time; - cd->u.integrity.params.interleave_sectors = dmd.u.integrity.interleave_sectors; - cd->u.integrity.params.buffer_sectors = dmd.u.integrity.buffer_sectors; - cd->u.integrity.params.integrity = dmd.u.integrity.integrity; - cd->u.integrity.params.journal_integrity = dmd.u.integrity.journal_integrity; - cd->u.integrity.params.journal_crypt = dmd.u.integrity.journal_crypt; + cd->u.integrity.params.tag_size = tgt->u.integrity.tag_size; + cd->u.integrity.params.sector_size = tgt->u.integrity.sector_size; + cd->u.integrity.params.journal_size = tgt->u.integrity.journal_size; + cd->u.integrity.params.journal_watermark = tgt->u.integrity.journal_watermark; + cd->u.integrity.params.journal_commit_time = tgt->u.integrity.journal_commit_time; + cd->u.integrity.params.interleave_sectors = tgt->u.integrity.interleave_sectors; + cd->u.integrity.params.buffer_sectors = tgt->u.integrity.buffer_sectors; + MOVE_REF(cd->u.integrity.params.integrity, tgt->u.integrity.integrity); + MOVE_REF(cd->u.integrity.params.journal_integrity, tgt->u.integrity.journal_integrity); + MOVE_REF(cd->u.integrity.params.journal_crypt, tgt->u.integrity.journal_crypt); - if (dmd.u.integrity.vk) - cd->u.integrity.params.integrity_key_size = dmd.u.integrity.vk->keylength; - if (dmd.u.integrity.journal_integrity_key) - cd->u.integrity.params.journal_integrity_key_size = dmd.u.integrity.journal_integrity_key->keylength; - if (dmd.u.integrity.journal_crypt_key) - cd->u.integrity.params.integrity_key_size = dmd.u.integrity.journal_crypt_key->keylength; - - cd->metadata_device = dmd.u.integrity.meta_device; - - integrity_type = 1; + if (tgt->u.integrity.vk) + cd->u.integrity.params.integrity_key_size = tgt->u.integrity.vk->keylength; + if (tgt->u.integrity.journal_integrity_key) + cd->u.integrity.params.journal_integrity_key_size = tgt->u.integrity.journal_integrity_key->keylength; + if (tgt->u.integrity.journal_crypt_key) + cd->u.integrity.params.integrity_key_size = tgt->u.integrity.journal_crypt_key->keylength; + MOVE_REF(cd->metadata_device, tgt->u.integrity.meta_device); } out: - if (!integrity_type) { - free(CONST_CAST(void*)dmd.u.integrity.integrity); - free(CONST_CAST(void*)dmd.u.integrity.journal_integrity); - free(CONST_CAST(void*)dmd.u.integrity.journal_crypt); - } - crypt_free_volume_key(dmd.u.integrity.vk); - crypt_free_volume_key(dmd.u.integrity.journal_integrity_key); - crypt_free_volume_key(dmd.u.integrity.journal_crypt_key); - device_free(cd, dmd.data_device); + dm_targets_free(cd, &dmd); return r; } @@ -1261,7 +1261,8 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, const char *header_device) { crypt_status_info ci; - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; int r; if (!cd || !name) @@ -1280,26 +1281,28 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, r = dm_query_device(NULL, name, DM_ACTIVE_DEVICE | DM_ACTIVE_UUID, &dmd); if (r < 0) + return r; + if (!single_segment(&dmd)) { + log_dbg(NULL, "Unsupported device table detected in %s.", name); + r = -EINVAL; goto out; + } *cd = NULL; if (header_device) { r = crypt_init(cd, header_device); } else { - r = crypt_init(cd, device_path(dmd.data_device)); + r = crypt_init(cd, device_path(tgt->data_device)); /* Underlying device disappeared but mapping still active */ - if (!dmd.data_device || r == -ENOTBLK) + if (!tgt->data_device || r == -ENOTBLK) log_verbose(NULL, _("Underlying device for crypt device %s disappeared."), name); /* Underlying device is not readable but crypt mapping exists */ - if (r == -ENOTBLK) { - device_free(NULL, dmd.data_device); - dmd.data_device = NULL; + if (r == -ENOTBLK) r = crypt_init(cd, NULL); - } } if (r < 0) @@ -1326,18 +1329,18 @@ int crypt_init_by_name_and_header(struct crypt_device **cd, log_dbg(NULL, "Active device has no UUID set, some parameters are not set."); if (header_device) { - r = crypt_set_data_device(*cd, device_path(dmd.data_device)); + r = crypt_set_data_device(*cd, device_path(tgt->data_device)); if (r < 0) goto out; } /* Try to initialise basic parameters from active device */ - if (dmd.target == DM_CRYPT) + if (tgt->type == DM_CRYPT) r = _init_by_name_crypt(*cd, name); - else if (dmd.target == DM_VERITY) + else if (tgt->type == DM_VERITY) r = _init_by_name_verity(*cd, name); - else if (dmd.target == DM_INTEGRITY) + else if (tgt->type == DM_INTEGRITY) r = _init_by_name_integrity(*cd, name); out: if (r < 0) { @@ -1348,8 +1351,8 @@ out: (*cd)->u.none.active_name = strdup(name); } - device_free(NULL, dmd.data_device); free(CONST_CAST(void*)dmd.uuid); + dm_targets_free(NULL, &dmd); return r; } @@ -2172,7 +2175,7 @@ static int _strcmp_null(const char *a, const char *b) return strcmp(a, b); } -static int _compare_crypt_devices(struct crypt_device *cd, +static int _compare_device_types(struct crypt_device *cd, const struct crypt_dm_active_device *src, const struct crypt_dm_active_device *tgt) { @@ -2181,23 +2184,39 @@ static int _compare_crypt_devices(struct crypt_device *cd, return -EINVAL; } - /* UUID checks */ - if (strncmp(cd->type, tgt->uuid, strlen(cd->type))) { - log_dbg(cd, "Unexpected uuid prefix %s in target device.", tgt->uuid); - return -EINVAL; + if (isLUKS2(cd->type) && !src->uuid) { + if (strncmp("INTEGRITY-", tgt->uuid, strlen("INTEGRITY-"))) { + log_dbg(cd, "Unexpected uuid prefix %s in target integrity device.", tgt->uuid); + return -EINVAL; + } + } else if (isLUKS(cd->type)) { + if (!src->uuid || strncmp(cd->type, tgt->uuid, strlen(cd->type)) || + crypt_uuid_cmp(tgt->uuid, src->uuid)) { + log_dbg(cd, "LUKS UUID mismatch."); + return -EINVAL; + } + } else if (isPLAIN(cd->type) || isLOOPAES(cd->type)) { + if (strncmp(cd->type, tgt->uuid, strlen(cd->type))) { + log_dbg(cd, "Unexpected uuid prefix %s in target device.", tgt->uuid); + return -EINVAL; + } + } else { + log_dbg(cd, "Unsupported device type %s for reload.", cd->type ?: ""); + return -ENOTSUP; } - /* Only LUKS devices support full UUID string */ - if (isLUKS(cd->type) && (!src->uuid || crypt_uuid_cmp(tgt->uuid, src->uuid))) { - log_dbg(cd, "UUID mismatch."); - return -EINVAL; - } + return 0; +} +static int _compare_crypt_devices(struct crypt_device *cd, + const struct dm_target *src, + const struct dm_target *tgt) +{ /* for crypt devices keys are mandatory */ if (!src->u.crypt.vk || !tgt->u.crypt.vk) return -EINVAL; - if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->flags & CRYPT_ACTIVATE_KEYRING_KEY)) { + if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) { log_dbg(cd, "Keys in context and target device do not match."); return -EINVAL; } @@ -2231,8 +2250,8 @@ static int _compare_crypt_devices(struct crypt_device *cd, } static int _compare_integrity_devices(struct crypt_device *cd, - const struct crypt_dm_active_device *src, - const struct crypt_dm_active_device *tgt) + const struct dm_target *src, + const struct dm_target *tgt) { /* * some parameters may be implicit (and set in dm-integrity ctor) @@ -2244,17 +2263,6 @@ static int _compare_integrity_devices(struct crypt_device *cd, * interleave_sectors */ - if (!tgt->uuid) { - log_dbg(cd, "Missing device uuid in target device."); - return -EINVAL; - } - - /* UUID checks */ - if (strncmp("INTEGRITY-", tgt->uuid, strlen("INTEGRITY-"))) { - log_dbg(cd, "Unexpected uuid prefix %s in target device.", tgt->uuid); - return -EINVAL; - } - /* check remaining integer values that makes sense */ if (src->u.integrity.tag_size != tgt->u.integrity.tag_size || src->u.integrity.offset != tgt->u.integrity.offset || @@ -2299,27 +2307,57 @@ static int _compare_dm_devices(struct crypt_device *cd, const struct crypt_dm_active_device *src, const struct crypt_dm_active_device *tgt) { - /* target types must match */ - if (src->target != tgt->target) { - log_dbg(cd, "type mismatch."); + int r; + const struct dm_target *s, *t; + + if (!src || !tgt) return -EINVAL; + + r = _compare_device_types(cd, src, tgt); + if (r) + return r; + + s = &src->segment; + t = &tgt->segment; + + while (s || t) { + if (!s || !t) { + log_dbg(cd, "segments count mismatch."); + return -EINVAL; + } + if (s->type != t->type) { + log_dbg(cd, "segment type mismatch."); + r = -EINVAL; + break; + } + + switch (s->type) { + case DM_CRYPT: + r = _compare_crypt_devices(cd, s, t); + break; + case DM_INTEGRITY: + r = _compare_integrity_devices(cd, s, t); + break; + default: + r = -ENOTSUP; + } + + if (r) + break; + + s = s->next; + t = t->next; } - switch (src->target) { - case DM_CRYPT: - return _compare_crypt_devices(cd, src, tgt); - case DM_INTEGRITY: - return _compare_integrity_devices(cd, src, tgt); - default: - return -ENOTSUP; - } + return r; } static int _reload_device(struct crypt_device *cd, const char *name, struct crypt_dm_active_device *sdmd) { int r; - struct crypt_dm_active_device tdmd = {}; + struct crypt_dm_active_device tdmd; + struct dm_target *src, *tgt = &tdmd.segment; if (!cd || !cd->type || !name || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) return -EINVAL; @@ -2332,7 +2370,7 @@ static int _reload_device(struct crypt_device *cd, const char *name, return -EINVAL; } - if (tdmd.target != DM_CRYPT || tdmd.u.crypt.tag_size) { + if (!single_segment(&tdmd) || tgt->type != DM_CRYPT || tgt->u.crypt.tag_size) { r = -ENOTSUP; log_err(cd, _("Unsupported parameters on device %s."), name); goto out; @@ -2344,6 +2382,8 @@ static int _reload_device(struct crypt_device *cd, const char *name, goto out; } + src = &sdmd->segment; + /* Changing read only flag for active device makes no sense */ if (tdmd.flags & CRYPT_ACTIVATE_READONLY) sdmd->flags |= CRYPT_ACTIVATE_READONLY; @@ -2351,34 +2391,29 @@ static int _reload_device(struct crypt_device *cd, const char *name, sdmd->flags &= ~CRYPT_ACTIVATE_READONLY; if (sdmd->flags & CRYPT_ACTIVATE_KEYRING_KEY) { - r = crypt_volume_key_set_description(tdmd.u.crypt.vk, sdmd->u.crypt.vk->key_description); + r = crypt_volume_key_set_description(tgt->u.crypt.vk, src->u.crypt.vk->key_description); if (r) goto out; } else { - crypt_free_volume_key(tdmd.u.crypt.vk); - tdmd.u.crypt.vk = crypt_alloc_volume_key(sdmd->u.crypt.vk->keylength, sdmd->u.crypt.vk->key); - if (!tdmd.u.crypt.vk) { + crypt_free_volume_key(tgt->u.crypt.vk); + tgt->u.crypt.vk = crypt_alloc_volume_key(src->u.crypt.vk->keylength, src->u.crypt.vk->key); + if (!tgt->u.crypt.vk) { r = -ENOMEM; goto out; } } - r = device_block_adjust(cd, sdmd->data_device, DEV_OK, - sdmd->u.crypt.offset, &sdmd->size, NULL); + r = device_block_adjust(cd, src->data_device, DEV_OK, + src->u.crypt.offset, &sdmd->size, NULL); if (r) goto out; tdmd.flags = sdmd->flags; - tdmd.size = sdmd->size; + tgt->size = tdmd.size = sdmd->size; r = dm_reload_device(cd, name, &tdmd, 1); out: - if (tdmd.target == DM_CRYPT) { - crypt_free_volume_key(tdmd.u.crypt.vk); - free(CONST_CAST(void*)tdmd.u.crypt.cipher); - free(CONST_CAST(void*)tdmd.u.crypt.integrity); - } - device_free(cd, tdmd.data_device); + dm_targets_free(cd, &tdmd); free(CONST_CAST(void*)tdmd.uuid); return r; @@ -2392,7 +2427,8 @@ static int _reload_device_with_integrity(struct crypt_device *cd, struct crypt_dm_active_device *sdmdi) { int r; - struct crypt_dm_active_device tdmd = {}, tdmdi = {}; + struct crypt_dm_active_device tdmd, tdmdi = {}; + struct dm_target *src, *srci, *tgt = &tdmd.segment, *tgti = &tdmdi.segment; struct device *data_device = NULL; if (!cd || !cd->type || !name || !iname || !(sdmd->flags & CRYPT_ACTIVATE_REFRESH)) @@ -2406,7 +2442,7 @@ static int _reload_device_with_integrity(struct crypt_device *cd, return -EINVAL; } - if (tdmd.target != DM_CRYPT || !tdmd.u.crypt.tag_size) { + if (!single_segment(&tdmd) || tgt->type != DM_CRYPT || !tgt->u.crypt.tag_size) { r = -ENOTSUP; log_err(cd, _("Unsupported parameters on device %s."), name); goto out; @@ -2419,7 +2455,7 @@ static int _reload_device_with_integrity(struct crypt_device *cd, goto out; } - if (tdmdi.target != DM_INTEGRITY) { + if (!single_segment(&tdmdi) || tgti->type != DM_INTEGRITY) { r = -ENOTSUP; log_err(cd, _("Unsupported parameters on device %s."), iname); goto out; @@ -2431,16 +2467,19 @@ static int _reload_device_with_integrity(struct crypt_device *cd, goto out; } + src = &sdmd->segment; + srci = &sdmdi->segment; + r = device_alloc(cd, &data_device, ipath); if (r < 0) goto out; - r = device_block_adjust(cd, sdmdi->data_device, DEV_OK, - sdmdi->u.integrity.offset, &sdmdi->size, NULL); + r = device_block_adjust(cd, srci->data_device, DEV_OK, + srci->u.integrity.offset, &sdmdi->size, NULL); if (r) goto out; - sdmd->data_device = data_device; + src->data_device = data_device; r = _compare_dm_devices(cd, sdmd, &tdmd); if (r) { @@ -2460,20 +2499,20 @@ static int _reload_device_with_integrity(struct crypt_device *cd, sdmdi->flags &= ~CRYPT_ACTIVATE_READONLY; if (sdmd->flags & CRYPT_ACTIVATE_KEYRING_KEY) { - r = crypt_volume_key_set_description(tdmd.u.crypt.vk, sdmd->u.crypt.vk->key_description); + r = crypt_volume_key_set_description(tgt->u.crypt.vk, src->u.crypt.vk->key_description); if (r) goto out; } else { - crypt_free_volume_key(tdmd.u.crypt.vk); - tdmd.u.crypt.vk = crypt_alloc_volume_key(sdmd->u.crypt.vk->keylength, sdmd->u.crypt.vk->key); - if (!tdmd.u.crypt.vk) { + crypt_free_volume_key(tgt->u.crypt.vk); + tgt->u.crypt.vk = crypt_alloc_volume_key(src->u.crypt.vk->keylength, src->u.crypt.vk->key); + if (!tgt->u.crypt.vk) { r = -ENOMEM; goto out; } } - r = device_block_adjust(cd, sdmd->data_device, DEV_OK, - sdmd->u.crypt.offset, &sdmd->size, NULL); + r = device_block_adjust(cd, src->data_device, DEV_OK, + src->u.crypt.offset, &sdmd->size, NULL); if (r) goto out; @@ -2532,23 +2571,9 @@ err_clear: if (dm_status_suspended(cd, iname) > 0) dm_resume_device(cd, iname, 0); out: - if (tdmd.target == DM_CRYPT) { - crypt_free_volume_key(tdmd.u.crypt.vk); - free(CONST_CAST(void*)tdmd.u.crypt.cipher); - free(CONST_CAST(void*)tdmd.u.crypt.integrity); - } - if (tdmdi.target == DM_INTEGRITY) { - free(CONST_CAST(void*)tdmdi.u.integrity.integrity); - crypt_free_volume_key(tdmdi.u.integrity.vk); - free(CONST_CAST(void*)tdmdi.u.integrity.journal_integrity); - crypt_free_volume_key(tdmdi.u.integrity.journal_integrity_key); - free(CONST_CAST(void*)tdmdi.u.integrity.journal_crypt); - crypt_free_volume_key(tdmdi.u.integrity.journal_crypt_key); - device_free(cd, tdmdi.u.integrity.meta_device); - } - device_free(cd, tdmdi.data_device); + dm_targets_free(cd, &tdmd); + dm_targets_free(cd, &tdmdi); free(CONST_CAST(void*)tdmdi.uuid); - device_free(cd, tdmd.data_device); free(CONST_CAST(void*)tdmd.uuid); device_free(cd, data_device); @@ -2557,7 +2582,8 @@ out: int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) { - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmdq, dmd = {}; + struct dm_target *tgt = &dmdq.segment; int r; /* @@ -2571,23 +2597,18 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size); - r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY, &dmd); + r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY, &dmdq); if (r < 0) { log_err(cd, _("Device %s is not active."), name); return -EINVAL; } - - if (dmd.target != DM_CRYPT) { + if (!single_segment(&dmdq) || tgt->type != DM_CRYPT) { + log_dbg(cd, "Unsupported device table detected in %s.", name); r = -EINVAL; goto out; } - dmd.uuid = crypt_get_uuid(cd); - dmd.data_device = crypt_data_device(cd); - dmd.u.crypt.cipher = crypt_get_cipher_spec(cd); - dmd.u.crypt.integrity = crypt_get_integrity(cd); - - if ((dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_key_in_keyring(cd)) { + if ((dmdq.flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_key_in_keyring(cd)) { r = -EPERM; goto out; } @@ -2598,11 +2619,11 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) goto out; } r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, - dmd.u.crypt.vk, CRYPT_DEFAULT_SEGMENT); + tgt->u.crypt.vk, CRYPT_DEFAULT_SEGMENT); if (r) goto out; - dmd.flags |= CRYPT_ACTIVATE_KEYRING_KEY; + dmdq.flags |= CRYPT_ACTIVATE_KEYRING_KEY; } if (crypt_loop_device(crypt_get_device_name(cd))) { @@ -2613,25 +2634,34 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) log_err(cd, _("Cannot resize loop device.")); } - r = device_block_adjust(cd, dmd.data_device, DEV_OK, - dmd.u.crypt.offset, &new_size, &dmd.flags); + r = device_block_adjust(cd, crypt_data_device(cd), DEV_OK, + crypt_get_data_offset(cd), &new_size, &dmdq.flags); if (r) goto out; - if (MISALIGNED(new_size, dmd.u.crypt.sector_size >> SECTOR_SHIFT)) { + if (MISALIGNED(new_size, tgt->u.crypt.sector_size >> SECTOR_SHIFT)) { log_err(cd, _("Device %s size is not aligned to requested sector size (%u bytes)."), - crypt_get_device_name(cd), (unsigned)dmd.u.crypt.sector_size); + crypt_get_device_name(cd), (unsigned)tgt->u.crypt.sector_size); r = -EINVAL; goto out; } - if (new_size == dmd.size) { + dmd.uuid = crypt_get_uuid(cd); + dmd.size = new_size; + dmd.flags = dmdq.flags | CRYPT_ACTIVATE_REFRESH; + r = dm_crypt_target_set(&dmd.segment, 0, new_size, crypt_data_device(cd), + tgt->u.crypt.vk, crypt_get_cipher_spec(cd), + crypt_get_iv_offset(cd), crypt_get_data_offset(cd), + crypt_get_integrity(cd), crypt_get_integrity_tag_size(cd), + crypt_get_sector_size(cd)); + if (r < 0) + goto out; + + if (new_size == dmdq.size) { log_dbg(cd, "Device has already requested size %" PRIu64 - " sectors.", dmd.size); + " sectors.", dmdq.size); r = 0; } else { - dmd.size = new_size; - dmd.flags |= CRYPT_ACTIVATE_REFRESH; if (isTCRYPT(cd->type)) r = -ENOTSUP; else if (isLUKS2(cd->type)) @@ -2640,8 +2670,8 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) r = _reload_device(cd, name, &dmd); } out: - if (dmd.target == DM_CRYPT) - crypt_free_volume_key(dmd.u.crypt.vk); + dm_targets_free(cd, &dmd); + dm_targets_free(cd, &dmdq); return r; } @@ -2799,21 +2829,20 @@ void crypt_free(struct crypt_device *cd) static char *crypt_get_device_key_description(struct crypt_device *cd, const char *name) { - char *tmp = NULL; + char *desc = NULL; struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; if (dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmd) < 0) return NULL; - if (dmd.target == DM_CRYPT) { - if ((dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) && dmd.u.crypt.vk->key_description) - tmp = strdup(dmd.u.crypt.vk->key_description); - crypt_free_volume_key(dmd.u.crypt.vk); - } else if (dmd.target == DM_INTEGRITY) { - crypt_free_volume_key(dmd.u.integrity.vk); - } + if (single_segment(&dmd) && tgt->type == DM_CRYPT && + (dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) && tgt->u.crypt.vk->key_description) + desc = strdup(tgt->u.crypt.vk->key_description); - return tmp; + dm_targets_free(cd, &dmd); + + return desc; } int crypt_suspend(struct crypt_device *cd, @@ -3464,8 +3493,16 @@ static int _create_device_with_integrity(struct crypt_device *cd, { int r; enum devcheck device_check; + struct dm_target *tgt; struct device *device = NULL; + if (!single_segment(dmd)) + return -EINVAL; + + tgt = &dmd->segment; + if (tgt->type != DM_CRYPT) + return -EINVAL; + device_check = dmd->flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL; r = INTEGRITY_activate_dmd_device(cd, iname, dmdi); @@ -3475,10 +3512,10 @@ static int _create_device_with_integrity(struct crypt_device *cd, r = device_alloc(cd, &device, ipath); if (r < 0) goto out; - dmd->data_device = device; + tgt->data_device = device; - r = device_block_adjust(cd, dmd->data_device, device_check, - dmd->u.crypt.offset, &dmd->size, &dmd->flags); + r = device_block_adjust(cd, tgt->data_device, device_check, + tgt->u.crypt.offset, &dmd->size, &dmd->flags); if (!r) r = dm_create_device(cd, name, type, dmd); @@ -3495,8 +3532,13 @@ int create_or_reload_device(struct crypt_device *cd, const char *name, { int r; enum devcheck device_check; + struct dm_target *tgt; - if (!type || !name || !dmd) + if (!type || !name || !single_segment(dmd)) + return -EINVAL; + + tgt = &dmd->segment; + if (tgt->type != DM_CRYPT) return -EINVAL; /* drop CRYPT_ACTIVATE_REFRESH flag if any device is inactive */ @@ -3509,10 +3551,12 @@ int create_or_reload_device(struct crypt_device *cd, const char *name, else { device_check = dmd->flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL; - r = device_block_adjust(cd, dmd->data_device, device_check, - dmd->u.crypt.offset, &dmd->size, &dmd->flags); - if (!r) + r = device_block_adjust(cd, tgt->data_device, device_check, + tgt->u.crypt.offset, &dmd->size, &dmd->flags); + if (!r) { + tgt->size = dmd->size; r = dm_create_device(cd, name, type, dmd); + } } return r; @@ -3899,6 +3943,7 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t const char *namei = NULL; struct crypt_dm_active_device dmd = {}; int r; + struct dm_target *tgt = &dmd.segment; uint32_t get_flags = DM_ACTIVE_DEVICE | DM_ACTIVE_HOLDERS; if (!name) @@ -3927,8 +3972,8 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t r = -EBUSY; break; } - if (isLUKS2(cd->type) && crypt_get_integrity_tag_size(cd)) - namei = device_dm_name(dmd.data_device); + if (isLUKS2(cd->type) && single_segment(&dmd) && tgt->type == DM_CRYPT && crypt_get_integrity_tag_size(cd)) + namei = device_dm_name(tgt->data_device); } key_desc = crypt_get_device_key_description(cd, name); @@ -3957,7 +4002,7 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t r = -EINVAL; } - device_free(cd, dmd.data_device); + dm_targets_free(cd, &dmd); crypt_free(fake_cd); return r; @@ -3971,9 +4016,10 @@ int crypt_deactivate(struct crypt_device *cd, const char *name) int crypt_get_active_device(struct crypt_device *cd, const char *name, struct crypt_active_device *cad) { - struct crypt_dm_active_device dmd = {}, dmdi = {}; - const char *namei = NULL; int r; + struct crypt_dm_active_device dmd, dmdi = {}; + const char *namei = NULL; + struct dm_target *tgt = &dmd.segment; if (!cd || !name || !cad) return -EINVAL; @@ -3982,30 +4028,42 @@ int crypt_get_active_device(struct crypt_device *cd, const char *name, if (r < 0) return r; - if (dmd.target != DM_CRYPT && - dmd.target != DM_VERITY && - dmd.target != DM_INTEGRITY) - return -ENOTSUP; + if (!single_segment(&dmd)) { + log_dbg(cd, "Unexpected multi-segment device detected."); + r = -ENOTSUP; + goto out; + } + + if (tgt->type != DM_CRYPT && + tgt->type != DM_VERITY && + tgt->type != DM_INTEGRITY) { + r = -ENOTSUP; + goto out; + } /* For LUKS2 with integrity we need flags from underlying dm-integrity */ if (isLUKS2(cd->type) && crypt_get_integrity_tag_size(cd)) { - namei = device_dm_name(dmd.data_device); + namei = device_dm_name(tgt->data_device); if (namei && dm_query_device(cd, namei, 0, &dmdi) >= 0) dmd.flags |= dmdi.flags; } - device_free(cd, dmd.data_device); if (cd && isTCRYPT(cd->type)) { cad->offset = TCRYPT_get_data_offset(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params); cad->iv_offset = TCRYPT_get_iv_offset(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params); - } else if (dmd.target == DM_CRYPT) { - cad->offset = dmd.u.crypt.offset; - cad->iv_offset = dmd.u.crypt.iv_offset; + } else if (tgt->type == DM_CRYPT) { + cad->offset = tgt->u.crypt.offset; + cad->iv_offset = tgt->u.crypt.iv_offset; } cad->size = dmd.size; cad->flags = dmd.flags; - return 0; + r = 0; +out: + dm_targets_free(cd, &dmd); + dm_targets_free(cd, &dmdi); + + return r; } uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd, const char *name) @@ -4020,11 +4078,12 @@ uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd, const char if (dm_query_device(cd, name, 0, &dmd) < 0) return 0; - if (dmd.target == DM_INTEGRITY && - !dm_status_integrity_failures(cd, name, &failures)) - return failures; + if (single_segment(&dmd) && dmd.segment.type == DM_INTEGRITY) + (void)dm_status_integrity_failures(cd, name, &failures); - return 0; + dm_targets_free(cd, &dmd); + + return failures; } /* diff --git a/lib/tcrypt/tcrypt.c b/lib/tcrypt/tcrypt.c index ba6f4e35..8ecf6a56 100644 --- a/lib/tcrypt/tcrypt.c +++ b/lib/tcrypt/tcrypt.c @@ -723,24 +723,18 @@ int TCRYPT_activate(struct crypt_device *cd, struct crypt_params_tcrypt *params, uint32_t flags) { - char cipher[MAX_CIPHER_LEN], dm_name[PATH_MAX], dm_dev_name[PATH_MAX]; + char dm_name[PATH_MAX], dm_dev_name[PATH_MAX], cipher_spec[MAX_CIPHER_LEN*2+1]; char *part_path; - struct device *device = NULL, *part_device = NULL; unsigned int i; int r; uint32_t req_flags, dmc_flags; struct tcrypt_algs *algs; enum devcheck device_check; + uint64_t offset = crypt_get_data_offset(cd); + struct volume_key *vk = NULL; + struct device *ptr_dev = crypt_data_device(cd), *device = NULL, *part_device = NULL; struct crypt_dm_active_device dmd = { - .target = DM_CRYPT, - .size = 0, - .data_device = crypt_data_device(cd), - .u.crypt = { - .cipher = cipher, - .offset = crypt_get_data_offset(cd), - .iv_offset = crypt_get_iv_offset(cd), - .sector_size = crypt_get_sector_size(cd), - } + .flags = flags }; if (!hdr->d.version) { @@ -784,15 +778,15 @@ int TCRYPT_activate(struct crypt_device *cd, device_check = DEV_EXCL; if ((params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) && - !crypt_dev_is_partition(device_path(dmd.data_device))) { - part_path = crypt_get_partition_device(device_path(dmd.data_device), - dmd.u.crypt.offset, dmd.size); + !crypt_dev_is_partition(device_path(crypt_data_device(cd)))) { + part_path = crypt_get_partition_device(device_path(crypt_data_device(cd)), + crypt_get_data_offset(cd), dmd.size); if (part_path) { if (!device_alloc(cd, &part_device, part_path)) { log_verbose(cd, _("Activating TCRYPT system encryption for partition %s."), part_path); - dmd.data_device = part_device; - dmd.u.crypt.offset = 0; + ptr_dev = part_device; + offset = 0; } free(part_path); } else @@ -803,19 +797,17 @@ int TCRYPT_activate(struct crypt_device *cd, device_check = DEV_OK; } - r = device_block_adjust(cd, dmd.data_device, device_check, - dmd.u.crypt.offset, &dmd.size, &dmd.flags); - if (r) { - device_free(cd, part_device); - return r; - } + r = device_block_adjust(cd, ptr_dev, device_check, + offset, &dmd.size, &dmd.flags); + if (r) + goto out; /* From here, key size for every cipher must be the same */ - dmd.u.crypt.vk = crypt_alloc_volume_key(algs->cipher[0].key_size + - algs->cipher[0].key_extra_size, NULL); - if (!dmd.u.crypt.vk) { - device_free(cd, part_device); - return -ENOMEM; + vk = crypt_alloc_volume_key(algs->cipher[0].key_size + + algs->cipher[0].key_extra_size, NULL); + if (!vk) { + r = -ENOMEM; + goto out; } for (i = algs->chain_count; i > 0; i--) { @@ -828,11 +820,8 @@ int TCRYPT_activate(struct crypt_device *cd, dmd.flags = flags | CRYPT_ACTIVATE_PRIVATE; } - snprintf(cipher, sizeof(cipher), "%s-%s", - algs->cipher[i-1].name, algs->mode); - TCRYPT_copy_key(&algs->cipher[i-1], algs->mode, - dmd.u.crypt.vk->key, hdr->d.keys); + vk->key, hdr->d.keys); if (algs->chain_count != i) { snprintf(dm_dev_name, sizeof(dm_dev_name), "%s/%s_%d", @@ -840,14 +829,29 @@ int TCRYPT_activate(struct crypt_device *cd, r = device_alloc(cd, &device, dm_dev_name); if (r) break; - dmd.data_device = device; - dmd.u.crypt.offset = 0; + ptr_dev = device; + offset = 0; } + r = snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", algs->cipher[i-1].name, algs->mode); + if (r < 0 || (size_t)r >= sizeof(cipher_spec)) { + r = -ENOMEM; + break; + } + + r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, ptr_dev, vk, + cipher_spec, crypt_get_iv_offset(cd), offset, + crypt_get_integrity(cd), + crypt_get_integrity_tag_size(cd), + crypt_get_sector_size(cd)); + if (r) + break; + log_dbg(cd, "Trying to activate TCRYPT device %s using cipher %s.", - dm_name, dmd.u.crypt.cipher); + dm_name, dmd.segment.u.crypt.cipher); r = dm_create_device(cd, dm_name, CRYPT_TCRYPT, &dmd); + dm_targets_free(cd, &dmd); device_free(cd, device); device = NULL; @@ -861,15 +865,17 @@ int TCRYPT_activate(struct crypt_device *cd, r = -ENOTSUP; } +out: + crypt_free_volume_key(vk); + device_free(cd, device); device_free(cd, part_device); - crypt_free_volume_key(dmd.u.crypt.vk); return r; } static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, const char *base_uuid, int index, uint32_t flags) { - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; char dm_name[PATH_MAX]; int r; @@ -890,7 +896,7 @@ static int TCRYPT_remove_one(struct crypt_device *cd, const char *name, int TCRYPT_deactivate(struct crypt_device *cd, const char *name, uint32_t flags) { - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; int r; r = dm_query_device(cd, name, DM_ACTIVE_UUID, &dmd); @@ -908,8 +914,6 @@ int TCRYPT_deactivate(struct crypt_device *cd, const char *name, uint32_t flags) goto out; r = TCRYPT_remove_one(cd, name, dmd.uuid, 2, flags); - if (r < 0) - goto out; out: free(CONST_CAST(void*)dmd.uuid); return (r == -ENODEV) ? 0 : r; @@ -920,7 +924,8 @@ static int TCRYPT_status_one(struct crypt_device *cd, const char *name, size_t *key_size, char *cipher, uint64_t *data_offset, struct device **device) { - struct crypt_dm_active_device dmd = {}; + struct crypt_dm_active_device dmd; + struct dm_target *tgt = &dmd.segment; char dm_name[PATH_MAX], *c; int r; @@ -935,30 +940,35 @@ static int TCRYPT_status_one(struct crypt_device *cd, const char *name, DM_ACTIVE_UUID | DM_ACTIVE_CRYPT_CIPHER | DM_ACTIVE_CRYPT_KEYSIZE, &dmd); - if (r > 0) - r = 0; - if (!r && !strncmp(dmd.uuid, base_uuid, strlen(base_uuid))) { - if ((c = strchr(dmd.u.crypt.cipher, '-'))) - *c = '\0'; - strcat(cipher, "-"); - strncat(cipher, dmd.u.crypt.cipher, MAX_CIPHER_LEN); - *key_size += dmd.u.crypt.vk->keylength; - *data_offset = dmd.u.crypt.offset * SECTOR_SIZE; - device_free(cd, *device); - *device = dmd.data_device; - } else { - device_free(cd, dmd.data_device); - r = -ENODEV; + if (r < 0) + return r; + if (!single_segment(&dmd) || tgt->type != DM_CRYPT) { + r = -ENOTSUP; + goto out; } + r = 0; + + if (!strncmp(dmd.uuid, base_uuid, strlen(base_uuid))) { + if ((c = strchr(tgt->u.crypt.cipher, '-'))) + *c = '\0'; + strcat(cipher, "-"); + strncat(cipher, tgt->u.crypt.cipher, MAX_CIPHER_LEN); + *key_size += tgt->u.crypt.vk->keylength; + *data_offset = tgt->u.crypt.offset * SECTOR_SIZE; + device_free(cd, *device); + MOVE_REF(*device, tgt->data_device); + } else + r = -ENODEV; +out: + dm_targets_free(cd, &dmd); free(CONST_CAST(void*)dmd.uuid); - free(CONST_CAST(void*)dmd.u.crypt.cipher); - crypt_free_volume_key(dmd.u.crypt.vk); return r; } int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, - const struct crypt_dm_active_device *dmd, + const char *uuid, + const struct dm_target *tgt, struct device **device, struct crypt_params_tcrypt *tcrypt_params, struct tcrypt_phdr *tcrypt_hdr) @@ -971,9 +981,9 @@ int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, memset(tcrypt_params, 0, sizeof(*tcrypt_params)); memset(tcrypt_hdr, 0, sizeof(*tcrypt_hdr)); tcrypt_hdr->d.sector_size = SECTOR_SIZE; - tcrypt_hdr->d.mk_offset = dmd->u.crypt.offset * SECTOR_SIZE; + tcrypt_hdr->d.mk_offset = tgt->u.crypt.offset * SECTOR_SIZE; - strncpy(cipher, dmd->u.crypt.cipher, MAX_CIPHER_LEN); + strncpy(cipher, tgt->u.crypt.cipher, MAX_CIPHER_LEN); tmp = strchr(cipher, '-'); if (!tmp) return -EINVAL; @@ -981,11 +991,11 @@ int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, mode[MAX_CIPHER_LEN] = '\0'; strncpy(mode, ++tmp, MAX_CIPHER_LEN); - key_size = dmd->u.crypt.vk->keylength; - r = TCRYPT_status_one(cd, name, dmd->uuid, 1, &key_size, + key_size = tgt->u.crypt.vk->keylength; + r = TCRYPT_status_one(cd, name, uuid, 1, &key_size, cipher, &tcrypt_hdr->d.mk_offset, device); if (!r) - r = TCRYPT_status_one(cd, name, dmd->uuid, 2, &key_size, + r = TCRYPT_status_one(cd, name, uuid, 2, &key_size, cipher, &tcrypt_hdr->d.mk_offset, device); if (r < 0 && r != -ENODEV) diff --git a/lib/tcrypt/tcrypt.h b/lib/tcrypt/tcrypt.h index ff554fe9..a95dd4cd 100644 --- a/lib/tcrypt/tcrypt.h +++ b/lib/tcrypt/tcrypt.h @@ -75,6 +75,7 @@ struct tcrypt_phdr { struct crypt_device; struct crypt_params_tcrypt; struct crypt_dm_active_device; +struct dm_target; struct volume_key; struct device; @@ -83,7 +84,8 @@ int TCRYPT_read_phdr(struct crypt_device *cd, struct crypt_params_tcrypt *params); int TCRYPT_init_by_name(struct crypt_device *cd, const char *name, - const struct crypt_dm_active_device *dmd, + const char *uuid, + const struct dm_target *tgt, struct device **device, struct crypt_params_tcrypt *tcrypt_params, struct tcrypt_phdr *tcrypt_hdr); diff --git a/lib/utils_dm.h b/lib/utils_dm.h index a607d8bb..8f7f48ba 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -31,6 +31,7 @@ struct crypt_device; struct volume_key; struct crypt_params_verity; struct device; +struct crypt_params_integrity; /* Device mapper backend - kernel support flags */ #define DM_KEY_WIPE_SUPPORTED (1 << 0) /* key wipe message */ @@ -51,7 +52,8 @@ struct device; #define DM_DEFERRED_SUPPORTED (1 << 15) /* deferred removal of device */ #define DM_INTEGRITY_RECALC_SUPPORTED (1 << 16) /* dm-integrity automatic recalculation supported */ -typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_UNKNOWN } dm_target_type; +typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_LINEAR, DM_UNKNOWN } dm_target_type; +enum tdirection { TARGET_SET = 1, TARGET_QUERY }; int dm_flags(struct crypt_device *cd, dm_target_type target, uint32_t *flags); @@ -69,13 +71,12 @@ int dm_flags(struct crypt_device *cd, dm_target_type target, uint32_t *flags); #define DM_ACTIVE_INTEGRITY_PARAMS (1 << 9) -struct crypt_dm_active_device { - dm_target_type target; - uint64_t size; /* active device size */ - uint32_t flags; /* activation flags */ - const char *uuid; +struct dm_target { + dm_target_type type; + enum tdirection direction; + uint64_t offset; + uint64_t size; struct device *data_device; - unsigned holders:1; union { struct { const char *cipher; @@ -125,12 +126,53 @@ struct crypt_dm_active_device { struct device *meta_device; } integrity; + struct { + uint64_t offset; + } linear; } u; + + char *params; + struct dm_target *next; }; +struct crypt_dm_active_device { + uint64_t size; /* active device size */ + uint32_t flags; /* activation flags */ + const char *uuid; + + unsigned holders:1; /* device holders detected (on query only) */ + + struct dm_target segment; +}; + +static inline bool single_segment(const struct crypt_dm_active_device *dmd) +{ + return dmd && !dmd->segment.next; +} + void dm_backend_init(struct crypt_device *cd); void dm_backend_exit(struct crypt_device *cd); +int dm_targets_allocate(struct dm_target *first, unsigned count); +void dm_targets_free(struct crypt_device *cd, struct crypt_dm_active_device *dmd); + +int dm_crypt_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, struct volume_key *vk, const char *cipher, + size_t iv_offset, size_t data_offset, const char *integrity, + uint32_t tag_size, uint32_t sector_size); +int dm_verity_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, struct device *hash_device, struct device *fec_device, + const char *root_hash, uint32_t root_hash_size, uint64_t hash_offset_block, + uint64_t hash_blocks, struct crypt_params_verity *vp); +int dm_integrity_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *meta_device, + struct device *data_device, uint64_t tag_size, uint64_t offset, uint32_t sector_size, + struct volume_key *vk, + struct volume_key *journal_crypt_key, struct volume_key *journal_mac_key, + const struct crypt_params_integrity *ip); +int dm_linear_target_set(struct dm_target *tgt, size_t seg_offset, size_t seg_size, + struct device *data_device, size_t data_offset); + int dm_remove_device(struct crypt_device *cd, const char *name, uint32_t flags); int dm_status_device(struct crypt_device *cd, const char *name); int dm_status_suspended(struct crypt_device *cd, const char *name); diff --git a/lib/verity/verity.c b/lib/verity/verity.c index cd8ffb0e..6b2abf24 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -243,10 +243,14 @@ int VERITY_activate(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, uint32_t activation_flags) { - struct crypt_dm_active_device dmd; uint32_t dmv_flags; unsigned int fec_errors = 0; int r; + struct crypt_dm_active_device dmd = { + .size = verity_hdr->data_size * verity_hdr->data_block_size / 512, + .flags = activation_flags, + .uuid = crypt_get_uuid(cd), + }; log_dbg(cd, "Trying to activate VERITY device %s using hash %s.", name ?: "[none]", verity_hdr->hash_name); @@ -272,50 +276,48 @@ int VERITY_activate(struct crypt_device *cd, if (!name) return 0; - dmd.target = DM_VERITY; - dmd.data_device = crypt_data_device(cd); - dmd.u.verity.hash_device = crypt_metadata_device(cd); - dmd.u.verity.fec_device = fec_device; - dmd.u.verity.root_hash = root_hash; - dmd.u.verity.root_hash_size = root_hash_size; - dmd.u.verity.hash_offset = VERITY_hash_offset_block(verity_hdr); - dmd.u.verity.fec_offset = verity_hdr->fec_area_offset / verity_hdr->hash_block_size; - dmd.u.verity.hash_blocks = VERITY_hash_blocks(cd, verity_hdr); - dmd.flags = activation_flags; - dmd.size = verity_hdr->data_size * verity_hdr->data_block_size / 512; - dmd.uuid = crypt_get_uuid(cd); - dmd.u.verity.vp = verity_hdr; - - r = device_block_adjust(cd, dmd.u.verity.hash_device, DEV_OK, + r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK, 0, NULL, NULL); if (r) return r; - r = device_block_adjust(cd, dmd.data_device, DEV_EXCL, + r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL, 0, &dmd.size, &dmd.flags); if (r) return r; - if (dmd.u.verity.fec_device) { - r = device_block_adjust(cd, dmd.u.verity.fec_device, DEV_OK, + if (fec_device) { + r = device_block_adjust(cd, fec_device, DEV_OK, 0, NULL, NULL); if (r) return r; } + r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), + crypt_metadata_device(cd), fec_device, root_hash, + root_hash_size, VERITY_hash_offset_block(verity_hdr), + VERITY_hash_blocks(cd, verity_hdr), verity_hdr); + + if (r) + return r; + r = dm_create_device(cd, name, CRYPT_VERITY, &dmd); if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) { log_err(cd, _("Kernel doesn't support dm-verity mapping.")); - return -ENOTSUP; + r = -ENOTSUP; } if (r < 0) - return r; + goto out; r = dm_status_verity_ok(cd, name); if (r < 0) - return r; + goto out; if (!r) log_err(cd, _("Verity device detected corruption after activation.")); - return 0; + + r = 0; +out: + dm_targets_free(cd, &dmd); + return r; }