[PATCH v2 05/13] security/keys: wipe key payloads before kdump

Jan Sebastian Götte <[email protected]>
Newsgroups org.kernel.vger.linux-crypto,dev.linux.lists.dm-devel,org.infradead.lists.kexec,org.kernel.vger.keyrings,org.kernel.vger.linux-doc,org.kernel.vger.linux-fscrypt,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-integrity,org.kernel.vger.linux-kernel,org.kernel.vger.linux-nfs,org.kernel.vger.linux-security-module,org.kernel.vger.netdev,org.kvack.linux-mm
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.