When CONFIG_CRASH_ZEROIZE is set, try to erase key payloads on panic before jumping to the kdump kernel. CRASH_ZEROIZE 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 zeroize key op. Implement the zeroize op for the user/logon, encrypted, trusted, and big_key types. Signed-off-by: Jan Sebastian Götte --- include/linux/key-type.h | 9 +++++ security/keys/big_key.c | 15 ++++++++ security/keys/encrypted-keys/encrypted.c | 12 +++++++ security/keys/key.c | 44 +++++++++++++++++++++++ security/keys/trusted-keys/trusted_core.c | 14 ++++++++ security/keys/user_defined.c | 11 ++++++ 6 files changed, 105 insertions(+) diff --git a/include/linux/key-type.h b/include/linux/key-type.h index bb97bd3e5af4..ff0944ce368f 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); + /* scrub the key material without free'ing (optional) + * - used from CONFIG_CRASH_ZEROIZE 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 (*zeroize)(struct key *key); + /* describe a key */ void (*describe)(const struct key *key, struct seq_file *p); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 268f702df380..ad8537dda70f 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -35,6 +35,8 @@ struct big_key_payload { */ #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) +static void big_key_zeroize(struct key *key); + /* * big_key defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload @@ -46,6 +48,7 @@ struct key_type key_type_big_key = { .instantiate = generic_key_instantiate, .revoke = big_key_revoke, .destroy = big_key_destroy, + .zeroize = big_key_zeroize, .describe = big_key_describe, .read = big_key_read, .update = big_key_update, @@ -279,6 +282,18 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) return ret; } +static void big_key_zeroize(struct key *key) +{ + struct big_key_payload *payload = to_big_key_payload(key->payload); + + if (payload->data) { + if (payload->length > BIG_KEY_FILE_THRESHOLD) + memzero_explicit(payload->data, CHACHA20POLY1305_KEY_SIZE); + else + memzero_explicit(payload->data, payload->length); + } +} + /* * Register key type */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 59cb77b237b3..9f56fa9b4aaf 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -970,11 +970,23 @@ static void encrypted_destroy(struct key *key) kfree_sensitive(key->payload.data[0]); } +static void encrypted_zeroize(struct key *key) +{ + struct encrypted_key_payload *epayload = key->payload.data[0]; + + if (!epayload) + return; + + memzero_explicit(epayload->payload_data, + epayload->payload_datalen + epayload->datablob_len); +} + struct key_type key_type_encrypted = { .name = "encrypted", .instantiate = encrypted_instantiate, .update = encrypted_update, .destroy = encrypted_destroy, + .zeroize = encrypted_zeroize, .describe = user_describe, .read = encrypted_read, }; diff --git a/security/keys/key.c b/security/keys/key.c index b34a64d81d47..5673dcc8c5d2 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -1268,6 +1269,44 @@ void unregister_key_type(struct key_type *ktype) } EXPORT_SYMBOL(unregister_key_type); +#ifdef CONFIG_CRASH_ZEROIZE +/* Called far into vpanic from crash_core.c with other CPUs stopped and + * preemption disabled + */ +static int key_crash_zeroize(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_zeroize: 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); + + if (key->type == &key_type_keyring || + key->state == KEY_IS_UNINSTANTIATED) + continue; + + /* custom zeroize since free'ing isn't safe at this point */ + if (key->type->zeroize) + key->type->zeroize(key); + } + /* off to kexec()! */ + return NOTIFY_DONE; +} + +static struct notifier_block key_crash_zeroize_nb = { + .notifier_call = key_crash_zeroize +}; +#endif /* CONFIG_CRASH_ZEROIZE */ + /* * Initialise the key management state. */ @@ -1290,4 +1329,9 @@ void __init key_init(void) rb_insert_color(&root_key_user.node, &key_user_tree); + +#ifdef CONFIG_CRASH_ZEROIZE + atomic_notifier_chain_register(&crash_zeroize_notifier_list, + &key_crash_zeroize_nb); +#endif } diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index 0509d9955f2a..f159faeafe23 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -325,11 +325,25 @@ static void trusted_destroy(struct key *key) kfree_sensitive(key->payload.data[0]); } +static void trusted_zeroize(struct key *key) +{ + struct trusted_key_payload *p = key->payload.data[0]; + + if (!p) + return; + + memzero_explicit(p->key, sizeof(p->key)); + memzero_explicit(p->blob, sizeof(p->blob)); + p->key_len = 0; + p->blob_len = 0; +} + struct key_type key_type_trusted = { .name = "trusted", .instantiate = trusted_instantiate, .update = trusted_update, .destroy = trusted_destroy, + .zeroize = trusted_zeroize, .describe = user_describe, .read = trusted_read, }; diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 6f88b507f927..ade95dc2481d 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -15,6 +15,7 @@ #include "internal.h" static int logon_vet_description(const char *desc); +static void user_zeroize(struct key *key); /* * user defined keys take an arbitrary string as the description and an @@ -28,6 +29,7 @@ struct key_type key_type_user = { .update = user_update, .revoke = user_revoke, .destroy = user_destroy, + .zeroize = user_zeroize, .describe = user_describe, .read = user_read, }; @@ -48,6 +50,7 @@ struct key_type key_type_logon = { .update = user_update, .revoke = user_revoke, .destroy = user_destroy, + .zeroize = user_zeroize, .describe = user_describe, .vet_description = logon_vet_description, }; @@ -152,6 +155,14 @@ void user_destroy(struct key *key) EXPORT_SYMBOL_GPL(user_destroy); +static void user_zeroize(struct key *key) +{ + struct user_key_payload *upayload = key->payload.data[0]; + + if (upayload) + memzero_explicit(upayload->data, upayload->datalen); +} + /* * describe the user key */ -- 2.53.0