From 537d3bb974d2a39cef5003dff4c60029a4c7fbfb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 12 Feb 2021 11:53:49 -0800 Subject: [PATCH] ANDROID: dm: sync inline crypto support with patches going upstream Replace the following patches with upstream versions (well, almost upstream; as of 2021-02-12 they are queued for 5.12 at https://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm.git/log/?h=for-next): ANDROID-dm-add-support-for-passing-through-inline-crypto-support.patch ANDROID-dm-enable-may_passthrough_inline_crypto-on-some-targets.patch ANDROID-block-Introduce-passthrough-keyslot-manager.patch Also, resolve conflicts with the following non-upstream patches for hardware-wrapped key support. Notably, we need to handle the field blk_keyslot_manager::features in a few places: ANDROID-block-add-hardware-wrapped-key-support.patch ANDROID-dm-add-support-for-passing-through-derive_raw_secret.patch Finally, update non-upstream device-mapper targets (dm-bow and dm-default-key) to use the new way of specifying inline crypto passthrough support (DM_TARGET_PASSES_CRYPTO) rather than the old way (may_passthrough_inline_crypto). These changes should be folded into: ANDROID-dm-bow-Add-dm-bow-feature.patch ANDROID-dm-add-dm-default-key-target-for-metadata-encryption.patch Test: tested on db845c; verified that inline crypto support gets passed through over dm-linear. Bug: 162257830 Change-Id: I5e3dea1aa09fc1215c90857b5b51d9e3720ef7db Signed-off-by: Eric Biggers --- block/keyslot-manager.c | 92 ++++++++- drivers/md/dm-bow.c | 2 +- drivers/md/dm-core.h | 7 +- drivers/md/dm-default-key.c | 3 +- drivers/md/dm-flakey.c | 4 +- drivers/md/dm-linear.c | 6 +- drivers/md/dm-table.c | 344 +++++++++++++++++++++++++++----- drivers/md/dm.c | 173 ++-------------- include/linux/device-mapper.h | 17 +- include/linux/keyslot-manager.h | 14 +- include/uapi/linux/dm-ioctl.h | 4 +- 11 files changed, 422 insertions(+), 244 deletions(-) diff --git a/block/keyslot-manager.c b/block/keyslot-manager.c index ecd7df6a3000..2b18b17a15fb 100644 --- a/block/keyslot-manager.c +++ b/block/keyslot-manager.c @@ -386,7 +386,7 @@ void blk_ksm_reprogram_all_keys(struct blk_keyslot_manager *ksm) { unsigned int slot; - if (WARN_ON(blk_ksm_is_passthrough(ksm))) + if (blk_ksm_is_passthrough(ksm)) return; /* This is for device initialization, so don't resume the device */ @@ -430,7 +430,6 @@ void blk_ksm_unregister(struct request_queue *q) { q->ksm = NULL; } -EXPORT_SYMBOL_GPL(blk_ksm_unregister); /** * blk_ksm_derive_raw_secret() - Derive software secret from wrapped key @@ -490,21 +489,100 @@ void blk_ksm_intersect_modes(struct blk_keyslot_manager *parent, parent->max_dun_bytes_supported = min(parent->max_dun_bytes_supported, child->max_dun_bytes_supported); - parent->features &= child->features; for (i = 0; i < ARRAY_SIZE(child->crypto_modes_supported); i++) { parent->crypto_modes_supported[i] &= child->crypto_modes_supported[i]; } + parent->features &= child->features; } else { parent->max_dun_bytes_supported = 0; - parent->features = 0; memset(parent->crypto_modes_supported, 0, sizeof(parent->crypto_modes_supported)); + parent->features = 0; } } EXPORT_SYMBOL_GPL(blk_ksm_intersect_modes); +/** + * blk_ksm_is_superset() - Check if a KSM supports a superset of crypto modes + * and DUN bytes that another KSM supports. Here, + * "superset" refers to the mathematical meaning of the + * word - i.e. if two KSMs have the *same* capabilities, + * they *are* considered supersets of each other. + * @ksm_superset: The KSM that we want to verify is a superset + * @ksm_subset: The KSM that we want to verify is a subset + * + * Return: True if @ksm_superset supports a superset of the crypto modes and DUN + * bytes that @ksm_subset supports. + */ +bool blk_ksm_is_superset(struct blk_keyslot_manager *ksm_superset, + struct blk_keyslot_manager *ksm_subset) +{ + int i; + + if (!ksm_subset) + return true; + + if (!ksm_superset) + return false; + + for (i = 0; i < ARRAY_SIZE(ksm_superset->crypto_modes_supported); i++) { + if (ksm_subset->crypto_modes_supported[i] & + (~ksm_superset->crypto_modes_supported[i])) { + return false; + } + } + + if (ksm_subset->max_dun_bytes_supported > + ksm_superset->max_dun_bytes_supported) { + return false; + } + + if (ksm_subset->features & ~ksm_superset->features) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(blk_ksm_is_superset); + +/** + * blk_ksm_update_capabilities() - Update the restrictions of a KSM to those of + * another KSM + * @target_ksm: The KSM whose restrictions to update. + * @reference_ksm: The KSM to whose restrictions this function will update + * @target_ksm's restrictions to. + * + * Blk-crypto requires that crypto capabilities that were + * advertised when a bio was created continue to be supported by the + * device until that bio is ended. This is turn means that a device cannot + * shrink its advertised crypto capabilities without any explicit + * synchronization with upper layers. So if there's no such explicit + * synchronization, @reference_ksm must support all the crypto capabilities that + * @target_ksm does + * (i.e. we need blk_ksm_is_superset(@reference_ksm, @target_ksm) == true). + * + * Note also that as long as the crypto capabilities are being expanded, the + * order of updates becoming visible is not important because it's alright + * for blk-crypto to see stale values - they only cause blk-crypto to + * believe that a crypto capability isn't supported when it actually is (which + * might result in blk-crypto-fallback being used if available, or the bio being + * failed). + */ +void blk_ksm_update_capabilities(struct blk_keyslot_manager *target_ksm, + struct blk_keyslot_manager *reference_ksm) +{ + memcpy(target_ksm->crypto_modes_supported, + reference_ksm->crypto_modes_supported, + sizeof(target_ksm->crypto_modes_supported)); + + target_ksm->max_dun_bytes_supported = + reference_ksm->max_dun_bytes_supported; + + target_ksm->features = reference_ksm->features; +} +EXPORT_SYMBOL_GPL(blk_ksm_update_capabilities); + /** * blk_ksm_init_passthrough() - Init a passthrough keyslot manager * @ksm: The keyslot manager to init @@ -512,10 +590,8 @@ EXPORT_SYMBOL_GPL(blk_ksm_intersect_modes); * Initialize a passthrough keyslot manager. * Called by e.g. storage drivers to set up a keyslot manager in their * request_queue, when the storage driver wants to manage its keys by itself. - * This is useful for inline encryption hardware that don't have a small fixed - * number of keyslots, and for layered devices. - * - * See blk_ksm_init() for more details about the parameters. + * This is useful for inline encryption hardware that doesn't have the concept + * of keyslots, and for layered devices. */ void blk_ksm_init_passthrough(struct blk_keyslot_manager *ksm) { diff --git a/drivers/md/dm-bow.c b/drivers/md/dm-bow.c index ea6a30fa27d4..cfd1fa63ff97 100644 --- a/drivers/md/dm-bow.c +++ b/drivers/md/dm-bow.c @@ -788,7 +788,6 @@ static int dm_bow_ctr(struct dm_target *ti, unsigned int argc, char **argv) rb_insert_color(&br->node, &bc->ranges); ti->discards_supported = true; - ti->may_passthrough_inline_crypto = true; return 0; @@ -1267,6 +1266,7 @@ static int dm_bow_iterate_devices(struct dm_target *ti, static struct target_type bow_target = { .name = "bow", .version = {1, 2, 0}, + .features = DM_TARGET_PASSES_CRYPTO, .module = THIS_MODULE, .ctr = dm_bow_ctr, .dtr = dm_bow_dtr, diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h index 805f2ea72782..ba4e51853cba 100644 --- a/drivers/md/dm-core.h +++ b/drivers/md/dm-core.h @@ -53,9 +53,6 @@ struct mapped_device { int numa_node_id; struct request_queue *queue; -#ifdef CONFIG_BLK_INLINE_ENCRYPTION - struct blk_keyslot_manager ksm; -#endif atomic_t holders; atomic_t open_count; @@ -173,6 +170,10 @@ struct dm_table { void *event_context; struct dm_md_mempools *mempools; + +#ifdef CONFIG_BLK_INLINE_ENCRYPTION + struct blk_keyslot_manager *ksm; +#endif }; static inline struct completion *dm_get_completion_from_kobject(struct kobject *kobj) diff --git a/drivers/md/dm-default-key.c b/drivers/md/dm-default-key.c index 1c578160c0ed..e8e9b72d30c5 100644 --- a/drivers/md/dm-default-key.c +++ b/drivers/md/dm-default-key.c @@ -255,8 +255,6 @@ static int default_key_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->num_flush_bios = 1; - ti->may_passthrough_inline_crypto = true; - err = 0; goto out; @@ -397,6 +395,7 @@ static void default_key_io_hints(struct dm_target *ti, static struct target_type default_key_target = { .name = "default-key", .version = {2, 1, 0}, + .features = DM_TARGET_PASSES_CRYPTO, .module = THIS_MODULE, .ctr = default_key_ctr, .dtr = default_key_dtr, diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index a2cc9e45cbba..30c6bc151213 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c @@ -482,8 +482,10 @@ static struct target_type flakey_target = { .name = "flakey", .version = {1, 5, 0}, #ifdef CONFIG_BLK_DEV_ZONED - .features = DM_TARGET_ZONED_HM, + .features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO, .report_zones = flakey_report_zones, +#else + .features = DM_TARGET_PASSES_CRYPTO, #endif .module = THIS_MODULE, .ctr = flakey_ctr, diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 345e22b9be5d..fc9c4272c10d 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -62,7 +62,6 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->num_secure_erase_bios = 1; ti->num_write_same_bios = 1; ti->num_write_zeroes_bios = 1; - ti->may_passthrough_inline_crypto = true; ti->private = lc; return 0; @@ -230,10 +229,11 @@ static struct target_type linear_target = { .version = {1, 4, 0}, #ifdef CONFIG_BLK_DEV_ZONED .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT | - DM_TARGET_ZONED_HM, + DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO, .report_zones = linear_report_zones, #else - .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT, + .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT | + DM_TARGET_PASSES_CRYPTO, #endif .module = THIS_MODULE, .ctr = linear_ctr, diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 59085a80a092..e2c7f91dd6c9 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include #define DM_MSG_PREFIX "table" @@ -189,6 +187,8 @@ static void free_devices(struct list_head *devices, struct mapped_device *md) } } +static void dm_table_destroy_keyslot_manager(struct dm_table *t); + void dm_table_destroy(struct dm_table *t) { unsigned int i; @@ -217,6 +217,8 @@ void dm_table_destroy(struct dm_table *t) dm_free_md_mempools(t->mempools); + dm_table_destroy_keyslot_manager(t); + kfree(t); } @@ -1212,6 +1214,287 @@ static int dm_table_register_integrity(struct dm_table *t) return 0; } +#ifdef CONFIG_BLK_INLINE_ENCRYPTION + +struct dm_keyslot_manager { + struct blk_keyslot_manager ksm; + struct mapped_device *md; +}; + +struct dm_keyslot_evict_args { + const struct blk_crypto_key *key; + int err; +}; + +static int dm_keyslot_evict_callback(struct dm_target *ti, struct dm_dev *dev, + sector_t start, sector_t len, void *data) +{ + struct dm_keyslot_evict_args *args = data; + int err; + + err = blk_crypto_evict_key(bdev_get_queue(dev->bdev), args->key); + if (!args->err) + args->err = err; + /* Always try to evict the key from all devices. */ + return 0; +} + +/* + * When an inline encryption key is evicted from a device-mapper device, evict + * it from all the underlying devices. + */ +static int dm_keyslot_evict(struct blk_keyslot_manager *ksm, + const struct blk_crypto_key *key, unsigned int slot) +{ + struct dm_keyslot_manager *dksm = container_of(ksm, + struct dm_keyslot_manager, + ksm); + struct mapped_device *md = dksm->md; + struct dm_keyslot_evict_args args = { key }; + struct dm_table *t; + int srcu_idx; + int i; + struct dm_target *ti; + + t = dm_get_live_table(md, &srcu_idx); + if (!t) + return 0; + for (i = 0; i < dm_table_get_num_targets(t); i++) { + ti = dm_table_get_target(t, i); + if (!ti->type->iterate_devices) + continue; + ti->type->iterate_devices(ti, dm_keyslot_evict_callback, &args); + } + dm_put_live_table(md, srcu_idx); + return args.err; +} + +struct dm_derive_raw_secret_args { + const u8 *wrapped_key; + unsigned int wrapped_key_size; + u8 *secret; + unsigned int secret_size; + int err; +}; + +static int dm_derive_raw_secret_callback(struct dm_target *ti, + struct dm_dev *dev, sector_t start, + sector_t len, void *data) +{ + struct dm_derive_raw_secret_args *args = data; + struct request_queue *q = bdev_get_queue(dev->bdev); + + if (!args->err) + return 0; + + if (!q->ksm) { + args->err = -EOPNOTSUPP; + return 0; + } + + args->err = blk_ksm_derive_raw_secret(q->ksm, args->wrapped_key, + args->wrapped_key_size, + args->secret, + args->secret_size); + /* Try another device in case this fails. */ + return 0; +} + +/* + * Retrieve the raw_secret from the underlying device. Given that only one + * raw_secret can exist for a particular wrappedkey, retrieve it only from the + * first device that supports derive_raw_secret(). + */ +static int dm_derive_raw_secret(struct blk_keyslot_manager *ksm, + const u8 *wrapped_key, + unsigned int wrapped_key_size, + u8 *secret, unsigned int secret_size) +{ + struct dm_keyslot_manager *dksm = container_of(ksm, + struct dm_keyslot_manager, + ksm); + struct mapped_device *md = dksm->md; + struct dm_derive_raw_secret_args args = { + .wrapped_key = wrapped_key, + .wrapped_key_size = wrapped_key_size, + .secret = secret, + .secret_size = secret_size, + .err = -EOPNOTSUPP, + }; + struct dm_table *t; + int srcu_idx; + int i; + struct dm_target *ti; + + t = dm_get_live_table(md, &srcu_idx); + if (!t) + return -EOPNOTSUPP; + for (i = 0; i < dm_table_get_num_targets(t); i++) { + ti = dm_table_get_target(t, i); + if (!ti->type->iterate_devices) + continue; + ti->type->iterate_devices(ti, dm_derive_raw_secret_callback, + &args); + if (!args.err) + break; + } + dm_put_live_table(md, srcu_idx); + return args.err; +} + + +static struct blk_ksm_ll_ops dm_ksm_ll_ops = { + .keyslot_evict = dm_keyslot_evict, + .derive_raw_secret = dm_derive_raw_secret, +}; + +static int device_intersect_crypto_modes(struct dm_target *ti, + struct dm_dev *dev, sector_t start, + sector_t len, void *data) +{ + struct blk_keyslot_manager *parent = data; + struct blk_keyslot_manager *child = bdev_get_queue(dev->bdev)->ksm; + + blk_ksm_intersect_modes(parent, child); + return 0; +} + +void dm_destroy_keyslot_manager(struct blk_keyslot_manager *ksm) +{ + struct dm_keyslot_manager *dksm = container_of(ksm, + struct dm_keyslot_manager, + ksm); + + if (!ksm) + return; + + blk_ksm_destroy(ksm); + kfree(dksm); +} + +static void dm_table_destroy_keyslot_manager(struct dm_table *t) +{ + dm_destroy_keyslot_manager(t->ksm); + t->ksm = NULL; +} + +/* + * Constructs and initializes t->ksm with a keyslot manager that + * represents the common set of crypto capabilities of the devices + * described by the dm_table. However, if the constructed keyslot + * manager does not support a superset of the crypto capabilities + * supported by the current keyslot manager of the mapped_device, + * it returns an error instead, since we don't support restricting + * crypto capabilities on table changes. Finally, if the constructed + * keyslot manager doesn't actually support any crypto modes at all, + * it just returns NULL. + */ +static int dm_table_construct_keyslot_manager(struct dm_table *t) +{ + struct dm_keyslot_manager *dksm; + struct blk_keyslot_manager *ksm; + struct dm_target *ti; + unsigned int i; + bool ksm_is_empty = true; + + dksm = kmalloc(sizeof(*dksm), GFP_KERNEL); + if (!dksm) + return -ENOMEM; + dksm->md = t->md; + + ksm = &dksm->ksm; + blk_ksm_init_passthrough(ksm); + ksm->ksm_ll_ops = dm_ksm_ll_ops; + ksm->max_dun_bytes_supported = UINT_MAX; + memset(ksm->crypto_modes_supported, 0xFF, + sizeof(ksm->crypto_modes_supported)); + ksm->features = BLK_CRYPTO_FEATURE_STANDARD_KEYS | + BLK_CRYPTO_FEATURE_WRAPPED_KEYS; + + for (i = 0; i < dm_table_get_num_targets(t); i++) { + ti = dm_table_get_target(t, i); + + if (!dm_target_passes_crypto(ti->type)) { + blk_ksm_intersect_modes(ksm, NULL); + break; + } + if (!ti->type->iterate_devices) + continue; + ti->type->iterate_devices(ti, device_intersect_crypto_modes, + ksm); + } + + if (t->md->queue && !blk_ksm_is_superset(ksm, t->md->queue->ksm)) { + DMWARN("Inline encryption capabilities of new DM table were more restrictive than the old table's. This is not supported!"); + dm_destroy_keyslot_manager(ksm); + return -EINVAL; + } + + /* + * If the new KSM doesn't actually support any crypto modes, we may as + * well represent it with a NULL ksm. + */ + ksm_is_empty = true; + for (i = 0; i < ARRAY_SIZE(ksm->crypto_modes_supported); i++) { + if (ksm->crypto_modes_supported[i]) { + ksm_is_empty = false; + break; + } + } + + if (ksm_is_empty) { + dm_destroy_keyslot_manager(ksm); + ksm = NULL; + } + + /* + * t->ksm is only set temporarily while the table is being set + * up, and it gets set to NULL after the capabilities have + * been transferred to the request_queue. + */ + t->ksm = ksm; + + return 0; +} + +static void dm_update_keyslot_manager(struct request_queue *q, + struct dm_table *t) +{ + if (!t->ksm) + return; + + /* Make the ksm less restrictive */ + if (!q->ksm) { + blk_ksm_register(t->ksm, q); + } else { + blk_ksm_update_capabilities(q->ksm, t->ksm); + dm_destroy_keyslot_manager(t->ksm); + } + t->ksm = NULL; +} + +#else /* CONFIG_BLK_INLINE_ENCRYPTION */ + +static int dm_table_construct_keyslot_manager(struct dm_table *t) +{ + return 0; +} + +void dm_destroy_keyslot_manager(struct blk_keyslot_manager *ksm) +{ +} + +static void dm_table_destroy_keyslot_manager(struct dm_table *t) +{ +} + +static void dm_update_keyslot_manager(struct request_queue *q, + struct dm_table *t) +{ +} + +#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */ + /* * Prepares the table for use by building the indices, * setting the type, and allocating mempools. @@ -1238,6 +1521,12 @@ int dm_table_complete(struct dm_table *t) return r; } + r = dm_table_construct_keyslot_manager(t); + if (r) { + DMERR("could not construct keyslot manager."); + return r; + } + r = dm_table_alloc_md_mempools(t, t->md); if (r) DMERR("unable to allocate mempools"); @@ -1536,54 +1825,6 @@ static void dm_table_verify_integrity(struct dm_table *t) } } -#ifdef CONFIG_BLK_INLINE_ENCRYPTION -static int device_intersect_crypto_modes(struct dm_target *ti, - struct dm_dev *dev, sector_t start, - sector_t len, void *data) -{ - struct blk_keyslot_manager *parent = data; - struct blk_keyslot_manager *child = bdev_get_queue(dev->bdev)->ksm; - - blk_ksm_intersect_modes(parent, child); - return 0; -} - -/* - * Update the inline crypto modes supported by 'q->ksm' to be the intersection - * of the modes supported by all targets in the table. - * - * For any mode to be supported at all, all targets must have explicitly - * declared that they can pass through inline crypto support. For a particular - * mode to be supported, all underlying devices must also support it. - * - * Assume that 'q->ksm' initially declares all modes to be supported. - */ -static void dm_calculate_supported_crypto_modes(struct dm_table *t, - struct request_queue *q) -{ - struct dm_target *ti; - unsigned int i; - - for (i = 0; i < dm_table_get_num_targets(t); i++) { - ti = dm_table_get_target(t, i); - - if (!ti->may_passthrough_inline_crypto) { - blk_ksm_intersect_modes(q->ksm, NULL); - return; - } - if (!ti->type->iterate_devices) - continue; - ti->type->iterate_devices(ti, device_intersect_crypto_modes, - q->ksm); - } -} -#else /* CONFIG_BLK_INLINE_ENCRYPTION */ -static inline void dm_calculate_supported_crypto_modes(struct dm_table *t, - struct request_queue *q) -{ -} -#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */ - static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev, sector_t start, sector_t len, void *data) { @@ -1918,8 +2159,6 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, dm_table_verify_integrity(t); - dm_calculate_supported_crypto_modes(t, q); - /* * Some devices don't use blk_integrity but still want stable pages * because they do their own checksumming. @@ -1950,6 +2189,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, } #endif + dm_update_keyslot_manager(q, t); blk_queue_update_readahead(q); } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 011384ca4968..eaf4587ad2aa 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1723,7 +1723,18 @@ static const struct dax_operations dm_dax_ops; static void dm_wq_work(struct work_struct *work); -static void dm_destroy_inline_encryption(struct request_queue *q); +#ifdef CONFIG_BLK_INLINE_ENCRYPTION +static void dm_queue_destroy_keyslot_manager(struct request_queue *q) +{ + dm_destroy_keyslot_manager(q->ksm); +} + +#else /* CONFIG_BLK_INLINE_ENCRYPTION */ + +static inline void dm_queue_destroy_keyslot_manager(struct request_queue *q) +{ +} +#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */ static void cleanup_mapped_device(struct mapped_device *md) { @@ -1747,7 +1758,7 @@ static void cleanup_mapped_device(struct mapped_device *md) } if (md->queue) { - dm_destroy_inline_encryption(md->queue); + dm_queue_destroy_keyslot_manager(md->queue); blk_cleanup_queue(md->queue); } @@ -2098,161 +2109,6 @@ struct queue_limits *dm_get_queue_limits(struct mapped_device *md) } EXPORT_SYMBOL_GPL(dm_get_queue_limits); -#ifdef CONFIG_BLK_INLINE_ENCRYPTION -struct dm_keyslot_evict_args { - const struct blk_crypto_key *key; - int err; -}; - -static int dm_keyslot_evict_callback(struct dm_target *ti, struct dm_dev *dev, - sector_t start, sector_t len, void *data) -{ - struct dm_keyslot_evict_args *args = data; - int err; - - err = blk_crypto_evict_key(bdev_get_queue(dev->bdev), args->key); - if (!args->err) - args->err = err; - /* Always try to evict the key from all devices. */ - return 0; -} - -/* - * When an inline encryption key is evicted from a device-mapper device, evict - * it from all the underlying devices. - */ -static int dm_keyslot_evict(struct blk_keyslot_manager *ksm, - const struct blk_crypto_key *key, unsigned int slot) -{ - struct mapped_device *md = container_of(ksm, struct mapped_device, ksm); - struct dm_keyslot_evict_args args = { key }; - struct dm_table *t; - int srcu_idx; - int i; - struct dm_target *ti; - - t = dm_get_live_table(md, &srcu_idx); - if (!t) - return 0; - for (i = 0; i < dm_table_get_num_targets(t); i++) { - ti = dm_table_get_target(t, i); - if (!ti->type->iterate_devices) - continue; - ti->type->iterate_devices(ti, dm_keyslot_evict_callback, &args); - } - dm_put_live_table(md, srcu_idx); - return args.err; -} - -struct dm_derive_raw_secret_args { - const u8 *wrapped_key; - unsigned int wrapped_key_size; - u8 *secret; - unsigned int secret_size; - int err; -}; - -static int dm_derive_raw_secret_callback(struct dm_target *ti, - struct dm_dev *dev, sector_t start, - sector_t len, void *data) -{ - struct dm_derive_raw_secret_args *args = data; - struct request_queue *q = bdev_get_queue(dev->bdev); - - if (!args->err) - return 0; - - if (!q->ksm) { - args->err = -EOPNOTSUPP; - return 0; - } - - args->err = blk_ksm_derive_raw_secret(q->ksm, args->wrapped_key, - args->wrapped_key_size, - args->secret, - args->secret_size); - /* Try another device in case this fails. */ - return 0; -} - -/* - * Retrieve the raw_secret from the underlying device. Given that - * only only one raw_secret can exist for a particular wrappedkey, - * retrieve it only from the first device that supports derive_raw_secret() - */ -static int dm_derive_raw_secret(struct blk_keyslot_manager *ksm, - const u8 *wrapped_key, - unsigned int wrapped_key_size, - u8 *secret, unsigned int secret_size) -{ - struct mapped_device *md = container_of(ksm, struct mapped_device, ksm); - struct dm_derive_raw_secret_args args = { - .wrapped_key = wrapped_key, - .wrapped_key_size = wrapped_key_size, - .secret = secret, - .secret_size = secret_size, - .err = -EOPNOTSUPP, - }; - struct dm_table *t; - int srcu_idx; - int i; - struct dm_target *ti; - - t = dm_get_live_table(md, &srcu_idx); - if (!t) - return -EOPNOTSUPP; - for (i = 0; i < dm_table_get_num_targets(t); i++) { - ti = dm_table_get_target(t, i); - if (!ti->type->iterate_devices) - continue; - ti->type->iterate_devices(ti, dm_derive_raw_secret_callback, - &args); - if (!args.err) - break; - } - dm_put_live_table(md, srcu_idx); - return args.err; -} - -static struct blk_ksm_ll_ops dm_ksm_ll_ops = { - .keyslot_evict = dm_keyslot_evict, - .derive_raw_secret = dm_derive_raw_secret, -}; - -static void dm_init_inline_encryption(struct mapped_device *md) -{ - blk_ksm_init_passthrough(&md->ksm); - md->ksm.ksm_ll_ops = dm_ksm_ll_ops; - - /* - * Initially declare support for all crypto settings. Anything - * unsupported by a child device will be removed later when calculating - * the device restrictions. - */ - md->ksm.max_dun_bytes_supported = UINT_MAX; - md->ksm.features = BLK_CRYPTO_FEATURE_STANDARD_KEYS | - BLK_CRYPTO_FEATURE_WRAPPED_KEYS; - memset(md->ksm.crypto_modes_supported, 0xFF, - sizeof(md->ksm.crypto_modes_supported)); - - blk_ksm_register(&md->ksm, md->queue); -} - -static void dm_destroy_inline_encryption(struct request_queue *q) -{ - blk_ksm_destroy(q->ksm); - blk_ksm_unregister(q); -} -#else /* CONFIG_BLK_INLINE_ENCRYPTION */ -static inline void dm_init_inline_encryption(struct mapped_device *md) -{ -} - -static inline void dm_destroy_inline_encryption(struct request_queue *q) -{ -} -#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */ - /* * Setup the DM device's queue based on md's type */ @@ -2284,9 +2140,6 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) DMERR("Cannot calculate initial queue limits"); return r; } - - dm_init_inline_encryption(md); - dm_table_set_restrictions(t, md->queue, &limits); blk_register_queue(md->disk); diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 6d05a6a8a129..47588130ef5e 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -257,6 +257,12 @@ struct target_type { #define DM_TARGET_NOWAIT 0x00000080 #define dm_target_supports_nowait(type) ((type)->features & DM_TARGET_NOWAIT) +/* + * A target supports passing through inline crypto support. + */ +#define DM_TARGET_PASSES_CRYPTO 0x00000100 +#define dm_target_passes_crypto(type) ((type)->features & DM_TARGET_PASSES_CRYPTO) + struct dm_target { struct dm_table *table; struct target_type *type; @@ -325,12 +331,6 @@ struct dm_target { * whether or not its underlying devices have support. */ bool discards_supported:1; - - /* - * Set if inline crypto capabilities from this target's underlying - * device(s) can be exposed via the device-mapper device. - */ - bool may_passthrough_inline_crypto:1; }; void *dm_per_bio_data(struct bio *bio, size_t data_size); @@ -539,6 +539,11 @@ void dm_table_run_md_queue_async(struct dm_table *t); struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *t); +/* + * Table keyslot manager functions + */ +void dm_destroy_keyslot_manager(struct blk_keyslot_manager *ksm); + /* * A wrapper around vmalloc. */ diff --git a/include/linux/keyslot-manager.h b/include/linux/keyslot-manager.h index 3910fb830fad..05bfaeb24e06 100644 --- a/include/linux/keyslot-manager.h +++ b/include/linux/keyslot-manager.h @@ -18,8 +18,6 @@ enum { BLK_CRYPTO_FEATURE_WRAPPED_KEYS = BIT(1), }; -#ifdef CONFIG_BLK_INLINE_ENCRYPTION - struct blk_keyslot_manager; /** @@ -127,16 +125,20 @@ void blk_ksm_reprogram_all_keys(struct blk_keyslot_manager *ksm); void blk_ksm_destroy(struct blk_keyslot_manager *ksm); -void blk_ksm_intersect_modes(struct blk_keyslot_manager *parent, - const struct blk_keyslot_manager *child); - int blk_ksm_derive_raw_secret(struct blk_keyslot_manager *ksm, const u8 *wrapped_key, unsigned int wrapped_key_size, u8 *secret, unsigned int secret_size); +void blk_ksm_intersect_modes(struct blk_keyslot_manager *parent, + const struct blk_keyslot_manager *child); + void blk_ksm_init_passthrough(struct blk_keyslot_manager *ksm); -#endif /* CONFIG_BLK_INLINE_ENCRYPTION */ +bool blk_ksm_is_superset(struct blk_keyslot_manager *ksm_superset, + struct blk_keyslot_manager *ksm_subset); + +void blk_ksm_update_capabilities(struct blk_keyslot_manager *target_ksm, + struct blk_keyslot_manager *reference_ksm); #endif /* __LINUX_KEYSLOT_MANAGER_H */ diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h index 4933b6b67b85..fcff6669137b 100644 --- a/include/uapi/linux/dm-ioctl.h +++ b/include/uapi/linux/dm-ioctl.h @@ -272,9 +272,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 43 +#define DM_VERSION_MINOR 44 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2020-10-01)" +#define DM_VERSION_EXTRA "-ioctl (2021-02-01)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */