From 88bb8fcb9e21eac2a84a7d10f2a2daf5cdaedff1 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 23 Mar 2018 16:59:15 +0900 Subject: [PATCH] [COMMON] fs: support diskcipher Change-Id: I19caef3696973a6aa3dbdad9cf53a6f72cdb0fce Signed-off-by: Boojin Kim --- fs/crypto/bio.c | 58 +++++++++++++++++++++++++-------- fs/crypto/crypto.c | 27 ++++++++++++--- fs/crypto/fscrypt_private.h | 15 +++++++++ fs/crypto/keyinfo.c | 32 ++++++++++++++++++ fs/direct-io.c | 5 +++ fs/ext4/inode.c | 5 ++- fs/ext4/page-io.c | 7 ++++ fs/ext4/readpage.c | 9 +++++ include/linux/fscrypt.h | 6 ++++ include/linux/fscrypt_notsupp.h | 4 +++ include/linux/fscrypt_supp.h | 2 +- 11 files changed, 150 insertions(+), 20 deletions(-) diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 0d5e6a569d58..b182c1afd080 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -87,29 +87,42 @@ EXPORT_SYMBOL(fscrypt_pullback_bio_page); int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, sector_t pblk, unsigned int len) { - struct fscrypt_ctx *ctx; + struct fscrypt_ctx *ctx = NULL; struct page *ciphertext_page = NULL; struct bio *bio; int ret, err = 0; BUG_ON(inode->i_sb->s_blocksize != PAGE_SIZE); - ctx = fscrypt_get_ctx(inode, GFP_NOFS); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); + if (fscrypt_disk_encrypted(inode)) { + ciphertext_page = fscrypt_alloc_bounce_page(NULL, GFP_NOWAIT); + if (!ciphertext_page || IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; + } + + memset(page_address(ciphertext_page), 0, PAGE_SIZE); + ciphertext_page->mapping = inode->i_mapping; + } else { + ctx = fscrypt_get_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); - ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT); - if (IS_ERR(ciphertext_page)) { - err = PTR_ERR(ciphertext_page); - goto errout; + ciphertext_page = fscrypt_alloc_bounce_page(ctx, GFP_NOWAIT); + if (IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; + } } while (len--) { - err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk, - ZERO_PAGE(0), ciphertext_page, - PAGE_SIZE, 0, GFP_NOFS); - if (err) - goto errout; + if (ctx) { + err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk, + ZERO_PAGE(0), ciphertext_page, + PAGE_SIZE, 0, GFP_NOFS); + if (err) + goto errout; + } bio = bio_alloc(GFP_NOWAIT, 1); if (!bio) { @@ -129,6 +142,10 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, err = -EIO; goto errout; } +#ifdef CONFIG_CRYPTO_DISKCIPHER + if (fscrypt_has_encryption_key(inode)) + fscrypt_set_bio(inode, bio); +#endif err = submit_bio_wait(bio); if (err == 0 && bio->bi_status) err = -EIO; @@ -140,7 +157,20 @@ int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, } err = 0; errout: - fscrypt_release_ctx(ctx); + if (!ctx && ciphertext_page) + fscrypt_free_bounce_page(ciphertext_page); + else + fscrypt_release_ctx(ctx); return err; } EXPORT_SYMBOL(fscrypt_zeroout_range); + +void fscrypt_set_bio(const struct inode *inode, struct bio *bio) +{ +#ifdef CONFIG_CRYPTO_DISKCIPHER + if (inode->i_crypt_info->ci_dtfm) + crypto_diskcipher_set(bio, inode->i_crypt_info->ci_dtfm); +#else + return; +#endif +} diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index 732a786cce9d..f65237d137f9 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -97,6 +97,9 @@ struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) if (ci == NULL) return ERR_PTR(-ENOKEY); + if (fscrypt_disk_encrypted(inode)) + return NULL; + /* * We first try getting the ctx from a free list because in * the common case the ctx will have an allocated and @@ -188,11 +191,22 @@ int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags) { - ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); - if (ctx->w.bounce_page == NULL) + void *pool = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags); + + if (pool == NULL) return ERR_PTR(-ENOMEM); - ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL; - return ctx->w.bounce_page; + + if (ctx) { + ctx->w.bounce_page = pool; + ctx->flags |= FS_CTX_HAS_BOUNCE_BUFFER_FL; + } + + return pool; +} + +void fscrypt_free_bounce_page(void *pool) +{ + mempool_free(pool, fscrypt_bounce_page_pool); } /** @@ -239,6 +253,9 @@ struct page *fscrypt_encrypt_page(const struct inode *inode, BUG_ON(len % FS_CRYPTO_BLOCK_SIZE != 0); + if (fscrypt_disk_encrypted(inode)) + return NULL; + if (inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES) { /* with inplace-encryption we just encrypt the page */ err = fscrypt_do_page_crypto(inode, FS_ENCRYPT, lblk_num, page, @@ -301,6 +318,8 @@ int fscrypt_decrypt_page(const struct inode *inode, struct page *page, if (!(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)) BUG_ON(!PageLocked(page)); + if (fscrypt_disk_encrypted(page->mapping->host)) + return 0; return fscrypt_do_page_crypto(inode, FS_DECRYPT, lblk_num, page, page, len, offs, GFP_NOFS); } diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index c0b4f5597e1a..06e0f4766050 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -15,6 +15,7 @@ #define __FS_HAS_ENCRYPTION 1 #include #include +#include /* Encryption parameters */ #define FS_IV_SIZE 16 @@ -58,6 +59,9 @@ struct fscrypt_info { u8 ci_data_mode; u8 ci_filename_mode; u8 ci_flags; +#ifdef CONFIG_CRYPTO_DISKCIPHER + struct crypto_diskcipher *ci_dtfm; +#endif struct crypto_skcipher *ci_ctfm; struct crypto_cipher *ci_essiv_tfm; u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE]; @@ -82,8 +86,19 @@ extern int fscrypt_do_page_crypto(const struct inode *inode, gfp_t gfp_flags); extern struct page *fscrypt_alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags); +extern void fscrypt_free_bounce_page(void *pool); /* keyinfo.c */ extern void __exit fscrypt_essiv_cleanup(void); +static inline int fscrypt_disk_encrypted(const struct inode *inode) +{ +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +#if IS_ENABLED(CONFIG_CRYPTO_DISKCIPHER) + if (inode && inode->i_crypt_info) + return (inode->i_crypt_info->ci_dtfm != NULL); +#endif +#endif + return 0; +} #endif /* _FSCRYPT_PRIVATE_H */ diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 5e6e846f5a24..628b22030503 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -168,6 +168,10 @@ static void put_crypt_info(struct fscrypt_info *ci) if (!ci) return; +#if defined(CONFIG_CRYPTO_DISKCIPHER) + if (ci->ci_dtfm) + crypto_free_diskcipher(ci->ci_dtfm); +#endif crypto_free_skcipher(ci->ci_ctfm); crypto_free_cipher(ci->ci_essiv_tfm); kmem_cache_free(fscrypt_info_cachep, ci); @@ -286,6 +290,9 @@ int fscrypt_get_encryption_info(struct inode *inode) crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; crypt_info->ci_ctfm = NULL; crypt_info->ci_essiv_tfm = NULL; +#if defined(CONFIG_CRYPTO_DISKCIPHER) + crypt_info->ci_dtfm = NULL; +#endif memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, sizeof(crypt_info->ci_master_key)); @@ -316,6 +323,31 @@ int fscrypt_get_encryption_info(struct inode *inode) } else if (res) { goto out; } +#if defined(CONFIG_CRYPTO_DISKCIPHER) + if (S_ISREG(inode->i_mode)) { + /* try discipher first */ + crypt_info->ci_dtfm = crypto_alloc_diskcipher(cipher_str, 0, 0, 1); + if (crypt_info->ci_dtfm && !IS_ERR(crypt_info->ci_dtfm)) { + res = crypto_diskcipher_setkey(crypt_info->ci_dtfm, + raw_key, keysize, 0); + if (!res) { + if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL) + crypt_info = NULL; + pr_debug("%s: (inode %lu) uses diskcipher tfm\n", + __func__, inode->i_ino); + goto out; + } else { + pr_warn("%s: error %d fails to set diskciher key\n", + __func__, res); + crypto_free_diskcipher(crypt_info->ci_dtfm); + } + } + /* clear diskcipher. use skcipher */ + pr_warn("%s: (inode %lu) fails to get diskcipher (%s, %d)\n", + __func__, inode->i_ino, cipher_str, res); + crypt_info->ci_dtfm = NULL; + } +#endif ctfm = crypto_alloc_skcipher(cipher_str, 0, 0); if (!ctfm || IS_ERR(ctfm)) { res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; diff --git a/fs/direct-io.c b/fs/direct-io.c index 625a84aa6484..4aa11cdbfe2c 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -37,6 +37,7 @@ #include #include #include +#include /* * How many user pages to map in one call to get_user_pages(). This determines @@ -448,6 +449,10 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio) dio->refcount++; spin_unlock_irqrestore(&dio->bio_lock, flags); +#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER) + if (dio->inode && fscrypt_has_encryption_key(dio->inode)) + fscrypt_set_bio(dio->inode, bio); +#endif if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty) bio_set_pages_dirty(bio); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index e8017aefbd44..51b70932d045 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3823,9 +3823,12 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ssize_t ret; int rw = iov_iter_rw(iter); -#ifdef CONFIG_EXT4_FS_ENCRYPTION +#if defined(CONFIG_EXT4_FS_ENCRYPTION) && !defined(CONFIG_CRYPTO_DISKCIPHER) if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; +#elif defined(CONFIG_CRYPTO_DISKCIPHER) + if (ext4_encrypted_inode(inode) && !fscrypt_has_encryption_key(inode)) + return 0; #endif /* diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index db7590178dfc..b769a6e8b80b 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -352,6 +352,8 @@ void ext4_io_submit(struct ext4_io_submit *io) int io_op_flags = io->io_wbc->sync_mode == WB_SYNC_ALL ? REQ_SYNC : 0; io->io_bio->bi_write_hint = io->io_end->inode->i_write_hint; + if (bio->bi_opf & REQ_CRYPT) + io_op_flags |= (REQ_CRYPT | REQ_AUX_PRIV); bio_set_op_attrs(io->io_bio, REQ_OP_WRITE, io_op_flags); submit_bio(io->io_bio); } @@ -399,6 +401,11 @@ submit_and_retry: ret = io_submit_init_bio(io, bh); if (ret) return ret; +#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER) + if (ext4_encrypted_inode(inode) && + S_ISREG(inode->i_mode) && fscrypt_has_encryption_key(inode)) + fscrypt_set_bio(inode, io->io_bio); +#endif io->io_bio->bi_write_hint = inode->i_write_hint; } ret = bio_add_page(io->io_bio, page, bh->b_size, bh_offset(bh)); diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index df22fcb3c41c..25797025c1b3 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -51,6 +51,10 @@ static inline bool ext4_bio_encrypted(struct bio *bio) { #ifdef CONFIG_EXT4_FS_ENCRYPTION + /* REQ_CRYPT is used for diskcipher */ + if (bio->bi_opf & REQ_CRYPT) + return false; + return unlikely(bio->bi_private != NULL); #else return false; @@ -299,6 +303,11 @@ int ext4_mpage_readpages(struct address_space *mapping, bio->bi_end_io = mpage_end_io; bio->bi_private = ctx; bio_set_op_attrs(bio, REQ_OP_READ, 0); +#if defined(CONFIG_EXT4_FS_ENCRYPTION) && defined(CONFIG_CRYPTO_DISKCIPHER) + if (ext4_encrypted_inode(inode) && + S_ISREG(inode->i_mode) && fscrypt_has_encryption_key(inode)) + fscrypt_set_bio(inode, bio); +#endif } length = first_hole << blkbits; diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 08b4b40c5aa8..5257e495bf3b 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -24,6 +24,12 @@ #define FS_CRYPTO_BLOCK_SIZE 16 +#ifndef __FS_HAS_ENCRYPTION +#define __FS_HAS_ENCRYPTION (IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) || \ + IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) || \ + IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION)) +#endif + struct fscrypt_info; struct fscrypt_ctx { diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 63e58808519a..9ea9625f0f7b 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -208,4 +208,8 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir, return -EOPNOTSUPP; } +static inline void fscrypt_set_bio(const struct inode *inode, struct bio *bio) +{ + return; +} #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index cf9e9fc02f0a..79c6a02a0558 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -143,7 +143,7 @@ extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); - +void fscrypt_set_bio(const struct inode *inode, struct bio *bio); /* hooks.c */ extern int fscrypt_file_open(struct inode *inode, struct file *filp); extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); -- 2.20.1