When CONFIG_CRASH_WIPE_SECRETS is set, try to erase key payloads on panic before jumping to the kdump kernel.
CRASH_WIPE_SECRETS notifiers run during panic() with other CPUs stopped and preemption disabled. In this state, we can't rely on free()'ing being safe, so we define a new `wipe` key op. Signed-off-by: Jan Sebastian Götte <[email protected]> --- Documentation/security/keys/core.rst | 13 +++++++++++ include/linux/key-type.h | 9 ++++++++ security/keys/key.c | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 326b8a973828..35cb00a87c46 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -1596,6 +1596,19 @@ The structure has a number of fields, some of which are mandatory: It is not safe to sleep in this method; the caller may hold spinlocks. + * ``void (*wipe)(struct key *key);`` + + This method is optional. It is called from the panic path when + CONFIG_CRASH_WIPE_SECRETS is enabled, to erase the key material from + memory before the kdump kernel is started, so that it does not end up in + the crash dump. Unlike destroy(), it must only clear the payload, not + free it. + + This method is called with all other CPUs stopped and preemption + disabled, and only for positively instantiated keys. It must not sleep, + allocate, free or take locks, as they will never be released. + + * ``void (*describe)(const struct key *key, struct seq_file *p);`` This method is optional. It is called during /proc/keys reading to diff --git a/include/linux/key-type.h b/include/linux/key-type.h index bb97bd3e5af4..21e07db0c5f2 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -122,6 +122,15 @@ struct key_type { /* clear the data from a key (optional) */ void (*destroy)(struct key *key); + /* wipe the key material without free'ing (optional) + * - used from CONFIG_CRASH_WIPE_SECRETS during panic to keep keys out + * of crash dumps + * - called from the panic path with other CPUs stopped and preemption + * disabled + * - must not sleep, allocate, free or take locks + */ + void (*wipe)(struct key *key); + /* describe a key */ void (*describe)(const struct key *key, struct seq_file *p); diff --git a/security/keys/key.c b/security/keys/key.c index b34a64d81d47..213e6f1d5d83 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/security.h> #include <linux/workqueue.h> +#include <linux/crash_core.h> #include <linux/random.h> #include <linux/err.h> #include "internal.h" @@ -1268,6 +1269,47 @@ void unregister_key_type(struct key_type *ktype) } EXPORT_SYMBOL(unregister_key_type); +/* Called far into vpanic from crash_core.c with other CPUs stopped and + * preemption disabled + */ +static int key_crash_wipe(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct rb_node *node; + + /* If we can't acquire the lock, the rbtree might be in an inconsistent + * state. That's all we can do then, as there's no point to waiting + * at this stage. + */ + if (!spin_trylock(&key_serial_lock)) { + pr_crit("crash_wipe_secrets: can't acquire key_serial_lock. skipping keyrings.\n"); + return NOTIFY_DONE; + } + + for (node = rb_first(&key_serial_tree); node; node = rb_next(node)) { + struct key *key = rb_entry(node, struct key, serial_node); + + /* Negatively instantiated keys have key->state < 0 and never + * had a payload attached, so only wipe positive ones. + */ + if (key->type == &key_type_keyring || !key_is_positive(key)) + continue; + + /* We have a dedicated wipe callback for this since free'ing + * isn't safe at this point + */ + if (key->type->wipe) + key->type->wipe(key); + } + spin_unlock(&key_serial_lock); + /* off to kexec()! */ + return NOTIFY_DONE; +} + +static struct notifier_block key_crash_wipe_nb = { + .notifier_call = key_crash_wipe +}; + /* * Initialise the key management state. */ @@ -1290,4 +1332,5 @@ void __init key_init(void) rb_insert_color(&root_key_user.node, &key_user_tree); + crash_wipe_secrets_register(&key_crash_wipe_nb); } -- 2.53.0

