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


Reply via email to