From b30501efe96dcd05211d7fc9daf64400dd9cc32b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 3 Apr 2020 12:06:11 -0700 Subject: [PATCH] ANDROID: fscrypt: fall back to filesystem-layer crypto when needed When the filesystem is mounted with '-o inlinecrypt', make fscrypt fall back to filesystem-layer crypto when inline crypto won't work, e.g. due to the hardware not supporting the encryption algorithm. When blk-crypto-fallback is disabled, this fixes '-o inlinecrypt' to not break any fscrypt policies that would otherwise work. This is needed for VtsKernelEncryptionTest to pass on some devices. Bug: 137270441 Bug: 151100202 Test: 'atest vts_kernel_encryption_test' on Pixel 4 with the inline crypto patches backported, and also on Cuttlefish. Change-Id: I3e730df4608efb12d7126d1a85faddcccb566764 Signed-off-by: Eric Biggers --- fs/crypto/fscrypt_private.h | 7 ++-- fs/crypto/inline_crypt.c | 70 ++++++++++++++++++++++++++++++------- fs/crypto/keysetup.c | 10 ++++-- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index f262f823fd58..0753681cb156 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -326,7 +326,8 @@ extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); /* inline_crypt.c */ #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT -extern void fscrypt_select_encryption_impl(struct fscrypt_info *ci); +extern int fscrypt_select_encryption_impl(struct fscrypt_info *ci, + bool is_hw_wrapped_key); static inline bool fscrypt_using_inline_encryption(const struct fscrypt_info *ci) @@ -370,8 +371,10 @@ fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ -static inline void fscrypt_select_encryption_impl(struct fscrypt_info *ci) +static inline int fscrypt_select_encryption_impl(struct fscrypt_info *ci, + bool is_hw_wrapped_key) { + return 0; } static inline bool fscrypt_using_inline_encryption( diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 298bf57ca781..eaa2014ec84b 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -25,26 +25,76 @@ struct fscrypt_blk_crypto_key { struct request_queue *devs[]; }; +static int fscrypt_get_num_devices(struct super_block *sb) +{ + if (sb->s_cop->get_num_devices) + return sb->s_cop->get_num_devices(sb); + return 1; +} + +static void fscrypt_get_devices(struct super_block *sb, int num_devs, + struct request_queue **devs) +{ + if (num_devs == 1) + devs[0] = bdev_get_queue(sb->s_bdev); + else + sb->s_cop->get_devices(sb, devs); +} + /* Enable inline encryption for this file if supported. */ -void fscrypt_select_encryption_impl(struct fscrypt_info *ci) +int fscrypt_select_encryption_impl(struct fscrypt_info *ci, + bool is_hw_wrapped_key) { const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; + enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; + struct request_queue **devs; + int num_devs; + int i; /* The file must need contents encryption, not filenames encryption */ if (!S_ISREG(inode->i_mode)) - return; + return 0; /* blk-crypto must implement the needed encryption algorithm */ - if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) - return; + if (crypto_mode == BLK_ENCRYPTION_MODE_INVALID) + return 0; /* The filesystem must be mounted with -o inlinecrypt */ if (!sb->s_cop->inline_crypt_enabled || !sb->s_cop->inline_crypt_enabled(sb)) - return; + return 0; + + /* + * The needed encryption settings must be supported either by + * blk-crypto-fallback, or by hardware on all the filesystem's devices. + */ + + if (IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) && + !is_hw_wrapped_key) { + ci->ci_inlinecrypt = true; + return 0; + } + + num_devs = fscrypt_get_num_devices(sb); + devs = kmalloc_array(num_devs, sizeof(*devs), GFP_NOFS); + if (!devs) + return -ENOMEM; + + fscrypt_get_devices(sb, num_devs, devs); + + for (i = 0; i < num_devs; i++) { + if (!keyslot_manager_crypto_mode_supported(devs[i]->ksm, + crypto_mode, + sb->s_blocksize, + is_hw_wrapped_key)) + goto out_free_devs; + } ci->ci_inlinecrypt = true; +out_free_devs: + kfree(devs); + return 0; } int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, @@ -56,14 +106,13 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; - int num_devs = 1; + int num_devs; int queue_refs = 0; struct fscrypt_blk_crypto_key *blk_key; int err; int i; - if (sb->s_cop->get_num_devices) - num_devs = sb->s_cop->get_num_devices(sb); + num_devs = fscrypt_get_num_devices(sb); if (WARN_ON(num_devs < 1)) return -EINVAL; @@ -72,10 +121,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, return -ENOMEM; blk_key->num_devs = num_devs; - if (num_devs == 1) - blk_key->devs[0] = bdev_get_queue(sb->s_bdev); - else - sb->s_cop->get_devices(sb, blk_key->devs); + fscrypt_get_devices(sb, num_devs, blk_key->devs); BUILD_BUG_ON(FSCRYPT_MAX_HW_WRAPPED_KEY_SIZE > BLK_CRYPTO_MAX_WRAPPED_KEY_SIZE); diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index e52bbbe68dc8..6b98ca38fc44 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -328,8 +328,6 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, struct fscrypt_key_specifier mk_spec; int err; - fscrypt_select_encryption_impl(ci); - switch (ci->ci_policy.version) { case FSCRYPT_POLICY_V1: mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR; @@ -354,6 +352,10 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, ci->ci_policy.version != FSCRYPT_POLICY_V1) return PTR_ERR(key); + err = fscrypt_select_encryption_impl(ci, false); + if (err) + return err; + /* * As a legacy fallback for v1 policies, search for the key in * the current task's subscribed keyrings too. Don't move this @@ -388,6 +390,10 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, goto out_release_key; } + err = fscrypt_select_encryption_impl(ci, mk->mk_secret.is_hw_wrapped); + if (err) + goto out_release_key; + switch (ci->ci_policy.version) { case FSCRYPT_POLICY_V1: err = fscrypt_setup_v1_file_key(ci, mk->mk_secret.raw);