lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <20200617175931.GB115256@google.com> Date: Wed, 17 Jun 2020 10:59:31 -0700 From: Jaegeuk Kim <jaegeuk@...nel.org> To: Satya Tangirala <satyat@...gle.com> Cc: linux-fscrypt@...r.kernel.org, linux-fsdevel@...r.kernel.org, linux-f2fs-devel@...ts.sourceforge.net, linux-ext4@...r.kernel.org, Eric Biggers <ebiggers@...gle.com> Subject: Re: [PATCH 2/4] fscrypt: add inline encryption support On 06/17, Satya Tangirala wrote: > Add support for inline encryption to fs/crypto/. With "inline > encryption", the block layer handles the decryption/encryption as part > of the bio, instead of the filesystem doing the crypto itself via > Linux's crypto API. This model is needed in order to take advantage of > the inline encryption hardware present on most modern mobile SoCs. > > To use inline encryption, the filesystem needs to be mounted with > '-o inlinecrypt'. The contents of any encrypted files will then be > encrypted using blk-crypto, instead of using the traditional > filesystem-layer crypto. Fscrypt still provides the key and IV to use, > and the actual ciphertext on-disk is still the same; therefore it's > testable using the existing fscrypt ciphertext verification tests. > > Note that since blk-crypto has a fallback to Linux's crypto API, and > also supports all the encryption modes currently supported by fscrypt, > this feature is usable and testable even without actual inline > encryption hardware. > > Per-filesystem changes will be needed to set encryption contexts when > submitting bios and to implement the 'inlinecrypt' mount option. This > patch just adds the common code. > > Co-developed-by: Eric Biggers <ebiggers@...gle.com> > Signed-off-by: Eric Biggers <ebiggers@...gle.com> > Signed-off-by: Satya Tangirala <satyat@...gle.com> Reviewed-by: Jaegeuk Kim <jaegeuk@...nel.org> > --- > fs/crypto/Kconfig | 6 + > fs/crypto/Makefile | 1 + > fs/crypto/bio.c | 50 ++++++ > fs/crypto/crypto.c | 2 +- > fs/crypto/fname.c | 4 +- > fs/crypto/fscrypt_private.h | 118 ++++++++++-- > fs/crypto/inline_crypt.c | 349 ++++++++++++++++++++++++++++++++++++ > fs/crypto/keyring.c | 6 +- > fs/crypto/keysetup.c | 68 ++++--- > fs/crypto/keysetup_v1.c | 16 +- > include/linux/fscrypt.h | 82 +++++++++ > 11 files changed, 655 insertions(+), 47 deletions(-) > create mode 100644 fs/crypto/inline_crypt.c > > diff --git a/fs/crypto/Kconfig b/fs/crypto/Kconfig > index 8046d7c7a3e9..f1f11a6228eb 100644 > --- a/fs/crypto/Kconfig > +++ b/fs/crypto/Kconfig > @@ -24,3 +24,9 @@ config FS_ENCRYPTION_ALGS > select CRYPTO_SHA256 > select CRYPTO_SHA512 > select CRYPTO_XTS > + > +config FS_ENCRYPTION_INLINE_CRYPT > + bool "Enable fscrypt to use inline crypto" > + depends on FS_ENCRYPTION && BLK_INLINE_ENCRYPTION > + help > + Enable fscrypt to use inline encryption hardware if available. > diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile > index 232e2bb5a337..652c7180ec6d 100644 > --- a/fs/crypto/Makefile > +++ b/fs/crypto/Makefile > @@ -11,3 +11,4 @@ fscrypto-y := crypto.o \ > policy.o > > fscrypto-$(CONFIG_BLOCK) += bio.o > +fscrypto-$(CONFIG_FS_ENCRYPTION_INLINE_CRYPT) += inline_crypt.o > diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c > index 4fa18fff9c4e..1ea9369a7688 100644 > --- a/fs/crypto/bio.c > +++ b/fs/crypto/bio.c > @@ -41,6 +41,52 @@ void fscrypt_decrypt_bio(struct bio *bio) > } > EXPORT_SYMBOL(fscrypt_decrypt_bio); > > +static int fscrypt_zeroout_range_inline_crypt(const struct inode *inode, > + pgoff_t lblk, sector_t pblk, > + unsigned int len) > +{ > + const unsigned int blockbits = inode->i_blkbits; > + const unsigned int blocks_per_page = 1 << (PAGE_SHIFT - blockbits); > + struct bio *bio; > + int ret, err = 0; > + int num_pages = 0; > + > + /* This always succeeds since __GFP_DIRECT_RECLAIM is set. */ > + bio = bio_alloc(GFP_NOFS, BIO_MAX_PAGES); > + > + while (len) { > + unsigned int blocks_this_page = min(len, blocks_per_page); > + unsigned int bytes_this_page = blocks_this_page << blockbits; > + > + if (num_pages == 0) { > + fscrypt_set_bio_crypt_ctx(bio, inode, lblk, GFP_NOFS); > + bio_set_dev(bio, inode->i_sb->s_bdev); > + bio->bi_iter.bi_sector = > + pblk << (blockbits - SECTOR_SHIFT); > + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); > + } > + ret = bio_add_page(bio, ZERO_PAGE(0), bytes_this_page, 0); > + if (WARN_ON(ret != bytes_this_page)) { > + err = -EIO; > + goto out; > + } > + num_pages++; > + len -= blocks_this_page; > + lblk += blocks_this_page; > + pblk += blocks_this_page; > + if (num_pages == BIO_MAX_PAGES || !len) { > + err = submit_bio_wait(bio); > + if (err) > + goto out; > + bio_reset(bio); > + num_pages = 0; > + } > + } > +out: > + bio_put(bio); > + return err; > +} > + > /** > * fscrypt_zeroout_range() - zero out a range of blocks in an encrypted file > * @inode: the file's inode > @@ -75,6 +121,10 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, > if (len == 0) > return 0; > > + if (fscrypt_inode_uses_inline_crypto(inode)) > + return fscrypt_zeroout_range_inline_crypt(inode, lblk, pblk, > + len); > + > BUILD_BUG_ON(ARRAY_SIZE(pages) > BIO_MAX_PAGES); > nr_pages = min_t(unsigned int, ARRAY_SIZE(pages), > (len + blocks_per_page - 1) >> blocks_per_page_bits); > diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c > index ed015cb66c7c..a52cf32733ab 100644 > --- a/fs/crypto/crypto.c > +++ b/fs/crypto/crypto.c > @@ -100,7 +100,7 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw, > DECLARE_CRYPTO_WAIT(wait); > struct scatterlist dst, src; > struct fscrypt_info *ci = inode->i_crypt_info; > - struct crypto_skcipher *tfm = ci->ci_ctfm; > + struct crypto_skcipher *tfm = ci->ci_enc_key.tfm; > int res = 0; > > if (WARN_ON_ONCE(len <= 0)) > diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c > index 83ca5f1e7934..d828e3df898b 100644 > --- a/fs/crypto/fname.c > +++ b/fs/crypto/fname.c > @@ -115,7 +115,7 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname, > struct skcipher_request *req = NULL; > DECLARE_CRYPTO_WAIT(wait); > const struct fscrypt_info *ci = inode->i_crypt_info; > - struct crypto_skcipher *tfm = ci->ci_ctfm; > + struct crypto_skcipher *tfm = ci->ci_enc_key.tfm; > union fscrypt_iv iv; > struct scatterlist sg; > int res; > @@ -171,7 +171,7 @@ static int fname_decrypt(const struct inode *inode, > DECLARE_CRYPTO_WAIT(wait); > struct scatterlist src_sg, dst_sg; > const struct fscrypt_info *ci = inode->i_crypt_info; > - struct crypto_skcipher *tfm = ci->ci_ctfm; > + struct crypto_skcipher *tfm = ci->ci_enc_key.tfm; > union fscrypt_iv iv; > int res; > > diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h > index eb7fcd2b7fb8..1572186b0db4 100644 > --- a/fs/crypto/fscrypt_private.h > +++ b/fs/crypto/fscrypt_private.h > @@ -14,6 +14,7 @@ > #include <linux/fscrypt.h> > #include <linux/siphash.h> > #include <crypto/hash.h> > +#include <linux/blk-crypto.h> > > #define CONST_STRLEN(str) (sizeof(str) - 1) > > @@ -166,6 +167,20 @@ struct fscrypt_symlink_data { > char encrypted_path[1]; > } __packed; > > +/** > + * struct fscrypt_prepared_key - a key prepared for actual encryption/decryption > + * @tfm: crypto API transform object > + * @blk_key: key for blk-crypto > + * > + * Normally only one of the fields will be non-NULL. > + */ > +struct fscrypt_prepared_key { > + struct crypto_skcipher *tfm; > +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT > + struct fscrypt_blk_crypto_key *blk_key; > +#endif > +}; > + > /* > * fscrypt_info - the "encryption key" for an inode > * > @@ -175,12 +190,23 @@ struct fscrypt_symlink_data { > */ > struct fscrypt_info { > > - /* The actual crypto transform used for encryption and decryption */ > - struct crypto_skcipher *ci_ctfm; > + /* The key in a form prepared for actual encryption/decryption */ > + struct fscrypt_prepared_key ci_enc_key; > > - /* True if the key should be freed when this fscrypt_info is freed */ > + /* > + * True if the ci_enc_key should be freed when this fscrypt_info is > + * freed > + */ > bool ci_owns_key; > > +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT > + /* > + * True if this inode will use inline encryption (blk-crypto) instead of > + * the traditional filesystem-layer encryption. > + */ > + bool ci_inlinecrypt; > +#endif > + > /* > * Encryption mode used for this inode. It corresponds to either the > * contents or filenames encryption mode, depending on the inode type. > @@ -205,7 +231,7 @@ struct fscrypt_info { > > /* > * If non-NULL, then encryption is done using the master key directly > - * and ci_ctfm will equal ci_direct_key->dk_ctfm. > + * and ci_enc_key will equal ci_direct_key->dk_key. > */ > struct fscrypt_direct_key *ci_direct_key; > > @@ -260,6 +286,7 @@ union fscrypt_iv { > u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE]; > }; > u8 raw[FSCRYPT_MAX_IV_SIZE]; > + __le64 dun[FSCRYPT_MAX_IV_SIZE / sizeof(__le64)]; > }; > > void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, > @@ -302,6 +329,74 @@ int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context, > > void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf); > > +/* inline_crypt.c */ > +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT > +void fscrypt_select_encryption_impl(struct fscrypt_info *ci); > + > +static inline bool > +fscrypt_using_inline_encryption(const struct fscrypt_info *ci) > +{ > + return ci->ci_inlinecrypt; > +} > + > +int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, > + const u8 *raw_key, > + const struct fscrypt_info *ci); > + > +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key); > + > +/* > + * Check whether the crypto transform or blk-crypto key has been allocated in > + * @prep_key, depending on which encryption implementation the file will use. > + */ > +static inline bool > +fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, > + const struct fscrypt_info *ci) > +{ > + /* > + * The READ_ONCE() here pairs with the smp_store_release() in > + * fscrypt_prepare_key(). (This only matters for the per-mode keys, > + * which are shared by multiple inodes.) > + */ > + if (fscrypt_using_inline_encryption(ci)) > + return READ_ONCE(prep_key->blk_key) != NULL; > + return READ_ONCE(prep_key->tfm) != NULL; > +} > + > +#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ > + > +static inline void fscrypt_select_encryption_impl(struct fscrypt_info *ci) > +{ > +} > + > +static inline bool fscrypt_using_inline_encryption( > + const struct fscrypt_info *ci) > +{ > + return false; > +} > + > +static inline int > +fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, > + const u8 *raw_key, > + const struct fscrypt_info *ci) > +{ > + WARN_ON(1); > + return -EOPNOTSUPP; > +} > + > +static inline void > +fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) > +{ > +} > + > +static inline bool > +fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, > + const struct fscrypt_info *ci) > +{ > + return READ_ONCE(prep_key->tfm) != NULL; > +} > +#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ > + > /* keyring.c */ > > /* > @@ -395,9 +490,9 @@ struct fscrypt_master_key { > * Per-mode encryption keys for the various types of encryption policies > * that use them. Allocated and derived on-demand. > */ > - struct crypto_skcipher *mk_direct_keys[__FSCRYPT_MODE_MAX + 1]; > - struct crypto_skcipher *mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1]; > - struct crypto_skcipher *mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1]; > + struct fscrypt_prepared_key mk_direct_keys[__FSCRYPT_MODE_MAX + 1]; > + struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1]; > + struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1]; > > /* Hash key for inode numbers. Initialized only when needed. */ > siphash_key_t mk_ino_hash_key; > @@ -461,13 +556,16 @@ struct fscrypt_mode { > int keysize; > int ivsize; > int logged_impl_name; > + enum blk_crypto_mode_num blk_crypto_mode; > }; > > extern struct fscrypt_mode fscrypt_modes[]; > > -struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode, > - const u8 *raw_key, > - const struct inode *inode); > +int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, > + const u8 *raw_key, > + const struct fscrypt_info *ci); > + > +void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key); > > int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key); > > diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c > new file mode 100644 > index 000000000000..7dbeb49260a6 > --- /dev/null > +++ b/fs/crypto/inline_crypt.c > @@ -0,0 +1,349 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Inline encryption support for fscrypt > + * > + * Copyright 2019 Google LLC > + */ > + > +/* > + * With "inline encryption", the block layer handles the decryption/encryption > + * as part of the bio, instead of the filesystem doing the crypto itself via > + * crypto API. See Documentation/block/inline-encryption.rst. fscrypt still > + * provides the key and IV to use. > + */ > + > +#include <linux/blk-crypto.h> > +#include <linux/blkdev.h> > +#include <linux/buffer_head.h> > +#include <linux/sched/mm.h> > + > +#include "fscrypt_private.h" > + > +struct fscrypt_blk_crypto_key { > + struct blk_crypto_key base; > + int num_devs; > + 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); > +} > + > +static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_info *ci) > +{ > + struct super_block *sb = ci->ci_inode->i_sb; > + unsigned int flags = fscrypt_policy_flags(&ci->ci_policy); > + int ino_bits = 64, lblk_bits = 64; > + > + if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) > + return offsetofend(union fscrypt_iv, nonce); > + > + if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) > + return sizeof(__le64); > + > + if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) > + return sizeof(__le32); > + > + /* Default case: IVs are just the file logical block number */ > + if (sb->s_cop->get_ino_and_lblk_bits) > + sb->s_cop->get_ino_and_lblk_bits(sb, &ino_bits, &lblk_bits); > + return DIV_ROUND_UP(lblk_bits, 8); > +} > + > +/* Enable inline encryption for this file if supported. */ > +void fscrypt_select_encryption_impl(struct fscrypt_info *ci) > +{ > + const struct inode *inode = ci->ci_inode; > + struct super_block *sb = inode->i_sb; > + struct blk_crypto_config crypto_cfg; > + int num_devs; > + struct request_queue **devs; > + int i; > + > + /* The file must need contents encryption, not filenames encryption */ > + if (!fscrypt_needs_contents_encryption(inode)) > + return; > + > + /* The crypto mode must be valid */ > + if (ci->ci_mode->blk_crypto_mode == BLK_ENCRYPTION_MODE_INVALID) > + return; > + > + /* The filesystem must be mounted with -o inlinecrypt */ > + if (!(sb->s_flags & SB_INLINECRYPT)) > + return; > + > + /* > + * blk-crypto must support the crypto configuration we'll use for the > + * inode on all devices in the sb > + */ > + crypto_cfg.crypto_mode = ci->ci_mode->blk_crypto_mode; > + crypto_cfg.data_unit_size = sb->s_blocksize; > + crypto_cfg.dun_bytes = fscrypt_get_dun_bytes(ci); > + num_devs = fscrypt_get_num_devices(sb); > + devs = kmalloc_array(num_devs, sizeof(*devs), GFP_NOFS); > + if (!devs) > + return; > + fscrypt_get_devices(sb, num_devs, devs); > + > + for (i = 0; i < num_devs; i++) { > + if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) > + goto out_free_devs; > + } > + > + ci->ci_inlinecrypt = true; > +out_free_devs: > + kfree(devs); > +} > + > +int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, > + const u8 *raw_key, > + const struct fscrypt_info *ci) > +{ > + 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 = fscrypt_get_num_devices(sb); > + int queue_refs = 0; > + struct fscrypt_blk_crypto_key *blk_key; > + int err; > + int i; > + unsigned int flags; > + > + blk_key = kzalloc(struct_size(blk_key, devs, num_devs), GFP_NOFS); > + if (!blk_key) > + return -ENOMEM; > + > + blk_key->num_devs = num_devs; > + fscrypt_get_devices(sb, num_devs, blk_key->devs); > + > + err = blk_crypto_init_key(&blk_key->base, raw_key, crypto_mode, > + fscrypt_get_dun_bytes(ci), sb->s_blocksize); > + if (err) { > + fscrypt_err(inode, "error %d initializing blk-crypto key", err); > + goto fail; > + } > + > + /* > + * We have to start using blk-crypto on all the filesystem's devices. > + * We also have to save all the request_queue's for later so that the > + * key can be evicted from them. This is needed because some keys > + * aren't destroyed until after the filesystem was already unmounted > + * (namely, the per-mode keys in struct fscrypt_master_key). > + */ > + for (i = 0; i < num_devs; i++) { > + if (!blk_get_queue(blk_key->devs[i])) { > + fscrypt_err(inode, "couldn't get request_queue"); > + err = -EAGAIN; > + goto fail; > + } > + queue_refs++; > + > + flags = memalloc_nofs_save(); > + err = blk_crypto_start_using_key(&blk_key->base, > + blk_key->devs[i]); > + memalloc_nofs_restore(flags); > + if (err) { > + fscrypt_err(inode, > + "error %d starting to use blk-crypto", err); > + goto fail; > + } > + } > + /* > + * Pairs with READ_ONCE() in fscrypt_is_key_prepared(). (Only matters > + * for the per-mode keys, which are shared by multiple inodes.) > + */ > + smp_store_release(&prep_key->blk_key, blk_key); > + return 0; > + > +fail: > + for (i = 0; i < queue_refs; i++) > + blk_put_queue(blk_key->devs[i]); > + kzfree(blk_key); > + return err; > +} > + > +void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) > +{ > + struct fscrypt_blk_crypto_key *blk_key = prep_key->blk_key; > + int i; > + > + if (blk_key) { > + for (i = 0; i < blk_key->num_devs; i++) { > + blk_crypto_evict_key(blk_key->devs[i], &blk_key->base); > + blk_put_queue(blk_key->devs[i]); > + } > + kzfree(blk_key); > + } > +} > + > +bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) > +{ > + return inode->i_crypt_info->ci_inlinecrypt; > +} > +EXPORT_SYMBOL_GPL(__fscrypt_inode_uses_inline_crypto); > + > +static void fscrypt_generate_dun(const struct fscrypt_info *ci, u64 lblk_num, > + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]) > +{ > + union fscrypt_iv iv; > + int i; > + > + fscrypt_generate_iv(&iv, lblk_num, ci); > + > + BUILD_BUG_ON(FSCRYPT_MAX_IV_SIZE > BLK_CRYPTO_MAX_IV_SIZE); > + memset(dun, 0, BLK_CRYPTO_MAX_IV_SIZE); > + for (i = 0; i < ci->ci_mode->ivsize/sizeof(dun[0]); i++) > + dun[i] = le64_to_cpu(iv.dun[i]); > +} > + > +/** > + * fscrypt_set_bio_crypt_ctx() - prepare a file contents bio for inline crypto > + * @bio: a bio which will eventually be submitted to the file > + * @inode: the file's inode > + * @first_lblk: the first file logical block number in the I/O > + * @gfp_mask: memory allocation flags - these must be a waiting mask so that > + * bio_crypt_set_ctx can't fail. > + * > + * If the contents of the file should be encrypted (or decrypted) with inline > + * encryption, then assign the appropriate encryption context to the bio. > + * > + * Normally the bio should be newly allocated (i.e. no pages added yet), as > + * otherwise fscrypt_mergeable_bio() won't work as intended. > + * > + * The encryption context will be freed automatically when the bio is freed. > + */ > +void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode, > + u64 first_lblk, gfp_t gfp_mask) > +{ > + const struct fscrypt_info *ci = inode->i_crypt_info; > + u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; > + > + if (!fscrypt_inode_uses_inline_crypto(inode)) > + return; > + > + fscrypt_generate_dun(ci, first_lblk, dun); > + bio_crypt_set_ctx(bio, &ci->ci_enc_key.blk_key->base, dun, gfp_mask); > +} > +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); > + > +/* Extract the inode and logical block number from a buffer_head. */ > +static bool bh_get_inode_and_lblk_num(const struct buffer_head *bh, > + const struct inode **inode_ret, > + u64 *lblk_num_ret) > +{ > + struct page *page = bh->b_page; > + const struct address_space *mapping; > + const struct inode *inode; > + > + /* > + * The ext4 journal (jbd2) can submit a buffer_head it directly created > + * for a non-pagecache page. fscrypt doesn't care about these. > + */ > + mapping = page_mapping(page); > + if (!mapping) > + return false; > + inode = mapping->host; > + > + *inode_ret = inode; > + *lblk_num_ret = ((u64)page->index << (PAGE_SHIFT - inode->i_blkbits)) + > + (bh_offset(bh) >> inode->i_blkbits); > + return true; > +} > + > +/** > + * fscrypt_set_bio_crypt_ctx_bh() - prepare a file contents bio for inline > + * crypto > + * @bio: a bio which will eventually be submitted to the file > + * @first_bh: the first buffer_head for which I/O will be submitted > + * @gfp_mask: memory allocation flags > + * > + * Same as fscrypt_set_bio_crypt_ctx(), except this takes a buffer_head instead > + * of an inode and block number directly. > + */ > +void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio, > + const struct buffer_head *first_bh, > + gfp_t gfp_mask) > +{ > + const struct inode *inode; > + u64 first_lblk; > + > + if (bh_get_inode_and_lblk_num(first_bh, &inode, &first_lblk)) > + fscrypt_set_bio_crypt_ctx(bio, inode, first_lblk, gfp_mask); > +} > +EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx_bh); > + > +/** > + * fscrypt_mergeable_bio() - test whether data can be added to a bio > + * @bio: the bio being built up > + * @inode: the inode for the next part of the I/O > + * @next_lblk: the next file logical block number in the I/O > + * > + * When building a bio which may contain data which should undergo inline > + * encryption (or decryption) via fscrypt, filesystems should call this function > + * to ensure that the resulting bio contains only logically contiguous data. > + * This will return false if the next part of the I/O cannot be merged with the > + * bio because either the encryption key would be different or the encryption > + * data unit numbers would be discontiguous. > + * > + * fscrypt_set_bio_crypt_ctx() must have already been called on the bio. > + * > + * Return: true iff the I/O is mergeable > + */ > +bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, > + u64 next_lblk) > +{ > + const struct bio_crypt_ctx *bc = bio->bi_crypt_context; > + u64 next_dun[BLK_CRYPTO_DUN_ARRAY_SIZE]; > + > + if (!!bc != fscrypt_inode_uses_inline_crypto(inode)) > + return false; > + if (!bc) > + return true; > + > + /* > + * Comparing the key pointers is good enough, as all I/O for each key > + * uses the same pointer. I.e., there's currently no need to support > + * merging requests where the keys are the same but the pointers differ. > + */ > + if (bc->bc_key != &inode->i_crypt_info->ci_enc_key.blk_key->base) > + return false; > + > + fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun); > + return bio_crypt_dun_is_contiguous(bc, bio->bi_iter.bi_size, next_dun); > +} > +EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio); > + > +/** > + * fscrypt_mergeable_bio_bh() - test whether data can be added to a bio > + * @bio: the bio being built up > + * @next_bh: the next buffer_head for which I/O will be submitted > + * > + * Same as fscrypt_mergeable_bio(), except this takes a buffer_head instead of > + * an inode and block number directly. > + * > + * Return: true iff the I/O is mergeable > + */ > +bool fscrypt_mergeable_bio_bh(struct bio *bio, > + const struct buffer_head *next_bh) > +{ > + const struct inode *inode; > + u64 next_lblk; > + > + if (!bh_get_inode_and_lblk_num(next_bh, &inode, &next_lblk)) > + return !bio->bi_crypt_context; > + > + return fscrypt_mergeable_bio(bio, inode, next_lblk); > +} > +EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh); > diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c > index e24eb48bfbe1..7f8ac61a20d6 100644 > --- a/fs/crypto/keyring.c > +++ b/fs/crypto/keyring.c > @@ -45,9 +45,9 @@ static void free_master_key(struct fscrypt_master_key *mk) > wipe_master_key_secret(&mk->mk_secret); > > for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) { > - crypto_free_skcipher(mk->mk_direct_keys[i]); > - crypto_free_skcipher(mk->mk_iv_ino_lblk_64_keys[i]); > - crypto_free_skcipher(mk->mk_iv_ino_lblk_32_keys[i]); > + fscrypt_destroy_prepared_key(&mk->mk_direct_keys[i]); > + fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_64_keys[i]); > + fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_32_keys[i]); > } > > key_put(mk->mk_users); > diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c > index 1129adfa097d..c789ac30af7c 100644 > --- a/fs/crypto/keysetup.c > +++ b/fs/crypto/keysetup.c > @@ -19,6 +19,7 @@ struct fscrypt_mode fscrypt_modes[] = { > .cipher_str = "xts(aes)", > .keysize = 64, > .ivsize = 16, > + .blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_256_XTS, > }, > [FSCRYPT_MODE_AES_256_CTS] = { > .friendly_name = "AES-256-CTS-CBC", > @@ -31,6 +32,7 @@ struct fscrypt_mode fscrypt_modes[] = { > .cipher_str = "essiv(cbc(aes),sha256)", > .keysize = 16, > .ivsize = 16, > + .blk_crypto_mode = BLK_ENCRYPTION_MODE_AES_128_CBC_ESSIV, > }, > [FSCRYPT_MODE_AES_128_CTS] = { > .friendly_name = "AES-128-CTS-CBC", > @@ -43,6 +45,7 @@ struct fscrypt_mode fscrypt_modes[] = { > .cipher_str = "adiantum(xchacha12,aes)", > .keysize = 32, > .ivsize = 32, > + .blk_crypto_mode = BLK_ENCRYPTION_MODE_ADIANTUM, > }, > }; > > @@ -64,9 +67,9 @@ select_encryption_mode(const union fscrypt_policy *policy, > } > > /* Create a symmetric cipher object for the given encryption mode and key */ > -struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode, > - const u8 *raw_key, > - const struct inode *inode) > +static struct crypto_skcipher * > +fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key, > + const struct inode *inode) > { > struct crypto_skcipher *tfm; > int err; > @@ -109,30 +112,54 @@ struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode, > return ERR_PTR(err); > } > > -/* Given a per-file encryption key, set up the file's crypto transform object */ > -int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key) > +/* > + * Prepare the crypto transform object or blk-crypto key in @prep_key, given the > + * raw key, encryption mode, and flag indicating which encryption implementation > + * (fs-layer or blk-crypto) will be used. > + */ > +int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, > + const u8 *raw_key, const struct fscrypt_info *ci) > { > struct crypto_skcipher *tfm; > > + if (fscrypt_using_inline_encryption(ci)) > + return fscrypt_prepare_inline_crypt_key(prep_key, raw_key, ci); > + > tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); > if (IS_ERR(tfm)) > return PTR_ERR(tfm); > + /* > + * Pairs with READ_ONCE() in fscrypt_is_key_prepared(). (Only matters > + * for the per-mode keys, which are shared by multiple inodes.) > + */ > + smp_store_release(&prep_key->tfm, tfm); > + return 0; > +} > + > +/* Destroy a crypto transform object and/or blk-crypto key. */ > +void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key) > +{ > + crypto_free_skcipher(prep_key->tfm); > + fscrypt_destroy_inline_crypt_key(prep_key); > +} > > - ci->ci_ctfm = tfm; > +/* Given a per-file encryption key, set up the file's crypto transform object */ > +int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key) > +{ > ci->ci_owns_key = true; > - return 0; > + return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); > } > > static int setup_per_mode_enc_key(struct fscrypt_info *ci, > struct fscrypt_master_key *mk, > - struct crypto_skcipher **tfms, > + struct fscrypt_prepared_key *keys, > u8 hkdf_context, bool include_fs_uuid) > { > const struct inode *inode = ci->ci_inode; > const struct super_block *sb = inode->i_sb; > struct fscrypt_mode *mode = ci->ci_mode; > const u8 mode_num = mode - fscrypt_modes; > - struct crypto_skcipher *tfm; > + struct fscrypt_prepared_key *prep_key; > u8 mode_key[FSCRYPT_MAX_KEY_SIZE]; > u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; > unsigned int hkdf_infolen = 0; > @@ -141,16 +168,15 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, > if (WARN_ON(mode_num > __FSCRYPT_MODE_MAX)) > return -EINVAL; > > - /* pairs with smp_store_release() below */ > - tfm = READ_ONCE(tfms[mode_num]); > - if (likely(tfm != NULL)) { > - ci->ci_ctfm = tfm; > + prep_key = &keys[mode_num]; > + if (fscrypt_is_key_prepared(prep_key, ci)) { > + ci->ci_enc_key = *prep_key; > return 0; > } > > mutex_lock(&fscrypt_mode_key_setup_mutex); > > - if (tfms[mode_num]) > + if (fscrypt_is_key_prepared(prep_key, ci)) > goto done_unlock; > > BUILD_BUG_ON(sizeof(mode_num) != 1); > @@ -167,16 +193,12 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, > mode_key, mode->keysize); > if (err) > goto out_unlock; > - tfm = fscrypt_allocate_skcipher(mode, mode_key, inode); > + err = fscrypt_prepare_key(prep_key, mode_key, ci); > memzero_explicit(mode_key, mode->keysize); > - if (IS_ERR(tfm)) { > - err = PTR_ERR(tfm); > + if (err) > goto out_unlock; > - } > - /* pairs with READ_ONCE() above */ > - smp_store_release(&tfms[mode_num], tfm); > done_unlock: > - ci->ci_ctfm = tfm; > + ci->ci_enc_key = *prep_key; > err = 0; > out_unlock: > mutex_unlock(&fscrypt_mode_key_setup_mutex); > @@ -310,6 +332,8 @@ 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; > @@ -402,7 +426,7 @@ static void put_crypt_info(struct fscrypt_info *ci) > if (ci->ci_direct_key) > fscrypt_put_direct_key(ci->ci_direct_key); > else if (ci->ci_owns_key) > - crypto_free_skcipher(ci->ci_ctfm); > + fscrypt_destroy_prepared_key(&ci->ci_enc_key); > > key = ci->ci_master_key; > if (key) { > diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c > index 801b48c0cd7f..a52686729a67 100644 > --- a/fs/crypto/keysetup_v1.c > +++ b/fs/crypto/keysetup_v1.c > @@ -146,7 +146,7 @@ struct fscrypt_direct_key { > struct hlist_node dk_node; > refcount_t dk_refcount; > const struct fscrypt_mode *dk_mode; > - struct crypto_skcipher *dk_ctfm; > + struct fscrypt_prepared_key dk_key; > u8 dk_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; > u8 dk_raw[FSCRYPT_MAX_KEY_SIZE]; > }; > @@ -154,7 +154,7 @@ struct fscrypt_direct_key { > static void free_direct_key(struct fscrypt_direct_key *dk) > { > if (dk) { > - crypto_free_skcipher(dk->dk_ctfm); > + fscrypt_destroy_prepared_key(&dk->dk_key); > kzfree(dk); > } > } > @@ -199,6 +199,8 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, > continue; > if (ci->ci_mode != dk->dk_mode) > continue; > + if (!fscrypt_is_key_prepared(&dk->dk_key, ci)) > + continue; > if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) > continue; > /* using existing tfm with same (descriptor, mode, raw_key) */ > @@ -231,13 +233,9 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key) > return ERR_PTR(-ENOMEM); > refcount_set(&dk->dk_refcount, 1); > dk->dk_mode = ci->ci_mode; > - dk->dk_ctfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, > - ci->ci_inode); > - if (IS_ERR(dk->dk_ctfm)) { > - err = PTR_ERR(dk->dk_ctfm); > - dk->dk_ctfm = NULL; > + err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci); > + if (err) > goto err_free_dk; > - } > memcpy(dk->dk_descriptor, ci->ci_policy.v1.master_key_descriptor, > FSCRYPT_KEY_DESCRIPTOR_SIZE); > memcpy(dk->dk_raw, raw_key, ci->ci_mode->keysize); > @@ -259,7 +257,7 @@ static int setup_v1_file_key_direct(struct fscrypt_info *ci, > if (IS_ERR(dk)) > return PTR_ERR(dk); > ci->ci_direct_key = dk; > - ci->ci_ctfm = dk->dk_ctfm; > + ci->ci_enc_key = dk->dk_key; > return 0; > } > > diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h > index 2862ca5fea33..bb257411365f 100644 > --- a/include/linux/fscrypt.h > +++ b/include/linux/fscrypt.h > @@ -69,6 +69,9 @@ struct fscrypt_operations { > bool (*has_stable_inodes)(struct super_block *sb); > void (*get_ino_and_lblk_bits)(struct super_block *sb, > int *ino_bits_ret, int *lblk_bits_ret); > + int (*get_num_devices)(struct super_block *sb); > + void (*get_devices)(struct super_block *sb, > + struct request_queue **devs); > }; > > static inline bool fscrypt_has_encryption_key(const struct inode *inode) > @@ -537,6 +540,85 @@ static inline void fscrypt_set_ops(struct super_block *sb, > > #endif /* !CONFIG_FS_ENCRYPTION */ > > +/* inline_crypt.c */ > +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT > + > +bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode); > + > +void fscrypt_set_bio_crypt_ctx(struct bio *bio, > + const struct inode *inode, u64 first_lblk, > + gfp_t gfp_mask); > + > +void fscrypt_set_bio_crypt_ctx_bh(struct bio *bio, > + const struct buffer_head *first_bh, > + gfp_t gfp_mask); > + > +bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, > + u64 next_lblk); > + > +bool fscrypt_mergeable_bio_bh(struct bio *bio, > + const struct buffer_head *next_bh); > + > +#else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ > + > +static inline bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) > +{ > + return false; > +} > + > +static inline void fscrypt_set_bio_crypt_ctx(struct bio *bio, > + const struct inode *inode, > + u64 first_lblk, gfp_t gfp_mask) { } > + > +static inline void fscrypt_set_bio_crypt_ctx_bh( > + struct bio *bio, > + const struct buffer_head *first_bh, > + gfp_t gfp_mask) { } > + > +static inline bool fscrypt_mergeable_bio(struct bio *bio, > + const struct inode *inode, > + u64 next_lblk) > +{ > + return true; > +} > + > +static inline bool fscrypt_mergeable_bio_bh(struct bio *bio, > + const struct buffer_head *next_bh) > +{ > + return true; > +} > +#endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ > + > +/** > + * fscrypt_inode_uses_inline_crypto() - test whether an inode uses inline > + * encryption > + * @inode: an inode. If encrypted, its key must be set up. > + * > + * Return: true if the inode requires file contents encryption and if the > + * encryption should be done in the block layer via blk-crypto rather > + * than in the filesystem layer. > + */ > +static inline bool fscrypt_inode_uses_inline_crypto(const struct inode *inode) > +{ > + return fscrypt_needs_contents_encryption(inode) && > + __fscrypt_inode_uses_inline_crypto(inode); > +} > + > +/** > + * fscrypt_inode_uses_fs_layer_crypto() - test whether an inode uses fs-layer > + * encryption > + * @inode: an inode. If encrypted, its key must be set up. > + * > + * Return: true if the inode requires file contents encryption and if the > + * encryption should be done in the filesystem layer rather than in the > + * block layer via blk-crypto. > + */ > +static inline bool fscrypt_inode_uses_fs_layer_crypto(const struct inode *inode) > +{ > + return fscrypt_needs_contents_encryption(inode) && > + !__fscrypt_inode_uses_inline_crypto(inode); > +} > + > /** > * fscrypt_require_key() - require an inode's encryption key > * @inode: the inode we need the key for > -- > 2.27.0.290.gba653c62da-goog
Powered by blists - more mailing lists