fscrypt master keys hang off the superblock rather than a keyring of their own key type, so register a separate notifier that walks the live superblocks via the new super_blocks_crash_wipe() helper.
Signed-off-by: Jan Sebastian Götte <[email protected]> --- fs/crypto/block.c | 10 +++++++ fs/crypto/fscrypt_private.h | 15 ++++++++++ fs/crypto/keyring.c | 68 ++++++++++++++++++++++++++++++++++++++++++++- fs/crypto/keysetup_v1.c | 15 ++++++++++ fs/super.c | 29 +++++++++++++++++++ include/linux/fs.h | 4 +++ 6 files changed, 140 insertions(+), 1 deletion(-) diff --git a/fs/crypto/block.c b/fs/crypto/block.c index 5193f8ba3ee0..a2fa1469b3c4 100644 --- a/fs/crypto/block.c +++ b/fs/crypto/block.c @@ -15,6 +15,7 @@ #include <linux/blk-crypto.h> #include <linux/blkdev.h> +#include <linux/crash_core.h> #include <linux/export.h> #include <linux/sched/mm.h> #include <linux/slab.h> @@ -144,6 +145,15 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, return err; } +#ifdef CONFIG_CRASH_WIPE_SECRETS +void fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key) +{ + if (prep_key->blk_key) + crash_wipe_memzero(prep_key->blk_key->bytes, + sizeof(prep_key->blk_key->bytes)); +} +#endif /* CONFIG_CRASH_WIPE_SECRETS */ + void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) { diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 74329e0953d1..95cb50e5cbcd 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -413,6 +413,10 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key); +#ifdef CONFIG_CRASH_WIPE_SECRETS /* && CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ +void fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key); +#endif + int fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, size_t wrapped_key_size, u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]); @@ -454,6 +458,13 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb, { } +#ifdef CONFIG_CRASH_WIPE_SECRETS /* && !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ +static inline void +fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key) +{ +} +#endif + static inline int fscrypt_derive_sw_secret(struct super_block *sb, const u8 *wrapped_key, size_t wrapped_key_size, @@ -760,6 +771,10 @@ static inline int fscrypt_require_key(struct inode *inode) void fscrypt_put_direct_key(struct fscrypt_direct_key *dk); +#ifdef CONFIG_CRASH_WIPE_SECRETS +void fscrypt_crash_wipe_direct_keys(void); +#endif + int fscrypt_setup_v1_file_key(struct fscrypt_inode_info *ci, const u8 *raw_master_key); diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 76e28d1e0064..5ac302146eb2 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -19,6 +19,7 @@ */ #include <crypto/skcipher.h> +#include <linux/crash_core.h> #include <linux/export.h> #include <linux/key-type.h> #include <linux/once.h> @@ -239,8 +240,9 @@ void fscrypt_destroy_keyring(struct super_block *sb) fscrypt_initiate_key_removal(sb, mk); } } + /* Stop panic-time walkers from finding @keyring before it is freed. */ + smp_store_release(&sb->s_master_keys, NULL); kfree_sensitive(keyring); - sb->s_master_keys = NULL; } static struct hlist_head * @@ -640,6 +642,13 @@ static void fscrypt_provisioning_key_destroy(struct key *key) kfree_sensitive(key->payload.data[0]); } +/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */ +static void fscrypt_provisioning_key_wipe(struct key *key) +{ + if (key->payload.data[0]) + crash_wipe_memzero(key->payload.data[0], key->datalen); +} + static struct key_type key_type_fscrypt_provisioning = { .name = "fscrypt-provisioning", .preparse = fscrypt_provisioning_key_preparse, @@ -647,6 +656,7 @@ static struct key_type key_type_fscrypt_provisioning = { .instantiate = generic_key_instantiate, .describe = fscrypt_provisioning_key_describe, .destroy = fscrypt_provisioning_key_destroy, + .wipe = fscrypt_provisioning_key_wipe, }; /* @@ -1220,6 +1230,58 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_key_status); +#ifdef CONFIG_CRASH_WIPE_SECRETS +/* + * Wipe the master keys of one superblock. The master keys don't live on a + * keyring of their own key_type, so the keyrings core can't reach them. + */ +static void fscrypt_crash_wipe_sb(struct super_block *sb) +{ + struct fscrypt_keyring *keyring = sb->s_master_keys; + size_t i; + + if (!keyring) + return; + + for (i = 0; i < ARRAY_SIZE(keyring->key_hashtable); i++) { + struct fscrypt_master_key *mk; + + hlist_for_each_entry(mk, &keyring->key_hashtable[i], mk_node) { + struct fscrypt_inode_info *ci; + struct fscrypt_mode_key *node; + + crash_wipe_memzero(&mk->mk_secret, + sizeof(mk->mk_secret)); + + list_for_each_entry(node, &mk->mk_mode_keys, link) + fscrypt_crash_wipe_inline_crypt_key(&node->key); + + list_for_each_entry(ci, &mk->mk_decrypted_inodes, + ci_master_key_link) + fscrypt_crash_wipe_inline_crypt_key(&ci->ci_enc_key); + } + } +} + +/* Called far into vpanic from crash_core.c with other CPUs stopped and + * preemption disabled + */ +static int fscrypt_crash_wipe(struct notifier_block *nb, unsigned long action, + void *data) +{ + if (!super_blocks_crash_wipe(fscrypt_crash_wipe_sb)) + pr_crit("crash_wipe_secrets: can't acquire sb_lock. skipping fscrypt keys.\n"); + + fscrypt_crash_wipe_direct_keys(); + + return NOTIFY_DONE; +} + +static struct notifier_block fscrypt_crash_wipe_nb = { + .notifier_call = fscrypt_crash_wipe +}; +#endif /* CONFIG_CRASH_WIPE_SECRETS */ + void __init fscrypt_init_keyring(void) { int err; @@ -1235,4 +1297,8 @@ void __init fscrypt_init_keyring(void) if (err) panic("failed to register fscrypt-provisioning key type (%d)", err); + +#ifdef CONFIG_CRASH_WIPE_SECRETS + crash_wipe_secrets_register(&fscrypt_crash_wipe_nb); +#endif } diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index 87fe13ccb253..08dd0a682a8f 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -23,6 +23,7 @@ #include <crypto/aes.h> #include <crypto/utils.h> #include <keys/user-type.h> +#include <linux/crash_core.h> #include <linux/hashtable.h> #include "fscrypt_private.h" @@ -118,6 +119,20 @@ void fscrypt_put_direct_key(struct fscrypt_direct_key *dk) free_direct_key(dk); } +#ifdef CONFIG_CRASH_WIPE_SECRETS +void fscrypt_crash_wipe_direct_keys(void) +{ + struct fscrypt_direct_key *dk; + unsigned int i; + + /* No locking: all other CPUs are stopped, so nothing can race with us. */ + hash_for_each(fscrypt_direct_keys, i, dk, dk_node) { + crash_wipe_memzero(dk->dk_raw, sizeof(dk->dk_raw)); + fscrypt_crash_wipe_inline_crypt_key(&dk->dk_key); + } +} +#endif /* CONFIG_CRASH_WIPE_SECRETS */ + /* * Find/insert the given key into the fscrypt_direct_keys table. If found, it * is returned with elevated refcount, and 'to_insert' is freed if non-NULL. If diff --git a/fs/super.c b/fs/super.c index 5feecf5d9038..cd51e9cfe93b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -2463,3 +2463,32 @@ int sb_init_dio_done_wq(struct super_block *sb) return 0; } EXPORT_SYMBOL_GPL(sb_init_dio_done_wq); + +#ifdef CONFIG_CRASH_WIPE_SECRETS +/** + * super_blocks_crash_wipe - run @wipe against every live superblock + * @wipe: callback to invoke for each superblock + * + * Called from the panic path with other CPUs stopped and preemption disabled. + * The callback must not sleep, allocate, free or take locks. + * + * Returns false without doing anything if @sb_lock could not be acquired, in + * which case the list may be inconsistent and walking it is unsafe. + */ +bool super_blocks_crash_wipe(void (*wipe)(struct super_block *sb)) +{ + struct super_block *sb; + + /* There is no point in waiting for a lock that will never be released + * at this stage. + */ + if (!spin_trylock(&sb_lock)) + return false; + + list_for_each_entry(sb, &super_blocks, s_list) + wipe(sb); + + spin_unlock(&sb_lock); + return true; +} +#endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 072d8cd09a0b..7832a77908d2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2378,6 +2378,10 @@ extern __printf(2, 3) int super_setup_bdi_name(struct super_block *sb, char *fmt, ...); extern int super_setup_bdi(struct super_block *sb); +#ifdef CONFIG_CRASH_WIPE_SECRETS +bool super_blocks_crash_wipe(void (*wipe)(struct super_block *sb)); +#endif + static inline void super_set_uuid(struct super_block *sb, const u8 *uuid, unsigned len) { if (WARN_ON(len > sizeof(sb->s_uuid))) -- 2.53.0

