[COMMON] fs: support diskcipher
authorBoojin Kim <boojin.kim@samsung.com>
Fri, 23 Mar 2018 07:59:15 +0000 (16:59 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:12 +0000 (20:22 +0300)
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 0959044c5ceecb37168743784df8d6d269f6301b..f1b3d014b60b9fed0fb32ffd442e650aa7be9f7a 100644 (file)
@@ -96,29 +96,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) {
@@ -138,6 +151,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;
@@ -149,7 +166,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 c83ddff3ff4ac4a647fb1f4c29ee34dc8f5fb8c1..2ed8e40bb533696e4f4e0288aaa6990c6ace0b29 100644 (file)
@@ -104,6 +104,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
@@ -195,11 +198,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);
 }
 
 /**
@@ -244,6 +258,9 @@ struct page *fscrypt_encrypt_page(const struct inode *inode,
        struct page *ciphertext_page = page;
        int err;
 
+       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,
@@ -308,6 +325,8 @@ int fscrypt_decrypt_page(const struct inode *inode, struct page *page,
                         !(inode->i_sb->s_cop->flags & FS_CFLG_OWN_PAGES)))
                return -EINVAL;
 
+       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 79debfc9cef9172896717299f1403f828159afd7..18bde3175a432a573c2bebb779f8e104bf4ec2c3 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
@@ -59,6 +60,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];
@@ -107,6 +111,8 @@ fscrypt_msg(struct super_block *sb, const char *level, const char *fmt, ...);
 #define fscrypt_err(sb, fmt, ...)              \
        fscrypt_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
 
+extern void fscrypt_free_bounce_page(void *pool);
+
 /* fname.c */
 extern int fname_encrypt(struct inode *inode, const struct qstr *iname,
                         u8 *out, unsigned int olen);
@@ -117,4 +123,14 @@ extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
 /* 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 f63bfd6dffd6a5bb70f09f6f9917f02afb500f36..8d4df6cc2e7382c0e120043c39c1417347d290b8 100644 (file)
@@ -158,6 +158,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);
@@ -277,6 +281,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));
 
@@ -307,6 +314,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 (IS_ERR(ctfm)) {
                res = PTR_ERR(ctfm);
index 8d9689aac5f4c3f8005aa8327d4014c9b3ed4b31..77f688e4709e382b4eda8c72241e0b12f50e26b3 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
@@ -470,6 +471,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 52ef6ebd943cd01623596eb86f5880c7ade08487..0e191add08bc96825b3214d694135db45fc24058 100644 (file)
@@ -3893,9 +3893,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 3de933354a08b1039cc1021304f0bd5bfe129965..d273e559223151947479fef359b8c4897e999d87 100644 (file)
@@ -354,6 +354,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);
        }
@@ -401,6 +403,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 039aa698159aea57d1f4f0a8f05fb1d4ea1537b4..143d3818089085de64879ddde8df6c4580671bff 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 952ab97af325ea32b537cf32f32a2afd30648cdd..84b2e59eb569399ab32637168afa712e34d07e85 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_ctx;
+
 struct fscrypt_info;
 
 struct fscrypt_str {
index ee8b43e4c15a615bbddc93e8869668eba100f1b9..3f118464b17a84126f33705a69336a5db7dc3e47 100644 (file)
@@ -204,6 +204,11 @@ 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;
+}
+
 static inline int __fscrypt_prepare_symlink(struct inode *dir,
                                            unsigned int len,
                                            unsigned int max_len,
index 6456c6b2005f498a56c2b60cd510e15a823ddb6c..e3604b0196858f22cc291c331a48e3ed019f8e81 100644 (file)
@@ -181,7 +181,7 @@ extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx,
 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);