[COMMON] fs: support diskcipher
authorBoojin Kim <boojin.kim@samsung.com>
Fri, 23 Mar 2018 07:59:15 +0000 (16:59 +0900)
committerJunho Choi <junhosj.choi@samsung.com>
Thu, 24 May 2018 00:02:03 +0000 (09:02 +0900)
Change-Id: I19caef3696973a6aa3dbdad9cf53a6f72cdb0fce
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
fs/crypto/bio.c
fs/crypto/crypto.c
fs/crypto/fscrypt_private.h
fs/crypto/keyinfo.c
fs/direct-io.c
fs/ext4/inode.c
fs/ext4/page-io.c
fs/ext4/readpage.c
include/linux/fscrypt.h
include/linux/fscrypt_notsupp.h
include/linux/fscrypt_supp.h

index 0d5e6a569d58ae2c775aad617200ca4fd1b88518..b182c1afd080fe64e54ceb95ebddb3b5c4d0e6d0 100644 (file)
@@ -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
+}
index 732a786cce9deabe490410ee6dfb15c72fc8f048..f65237d137f9fa65e513e92b307dc6323df6ef7d 100644 (file)
@@ -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);
 }
index c0b4f5597e1a3ee772d1c6ecba8f3b73033b74ba..06e0f47660507f0677a14d4d9aa717c42c40a0bb 100644 (file)
@@ -15,6 +15,7 @@
 #define __FS_HAS_ENCRYPTION 1
 #include <linux/fscrypt.h>
 #include <crypto/hash.h>
+#include <crypto/diskcipher.h>
 
 /* 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 */
index 5e6e846f5a24dde322846fe54f642a40fbdc2f3c..628b22030503e19af6c8f054f385237bb39cd692 100644 (file)
@@ -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;
index 625a84aa6484f4db92261c376f3b9db008ccb6b9..4aa11cdbfe2c85446e0c4a18abf51b5e3547e9ac 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/uio.h>
 #include <linux/atomic.h>
 #include <linux/prefetch.h>
+#include <linux/fscrypt.h>
 
 /*
  * 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);
 
index e8017aefbd44f5bdf8978abe7fc1cffef8ffa084..51b70932d0453da60511758dde5ddbcc4d461cf0 100644 (file)
@@ -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
 
        /*
index db7590178dfcf1a4b59ee3c44deaa89a21de8ca6..b769a6e8b80bd429c4d86acd4856b8f8466a5c14 100644 (file)
@@ -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));
index df22fcb3c41c2922fdf94602f3e412290fbb932b..25797025c1b311babd7399ba3ca62af1da5a1e6e 100644 (file)
 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;
index 08b4b40c5aa83ac265f0d5dadbb06cde79641323..5257e495bf3bd56cd7edfc005ed3e0950762db21 100644 (file)
 
 #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 {
index 63e58808519aaeb60aea66287b40963668a8c6bc..9ea9625f0f7bd7b843b16ec107690806d8d9f716 100644 (file)
@@ -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 */
index cf9e9fc02f0afe7348404ca769927d587a63ec64..79c6a02a055836c6a7007bd63df1d34614386a93 100644 (file)
@@ -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);