tfm structs contain key material like expanded key schedules. Under CONFIG_CRASH_WIPE_SECRETS, wipe all tfms before kdump to prevent leakage. Copies outside the tfm context, e.g. on the stack or in hardware key registers, are not covered. Also change two kfree() calls to kfree_sensitive() for good measure. Signed-off-by: Jan Sebastian Götte --- crypto/api.c | 78 ++++++++++++++++++++++++++++++++++++++++++++------ include/linux/crypto.h | 11 +++++++ 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/crypto/api.c b/crypto/api.c index 24227582cfcf..f919f52e8b5f 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -10,13 +10,16 @@ * and Nettle, by Niels Möller. */ +#include #include #include #include #include #include #include +#include #include +#include #include #include #include @@ -397,6 +400,60 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask) return len; } +#ifdef CONFIG_CRASH_WIPE_SECRETS +/* + * tfm allocations are tracked on crypto_tfm_list so that they can be wiped + * before kdump. + */ +static HLIST_HEAD(crypto_tfm_list); +static DEFINE_SPINLOCK(crypto_tfm_list_lock); + +static int crypto_crash_wipe(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct crypto_tfm *tfm; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tfm, &crypto_tfm_list, wipe_list) + crash_wipe_memzero(tfm->__crt_ctx, tfm->wipe_size); + rcu_read_unlock(); + + /* off to kexec()! */ + return NOTIFY_DONE; +} + +static struct notifier_block crypto_wipe_nb = { + .notifier_call = crypto_crash_wipe +}; + +static int __init crypto_tfm_track_init(void) +{ + crash_wipe_secrets_register(&crypto_wipe_nb); + return 0; +} +core_initcall(crypto_tfm_track_init); + +/* @size is the size of __crt_ctx */ +static void crypto_track_tfm(struct crypto_tfm *tfm, size_t size) +{ + tfm->wipe_size = size; + + spin_lock(&crypto_tfm_list_lock); + hlist_add_head_rcu(&tfm->wipe_list, &crypto_tfm_list); + spin_unlock(&crypto_tfm_list_lock); +} + +static void crypto_untrack_tfm(struct crypto_tfm *tfm) +{ + spin_lock(&crypto_tfm_list_lock); + hlist_del_rcu(&tfm->wipe_list); + spin_unlock(&crypto_tfm_list_lock); +} +#else +static void crypto_track_tfm(struct crypto_tfm *tfm, size_t size) { } +static void crypto_untrack_tfm(struct crypto_tfm *tfm) { } +#endif /* CONFIG_CRASH_WIPE_SECRETS */ + void crypto_shoot_alg(struct crypto_alg *alg) { down_write(&crypto_alg_sem); @@ -409,15 +466,16 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, u32 mask) { struct crypto_tfm *tfm; - unsigned int tfm_size; + unsigned int ctx_size; int err = -ENOMEM; - tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask); - tfm = kzalloc(tfm_size, GFP_KERNEL); + ctx_size = crypto_ctxsize(alg, type, mask); + tfm = kzalloc(sizeof(*tfm) + ctx_size, GFP_KERNEL); if (tfm == NULL) goto out_err; tfm->__crt_alg = alg; + crypto_track_tfm(tfm, ctx_size); if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm))) goto cra_init_failed; @@ -428,7 +486,8 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, crypto_exit_ops(tfm); if (err == -EAGAIN) crypto_shoot_alg(alg); - kfree(tfm); + crypto_untrack_tfm(tfm); + kfree_sensitive(tfm); out_err: tfm = ERR_PTR(err); out: @@ -497,12 +556,12 @@ void *crypto_create_tfm_node(struct crypto_alg *alg, int node) { struct crypto_tfm *tfm; - size_t size; char *mem; int err; + size_t ctx_size = frontend->extsize(alg); - size = frontend->tfmsize + sizeof(*tfm) + frontend->extsize(alg); - mem = kzalloc_node(size, GFP_KERNEL, node); + mem = kzalloc_node(frontend->tfmsize + sizeof(*tfm) + ctx_size, + GFP_KERNEL, node); if (!mem) return ERR_PTR(-ENOMEM); @@ -510,6 +569,7 @@ void *crypto_create_tfm_node(struct crypto_alg *alg, tfm->__crt_alg = alg; tfm->node = node; tfm->fb = tfm; + crypto_track_tfm(tfm, ctx_size); err = frontend->init_tfm(tfm); if (err) @@ -525,7 +585,8 @@ void *crypto_create_tfm_node(struct crypto_alg *alg, out_free_tfm: if (err == -EAGAIN) crypto_shoot_alg(alg); - kfree(mem); + crypto_untrack_tfm(tfm); + kfree_sensitive(mem); mem = ERR_PTR(err); out: return mem; @@ -627,6 +688,7 @@ void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm) alg->cra_exit(tfm); crypto_exit_ops(tfm); crypto_mod_put(alg); + crypto_untrack_tfm(tfm); kfree_sensitive(mem); } EXPORT_SYMBOL_GPL(crypto_destroy_tfm); diff --git a/include/linux/crypto.h b/include/linux/crypto.h index b7c97f1c47c9..59d6d62180fd 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -420,6 +421,16 @@ struct crypto_tfm { struct crypto_alg *__crt_alg; +#ifdef CONFIG_CRASH_WIPE_SECRETS + /* + * Link on crypto_tfm_list, used to wipe the expanded key schedule in + * __crt_ctx before kdump. wipe_size covers __crt_ctx only, excluding + * this struct and any frontend preceding it. + */ + struct hlist_node wipe_list; + size_t wipe_size; +#endif + void *__crt_ctx[] CRYPTO_MINALIGN_ATTR; }; -- 2.53.0