Wipe volume key/iv copies kept by dm-crypt with CONFIG_CRASH_WIPE_SECRETS. The backend tfms are already handled separately.
Add a list tracking struct crypt_config instances when CONFIG_CRASH_WIPE_SECRETS is set. Structs are tracked here to avoid having to enumerate them through some roundabout way before kdump, when we can't safely take locks anymore. Use custom wipe handlers even for things like ivs that have existing wipe functions elsewhere because we need to use crash_wipe_memzero instead of memzero_explicit. The crash_wipe helper memzero_explicit's the target buffers and flushes data caches. On ARM64, missing that cache flush could lead to the zeros not being written to DRAM before the kdump code turns off the data caches moments later. Signed-off-by: Jan Sebastian Götte <[email protected]> --- drivers/md/dm-crypt.c | 151 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 608b617fb817..86adb2f9b94c 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -9,12 +9,14 @@ */ #include <linux/completion.h> +#include <linux/crash_core.h> #include <linux/err.h> #include <linux/module.h> #include <linux/hex.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/key.h> +#include <linux/list.h> #include <linux/bio.h> #include <linux/blkdev.h> #include <linux/blk-integrity.h> @@ -234,6 +236,11 @@ struct crypt_config { struct mutex bio_alloc_lock; u8 *authenc_key; /* space for keys in authenc() format (if used) */ + +#ifdef CONFIG_CRASH_WIPE_SECRETS + struct list_head wipe_list; +#endif + u8 key[] __counted_by(key_size); }; @@ -243,6 +250,12 @@ struct crypt_config { static DEFINE_SPINLOCK(dm_crypt_clients_lock); static unsigned int dm_crypt_clients_n; + +#ifdef CONFIG_CRASH_WIPE_SECRETS +static LIST_HEAD(dm_crypt_wipe_list); +static DEFINE_SPINLOCK(dm_crypt_wipe_list_lock); +#endif + static volatile unsigned long dm_crypt_pages_per_client; #define DM_CRYPT_MEMORY_PERCENT 2 #define DM_CRYPT_MIN_PAGES_PER_CLIENT (BIO_MAX_VECS * 16) @@ -460,8 +473,7 @@ static void crypt_iv_lmk_dtr(struct crypt_config *cc) { struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk; - kfree_sensitive(lmk->seed); - lmk->seed = NULL; + kfree_sensitive(xchg(&lmk->seed, NULL)); } static int crypt_iv_lmk_ctr(struct crypt_config *cc, struct dm_target *ti, @@ -582,10 +594,8 @@ static void crypt_iv_tcw_dtr(struct crypt_config *cc) { struct iv_tcw_private *tcw = &cc->iv_gen_private.tcw; - kfree_sensitive(tcw->iv_seed); - tcw->iv_seed = NULL; - kfree_sensitive(tcw->whitening); - tcw->whitening = NULL; + kfree_sensitive(xchg(&tcw->iv_seed, NULL)); + kfree_sensitive(xchg(&tcw->whitening, NULL)); } static int crypt_iv_tcw_ctr(struct crypt_config *cc, struct dm_target *ti, @@ -761,8 +771,7 @@ static void crypt_iv_elephant_dtr(struct crypt_config *cc) { struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant; - kfree_sensitive(elephant->key); - elephant->key = NULL; + kfree_sensitive(xchg(&elephant->key, NULL)); } static int crypt_iv_elephant_ctr(struct crypt_config *cc, struct dm_target *ti, @@ -2547,8 +2556,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string goto free_new_key_string; set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - kfree_sensitive(cc->key_string); - cc->key_string = new_key_string; + kfree_sensitive(xchg(&cc->key_string, new_key_string)); return 0; free_new_key_string: @@ -2612,8 +2620,7 @@ static int crypt_set_key(struct crypt_config *cc, char *key) clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); /* wipe references to any kernel keyring key */ - kfree_sensitive(cc->key_string); - cc->key_string = NULL; + kfree_sensitive(xchg(&cc->key_string, NULL)); /* Decode key from its hex representation. */ if (cc->key_size && hex2bin(cc->key, key, cc->key_size) < 0) @@ -2641,14 +2648,97 @@ static int crypt_wipe_key(struct crypt_config *cc) if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) cc->iv_gen_ops->wipe(cc); - kfree_sensitive(cc->key_string); - cc->key_string = NULL; + kfree_sensitive(xchg(&cc->key_string, NULL)); r = crypt_setkey(cc); memset(&cc->key, 0, cc->key_size * sizeof(u8)); return r; } +#ifdef CONFIG_CRASH_WIPE_SECRETS +static void crypt_crash_wipe_iv(struct crypt_config *cc) +{ + if (cc->iv_gen_ops == &crypt_iv_lmk_ops) { + struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk; + + if (lmk->seed) + crash_wipe_memzero(lmk->seed, LMK_SEED_SIZE); + } else if (cc->iv_gen_ops == &crypt_iv_tcw_ops) { + struct iv_tcw_private *tcw = &cc->iv_gen_private.tcw; + + if (tcw->iv_seed) + crash_wipe_memzero(tcw->iv_seed, cc->iv_size); + if (tcw->whitening) + crash_wipe_memzero(tcw->whitening, TCW_WHITENING_SIZE); + } else if (cc->iv_gen_ops == &crypt_iv_elephant_ops) { + struct iv_elephant_private *elephant = + &cc->iv_gen_private.elephant; + + if (elephant->key) + crash_wipe_memzero(elephant->key, + sizeof(*elephant->key)); + } +} + +static int crypt_crash_wipe(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct crypt_config *cc; + + /* No locking: all other CPUs are stopped, and a cc is unlinked before + * it is freed, so the forward walk can't reach freed memory. + */ + list_for_each_entry(cc, &dm_crypt_wipe_list, wipe_list) { + crash_wipe_memzero(cc->key, cc->key_size); + + if (cc->authenc_key) + crash_wipe_memzero(cc->authenc_key, + crypt_authenckey_size(cc)); + + if (cc->key_string) + crash_wipe_memzero(cc->key_string, + strlen(cc->key_string)); + + crypt_crash_wipe_iv(cc); + } + + return NOTIFY_DONE; +} + +static struct notifier_block crypt_crash_wipe_nb = { + .notifier_call = crypt_crash_wipe +}; + +static void crypt_track_cc(struct crypt_config *cc) +{ + spin_lock(&dm_crypt_wipe_list_lock); + list_add(&cc->wipe_list, &dm_crypt_wipe_list); + spin_unlock(&dm_crypt_wipe_list_lock); +} + +static void crypt_untrack_cc(struct crypt_config *cc) +{ + spin_lock(&dm_crypt_wipe_list_lock); + list_del(&cc->wipe_list); + spin_unlock(&dm_crypt_wipe_list_lock); +} + +static void crypt_crash_wipe_init(void) +{ + crash_wipe_secrets_register(&crypt_crash_wipe_nb); +} + +static void crypt_crash_wipe_exit(void) +{ + crash_wipe_secrets_unregister(&crypt_crash_wipe_nb); +} +#else +static void crypt_track_cc(struct crypt_config *cc) { } +static void crypt_untrack_cc(struct crypt_config *cc) { } +static void crypt_crash_wipe_init(void) { } +static void crypt_crash_wipe_exit(void) { } +#endif /* CONFIG_CRASH_WIPE_SECRETS */ + static void crypt_calculate_pages_per_client(void) { unsigned long pages = (totalram_pages() - totalhigh_pages()) * DM_CRYPT_MEMORY_PERCENT / 100; @@ -2729,12 +2819,15 @@ static void crypt_dtr(struct dm_target *ti) dm_put_device(ti, cc->dev); kfree_sensitive(cc->cipher_string); - kfree_sensitive(cc->key_string); + kfree_sensitive(xchg(&cc->key_string, NULL)); kfree_sensitive(cc->cipher_auth); - kfree_sensitive(cc->authenc_key); + kfree_sensitive(xchg(&cc->authenc_key, NULL)); mutex_destroy(&cc->bio_alloc_lock); + memzero_explicit(cc->key, cc->key_size); + crypt_untrack_cc(cc); + /* Must zero key material before freeing */ kfree_sensitive(cc); @@ -3210,6 +3303,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->private = cc; + crypt_track_cc(cc); + spin_lock(&dm_crypt_clients_lock); dm_crypt_clients_n++; crypt_calculate_pages_per_client(); @@ -3716,7 +3811,29 @@ static struct target_type crypt_target = { .iterate_devices = crypt_iterate_devices, .io_hints = crypt_io_hints, }; -module_dm(crypt); + +static int __init dm_crypt_init(void) +{ + int r; + + crypt_crash_wipe_init(); + + r = dm_register_target(&crypt_target); + if (r) { + crypt_crash_wipe_exit(); + return r; + } + + return 0; +} +module_init(dm_crypt_init); + +static void __exit dm_crypt_exit(void) +{ + crypt_crash_wipe_exit(); + dm_unregister_target(&crypt_target); +} +module_exit(dm_crypt_exit); MODULE_AUTHOR("Jana Saout <[email protected]>"); MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption"); -- 2.53.0

