All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/22] ext4 encryption patches
@ 2015-04-02 22:10 Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 01/22] ext4: add ext4_mpage_readpages() Theodore Ts'o
                   ` (23 more replies)
  0 siblings, 24 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Android userspace code to use ext4 encryption have been checked into
the public AOSP branch.  If all goes well, this feature will hopefully
(my fingers are crossed, but obviously nothing is guaranteed at this
point) be included in the 'M' release of Android.

Currently all of the changes are in fs/ext4 but I've been talking to
Jaegeuk about adopting the same same interfaces (which is essentially
just two ioctl's to set the encryption policy and to get the
per-file-system 'salt' for passwords) for f2fs.

As a result, it may be that some of the functions in
fs/ext4/crypto_*.c will end up getting refactored and moved into some
fs/*.c so that f2fs can use it as well.  But I'd like to get the basic
feature into the kernel tree (marked as experimental initially) and
then do more polishing from there.

There is a design document here.  It should hopefully be mostly up to
date, but there are a few things that we might end up changing (for
example, just using CTS all the time for protecting directory file
names).

https://docs.google.com/document/d/1IsyQ9DU1gA6NUqS0jF4ni_NTvv-b0HfCkRk47Zkd7W0

						- Ted

Michael Halcrow (13):
  ext4 crypto: export ext4_empty_dir()
  ext4 crypto: add encryption xattr support
  ext4 crypto: add encryption policy checking
  ext4 crypto: add ext4 encryption facilities
  ext4 crypto: add encryption key management facilities
  ext4 crypto: inherit encryption policies on inode and directory create
  ext4 crypto: implement the ext4 encryption write path
  ext4 crypto: implement the ext4 decryption read path
  ext4 crypto: filename encryption facilities
  ext4 crypto: insert encrypted filenames into a leaf directory block
  ext4 crypto: partial update to namei.c for fname crypto
  ext4 crypto: filename encryption modifications
  ext4 crypto: enable filename encryption

Theodore Ts'o (9):
  ext4: add ext4_mpage_readpages()
  ext4: reserve codepoints used by the ext4 encryption feature
  ext4 crypto: add ext4 encryption Kconfig
  ext4 crypto: add ioctl to set encryption policy
  ext4 crypto: validate context consistency on lookup
  ext4: teach ext4_htree_store_dirent() to store decrypted filenames
  ext4 crypto: Add symlink encryption
  ext4 crypto: enable encryption feature flag
  ext4 crypto: add password salt support

 fs/ext4/Kconfig         |  20 ++
 fs/ext4/Makefile        |   4 +-
 fs/ext4/crypto.c        | 601 ++++++++++++++++++++++++++++++++++
 fs/ext4/crypto_fname.c  | 831 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/crypto_key.c    | 170 ++++++++++
 fs/ext4/crypto_policy.c | 186 +++++++++++
 fs/ext4/dir.c           |  98 +++++-
 fs/ext4/ext4.h          | 153 ++++++++-
 fs/ext4/ext4_crypto.h   | 140 ++++++++
 fs/ext4/extents.c       |   6 +
 fs/ext4/file.c          |  22 +-
 fs/ext4/ialloc.c        |  28 +-
 fs/ext4/inline.c        |  17 +-
 fs/ext4/inode.c         | 127 +++++++-
 fs/ext4/ioctl.c         |  69 ++++
 fs/ext4/namei.c         | 567 +++++++++++++++++++++++++++++----
 fs/ext4/page-io.c       |  46 ++-
 fs/ext4/readpage.c      | 321 +++++++++++++++++++
 fs/ext4/super.c         |  31 +-
 fs/ext4/symlink.c       | 104 +++++-
 fs/ext4/xattr.h         |   3 +
 21 files changed, 3431 insertions(+), 113 deletions(-)
 create mode 100644 fs/ext4/crypto.c
 create mode 100644 fs/ext4/crypto_fname.c
 create mode 100644 fs/ext4/crypto_key.c
 create mode 100644 fs/ext4/crypto_policy.c
 create mode 100644 fs/ext4/ext4_crypto.h
 create mode 100644 fs/ext4/readpage.c

-- 
2.3.0


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 01/22] ext4: add ext4_mpage_readpages()
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-06 21:08   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 02/22] ext4: reserve codepoints used by the ext4 encryption feature Theodore Ts'o
                   ` (22 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

This takes code from fs/mpage.c and optimizes it for ext4.  Its
primary reason is to allow us to more easily add encryption to ext4's
read path in an efficient manner.

Change-Id: I0a115e90ab13861ab3e96641a0eb82b951198ecc
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/Makefile   |   2 +-
 fs/ext4/ext4.h     |   4 +
 fs/ext4/inode.c    |   4 +-
 fs/ext4/readpage.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 268 insertions(+), 3 deletions(-)
 create mode 100644 fs/ext4/readpage.c

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 0310fec..cd6f50f 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -8,7 +8,7 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 		ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
 		ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
 		mmp.o indirect.o extents_status.o xattr.o xattr_user.o \
-		xattr_trusted.o inline.o
+		xattr_trusted.o inline.o readpage.o
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index a75fba6..06e8add 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2683,6 +2683,10 @@ static inline void ext4_set_de_type(struct super_block *sb,
 		de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
 }
 
+/* readpages.c */
+extern int ext4_mpage_readpages(struct address_space *mapping,
+				struct list_head *pages, struct page *page,
+				unsigned nr_pages);
 
 /* symlink.c */
 extern const struct inode_operations ext4_symlink_inode_operations;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 5653fa4..a68cacc 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2798,7 +2798,7 @@ static int ext4_readpage(struct file *file, struct page *page)
 		ret = ext4_readpage_inline(inode, page);
 
 	if (ret == -EAGAIN)
-		return mpage_readpage(page, ext4_get_block);
+		return ext4_mpage_readpages(page->mapping, NULL, page, 1);
 
 	return ret;
 }
@@ -2813,7 +2813,7 @@ ext4_readpages(struct file *file, struct address_space *mapping,
 	if (ext4_has_inline_data(inode))
 		return 0;
 
-	return mpage_readpages(mapping, pages, nr_pages, ext4_get_block);
+	return ext4_mpage_readpages(mapping, pages, NULL, nr_pages);
 }
 
 static void ext4_invalidatepage(struct page *page, unsigned int offset,
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
new file mode 100644
index 0000000..9ca4dfc
--- /dev/null
+++ b/fs/ext4/readpage.c
@@ -0,0 +1,261 @@
+/*
+ * linux/fs/ext4/readpage.c
+ *
+ * This was originally taken from fs/mpage.c
+ *
+ * The intent is the ext4_mpage_readpages() function here is intended
+ * to replace mpage_readpages() in the general case, not just for
+ * encrypted files.  It has some limitations (see below), where it
+ * will fall back to read_block_full_page(), but these limitations
+ * should never be hit when page_size != block_size.
+ *
+ * This will allow us to attach a callback function to support ext4
+ * encryption.
+ *
+ * If anything unusual happens, such as:
+ *
+ * - encountering a page which has buffers
+ * - encountering a page which has a non-hole after a hole
+ * - encountering a page with non-contiguous blocks
+ *
+ * then this code just gives up and calls the buffer_head-based read function.
+ * It does handle a page which has holes at the end - that is a common case:
+ * the end-of-file on blocksize < PAGE_CACHE_SIZE setups.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/kdev_t.h>
+#include <linux/gfp.h>
+#include <linux/bio.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/blkdev.h>
+#include <linux/highmem.h>
+#include <linux/prefetch.h>
+#include <linux/mpage.h>
+#include <linux/writeback.h>
+#include <linux/backing-dev.h>
+#include <linux/pagevec.h>
+#include <linux/cleancache.h>
+
+#include "ext4.h"
+
+/*
+ * I/O completion handler for multipage BIOs.
+ *
+ * The mpage code never puts partial pages into a BIO (except for end-of-file).
+ * If a page does not map to a contiguous run of blocks then it simply falls
+ * back to block_read_full_page().
+ *
+ * Why is this?  If a page's completion depends on a number of different BIOs
+ * which can complete in any order (or at the same time) then determining the
+ * status of that page is hard.  See end_buffer_async_read() for the details.
+ * There is no point in duplicating all that complexity.
+ */
+static void mpage_end_io(struct bio *bio, int err)
+{
+	struct bio_vec *bv;
+	int i;
+
+	bio_for_each_segment_all(bv, bio, i) {
+		struct page *page = bv->bv_page;
+
+		if (!err) {
+			SetPageUptodate(page);
+		} else {
+			ClearPageUptodate(page);
+			SetPageError(page);
+		}
+		unlock_page(page);
+	}
+
+	bio_put(bio);
+}
+
+int ext4_mpage_readpages(struct address_space *mapping,
+			 struct list_head *pages, struct page *page,
+			 unsigned nr_pages)
+{
+	struct bio *bio = NULL;
+	unsigned page_idx;
+	sector_t last_block_in_bio = 0;
+
+	struct inode *inode = mapping->host;
+	const unsigned blkbits = inode->i_blkbits;
+	const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits;
+	const unsigned blocksize = 1 << blkbits;
+	sector_t block_in_file;
+	sector_t last_block;
+	sector_t last_block_in_file;
+	sector_t blocks[MAX_BUF_PER_PAGE];
+	unsigned page_block;
+	struct block_device *bdev = inode->i_sb->s_bdev;
+	int length;
+	unsigned relative_block = 0;
+	struct ext4_map_blocks map;
+
+	map.m_pblk = 0;
+	map.m_lblk = 0;
+	map.m_len = 0;
+	map.m_flags = 0;
+
+	for (page_idx = 0; nr_pages; page_idx++, nr_pages--) {
+		int fully_mapped = 1;
+		unsigned first_hole = blocks_per_page;
+
+		prefetchw(&page->flags);
+		if (pages) {
+			page = list_entry(pages->prev, struct page, lru);
+			list_del(&page->lru);
+			if (add_to_page_cache_lru(page, mapping,
+						  page->index, GFP_KERNEL))
+				goto next_page;
+		}
+
+		if (page_has_buffers(page))
+			goto confused;
+
+		block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits);
+		last_block = block_in_file + nr_pages * blocks_per_page;
+		last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits;
+		if (last_block > last_block_in_file)
+			last_block = last_block_in_file;
+		page_block = 0;
+
+		/*
+		 * Map blocks using the previous result first.
+		 */
+		if ((map.m_flags & EXT4_MAP_MAPPED) &&
+		    block_in_file > map.m_lblk &&
+		    block_in_file < (map.m_lblk + map.m_len)) {
+			unsigned map_offset = block_in_file - map.m_lblk;
+			unsigned last = map.m_len - map_offset;
+
+			for (relative_block = 0; ; relative_block++) {
+				if (relative_block == last) {
+					/* needed? */
+					map.m_flags &= ~EXT4_MAP_MAPPED;
+					break;
+				}
+				if (page_block == blocks_per_page)
+					break;
+				blocks[page_block] = map.m_pblk + map_offset +
+					relative_block;
+				page_block++;
+				block_in_file++;
+			}
+		}
+
+		/*
+		 * Then do more ext4_map_blocks() calls until we are
+		 * done with this page.
+		 */
+		while (page_block < blocks_per_page) {
+			if (block_in_file < last_block) {
+				map.m_lblk = block_in_file;
+				map.m_len = last_block - block_in_file;
+
+				if (ext4_map_blocks(NULL, inode, &map, 0) < 0) {
+				set_error_page:
+					SetPageError(page);
+					zero_user_segment(page, 0,
+							  PAGE_CACHE_SIZE);
+					unlock_page(page);
+					goto next_page;
+				}
+			}
+			if ((map.m_flags & EXT4_MAP_MAPPED) == 0) {
+				fully_mapped = 0;
+				if (first_hole == blocks_per_page)
+					first_hole = page_block;
+				page_block++;
+				block_in_file++;
+				continue;
+			}
+			if (first_hole != blocks_per_page)
+				goto confused;		/* hole -> non-hole */
+
+			/* Contiguous blocks? */
+			if (page_block && blocks[page_block-1] != map.m_pblk-1)
+				goto confused;
+			for (relative_block = 0; ; relative_block++) {
+				if (relative_block == map.m_len) {
+					/* needed? */
+					map.m_flags &= ~EXT4_MAP_MAPPED;
+					break;
+				} else if (page_block == blocks_per_page)
+					break;
+				blocks[page_block] = map.m_pblk+relative_block;
+				page_block++;
+				block_in_file++;
+			}
+		}
+		if (first_hole != blocks_per_page) {
+			zero_user_segment(page, first_hole << blkbits,
+					  PAGE_CACHE_SIZE);
+			if (first_hole == 0) {
+				SetPageUptodate(page);
+				unlock_page(page);
+				goto next_page;
+			}
+		} else if (fully_mapped) {
+			SetPageMappedToDisk(page);
+		}
+		if (fully_mapped && blocks_per_page == 1 &&
+		    !PageUptodate(page) && cleancache_get_page(page) == 0) {
+			SetPageUptodate(page);
+			goto confused;
+		}
+
+		/*
+		 * This page will go to BIO.  Do we need to send this
+		 * BIO off first?
+		 */
+		if (bio && (last_block_in_bio != blocks[0] - 1)) {
+		submit_and_realloc:
+			submit_bio(READ, bio);
+			bio = NULL;
+		}
+		if (bio == NULL) {
+			bio = bio_alloc(GFP_KERNEL,
+				min_t(int, nr_pages, bio_get_nr_vecs(bdev)));
+			if (!bio)
+				goto set_error_page;
+			bio->bi_bdev = bdev;
+			bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
+			bio->bi_end_io = mpage_end_io;
+		}
+
+		length = first_hole << blkbits;
+		if (bio_add_page(bio, page, length, 0) < length)
+			goto submit_and_realloc;
+
+		if (((map.m_flags & EXT4_MAP_BOUNDARY) &&
+		     (relative_block == map.m_len)) ||
+		    (first_hole != blocks_per_page)) {
+			submit_bio(READ, bio);
+			bio = NULL;
+		} else
+			last_block_in_bio = blocks[blocks_per_page - 1];
+		goto next_page;
+	confused:
+		if (bio) {
+			submit_bio(READ, bio);
+			bio = NULL;
+		}
+		if (!PageUptodate(page))
+			block_read_full_page(page, ext4_get_block);
+		else
+			unlock_page(page);
+	next_page:
+		if (pages)
+			page_cache_release(page);
+	}
+	BUG_ON(pages && !list_empty(pages));
+	if (bio)
+		submit_bio(READ, bio);
+	return 0;
+}
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 02/22] ext4: reserve codepoints used by the ext4 encryption feature
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 01/22] ext4: add ext4_mpage_readpages() Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 03/22] ext4 crypto: add ext4 encryption Kconfig Theodore Ts'o
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Change-Id: I5f01f62e75426150c32c22188ae8ad3192da95e6
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 06e8add..c587684 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -364,7 +364,8 @@ struct flex_groups {
 #define EXT4_DIRTY_FL			0x00000100
 #define EXT4_COMPRBLK_FL		0x00000200 /* One or more compressed clusters */
 #define EXT4_NOCOMPR_FL			0x00000400 /* Don't compress */
-#define EXT4_ECOMPR_FL			0x00000800 /* Compression error */
+	/* nb: was previously EXT2_ECOMPR_FL */
+#define EXT4_ENCRYPT_FL			0x00000800 /* encrypted file */
 /* End compression flags --- maybe not all used */
 #define EXT4_INDEX_FL			0x00001000 /* hash-indexed directory */
 #define EXT4_IMAGIC_FL			0x00002000 /* AFS directory */
@@ -417,11 +418,11 @@ enum {
 	EXT4_INODE_APPEND	= 5,	/* writes to file may only append */
 	EXT4_INODE_NODUMP	= 6,	/* do not dump file */
 	EXT4_INODE_NOATIME	= 7,	/* do not update atime */
-/* Reserved for compression usage... */
+/* Reserved for compression usage, co-opted for encryption usage */
 	EXT4_INODE_DIRTY	= 8,
 	EXT4_INODE_COMPRBLK	= 9,	/* One or more compressed clusters */
 	EXT4_INODE_NOCOMPR	= 10,	/* Don't compress */
-	EXT4_INODE_ECOMPR	= 11,	/* Compression error */
+	EXT4_INODE_ENCRYPT	= 11,	/* Encrypted */
 /* End compression flags --- maybe not all used */
 	EXT4_INODE_INDEX	= 12,	/* hash-indexed directory */
 	EXT4_INODE_IMAGIC	= 13,	/* AFS directory */
@@ -466,7 +467,7 @@ static inline void ext4_check_flag_values(void)
 	CHECK_FLAG_VALUE(DIRTY);
 	CHECK_FLAG_VALUE(COMPRBLK);
 	CHECK_FLAG_VALUE(NOCOMPR);
-	CHECK_FLAG_VALUE(ECOMPR);
+	CHECK_FLAG_VALUE(ENCRYPT);
 	CHECK_FLAG_VALUE(INDEX);
 	CHECK_FLAG_VALUE(IMAGIC);
 	CHECK_FLAG_VALUE(JOURNAL_DATA);
@@ -581,6 +582,12 @@ enum {
 #define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER	0x0010
 #define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER	0x0020
 
+/* Encryption algorithms */
+#define EXT4_ENCRYPTION_MODE_INVALID		0
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS	1
+#define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
+#define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
+
 /*
  * ioctl commands
  */
@@ -1156,7 +1163,8 @@ struct ext4_super_block {
 	__le32	s_grp_quota_inum;	/* inode for tracking group quota */
 	__le32	s_overhead_clusters;	/* overhead blocks/clusters in fs */
 	__le32	s_backup_bgs[2];	/* groups with sparse_super2 SBs */
-	__le32	s_reserved[106];	/* Padding to the end of the block */
+	__u8	s_encrypt_algos[4];	/* Encryption algorithms in use  */
+	__le32	s_reserved[105];	/* Padding to the end of the block */
 	__le32	s_checksum;		/* crc32c(superblock) */
 };
 
@@ -1537,6 +1545,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM	0x2000 /* use crc32c for bg */
 #define EXT4_FEATURE_INCOMPAT_LARGEDIR		0x4000 /* >2GB or 3-lvl htree */
 #define EXT4_FEATURE_INCOMPAT_INLINE_DATA	0x8000 /* data in inode */
+#define EXT4_FEATURE_INCOMPAT_ENCRYPT		0x10000
 
 #define EXT2_FEATURE_COMPAT_SUPP	EXT4_FEATURE_COMPAT_EXT_ATTR
 #define EXT2_FEATURE_INCOMPAT_SUPP	(EXT4_FEATURE_INCOMPAT_FILETYPE| \
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 03/22] ext4 crypto: add ext4 encryption Kconfig
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 01/22] ext4: add ext4_mpage_readpages() Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 02/22] ext4: reserve codepoints used by the ext4 encryption feature Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 04/22] ext4 crypto: export ext4_empty_dir() Theodore Ts'o
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Change-Id: Ieedde8447d748d2ca1ffca546e58418fb9e9c885
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/Kconfig | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index efea5d5..33de739 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -64,6 +64,26 @@ config EXT4_FS_SECURITY
 	  If you are not using a security module that requires using
 	  extended attributes for file security labels, say N.
 
+config EXT4_FS_ENCRYPTION
+	bool "Ext4 Encryption"
+	depends on EXT4_FS
+	select CRYPTO_AES
+	select CRYPTO_CBC
+	select CRYPTO_CTR
+	select CRYPTO_ECB
+	select CRYPTO_XTS
+	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
+	select CRYPTO_HMAC
+	select KEYS
+	select ENCRYPTED_KEYS
+	help
+	  Enable encryption of ext4 files and directories.  This
+	  feature is similar to ecryptfs, but it is more memory
+	  efficient since it avoids caching the encrypted and
+	  decrypted pages in the page cache.
+
 config EXT4_DEBUG
 	bool "EXT4 debugging support"
 	depends on EXT4_FS
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 04/22] ext4 crypto: export ext4_empty_dir()
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (2 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 03/22] ext4 crypto: add ext4 encryption Kconfig Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 05/22] ext4 crypto: add encryption xattr support Theodore Ts'o
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Required for future encryption xattr changes.

Change-Id: Ib12fcceed8af6e2460bc387ec94434cf51b7befb
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  |  1 +
 fs/ext4/namei.c | 11 ++++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index c587684..2f3808e 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2183,6 +2183,7 @@ extern int ext4_generic_delete_entry(handle_t *handle,
 				     void *entry_buf,
 				     int buf_size,
 				     int csum_size);
+extern int ext4_empty_dir(struct inode *inode);
 
 /* resize.c */
 extern int ext4_group_add(struct super_block *sb,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2291923..2fb55fd 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2450,7 +2450,7 @@ out_stop:
 /*
  * routine to check that the specified directory is empty (for rmdir)
  */
-static int empty_dir(struct inode *inode)
+int ext4_empty_dir(struct inode *inode)
 {
 	unsigned int offset;
 	struct buffer_head *bh;
@@ -2718,7 +2718,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
 		goto end_rmdir;
 
 	retval = -ENOTEMPTY;
-	if (!empty_dir(inode))
+	if (!ext4_empty_dir(inode))
 		goto end_rmdir;
 
 	handle = ext4_journal_start(dir, EXT4_HT_DIR,
@@ -3272,7 +3272,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (S_ISDIR(old.inode->i_mode)) {
 		if (new.inode) {
 			retval = -ENOTEMPTY;
-			if (!empty_dir(new.inode))
+			if (!ext4_empty_dir(new.inode))
 				goto end_rename;
 		} else {
 			retval = -EMLINK;
@@ -3346,8 +3346,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 		ext4_dec_count(handle, old.dir);
 		if (new.inode) {
-			/* checked empty_dir above, can't have another parent,
-			 * ext4_dec_count() won't work for many-linked dirs */
+			/* checked ext4_empty_dir above, can't have another
+			 * parent, ext4_dec_count() won't work for many-linked
+			 * dirs */
 			clear_nlink(new.inode);
 		} else {
 			ext4_inc_count(handle, new.dir);
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 05/22] ext4 crypto: add encryption xattr support
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (3 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 04/22] ext4 crypto: export ext4_empty_dir() Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 06/22] ext4 crypto: add encryption policy checking Theodore Ts'o
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: Icec301ab73c83e647c184f3b33aa0be76228dfcc
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/xattr.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 29bedf5..ddc0957 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -23,6 +23,7 @@
 #define EXT4_XATTR_INDEX_SECURITY	        6
 #define EXT4_XATTR_INDEX_SYSTEM			7
 #define EXT4_XATTR_INDEX_RICHACL		8
+#define EXT4_XATTR_INDEX_ENCRYPTION		9
 
 struct ext4_xattr_header {
 	__le32	h_magic;	/* magic number for identification */
@@ -98,6 +99,8 @@ extern const struct xattr_handler ext4_xattr_user_handler;
 extern const struct xattr_handler ext4_xattr_trusted_handler;
 extern const struct xattr_handler ext4_xattr_security_handler;
 
+#define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c"
+
 extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);
 
 extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (4 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 05/22] ext4 crypto: add encryption xattr support Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-06 21:31   ` Andreas Dilger
  2015-04-08 18:07   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 07/22] ext4 crypto: add ioctl to set encryption policy Theodore Ts'o
                   ` (17 subsequent siblings)
  23 siblings, 2 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Theodore Ts'o, Ildar Muslukhov

From: Michael Halcrow <mhalcrow@google.com>

The ext4_crypto.h header will get fleshed out as later patches in this
patchset add functionality.

Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
---
 fs/ext4/Makefile        |   1 +
 fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/ext4.h          |   2 +
 fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
 4 files changed, 212 insertions(+)
 create mode 100644 fs/ext4/crypto_policy.c
 create mode 100644 fs/ext4/ext4_crypto.h

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index cd6f50f..3886ee4 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,3 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
+ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
new file mode 100644
index 0000000..5cb4e74
--- /dev/null
+++ b/fs/ext4/crypto_policy.c
@@ -0,0 +1,155 @@
+/*
+ * linux/fs/ext4/crypto_policy.c
+ *
+ * This contains encryption policy functions for ext4
+ *
+ * Written by Michael Halcrow, 2015.
+ */
+
+#include <linux/random.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "ext4.h"
+#include "xattr.h"
+
+/**
+ * ext4_to_hex() - Converts to hexadecimal characters
+ * @dst: Buffer to take hex character representation of contents of
+ *       src. Must be at least of size (src_size * 2).
+ * @src: Buffer to be converted to a hex string respresentation.
+ * @src_size: Number of bytes to convert.
+ */
+void ext4_to_hex(char *dst, char *src, size_t src_size)
+{
+	int x;
+
+	for (x = 0; x < src_size; x++)
+		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
+}
+
+/**
+ *
+ */
+static int ext4_inode_has_encryption_context(struct inode *inode)
+{
+	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
+	return (res > 0);
+}
+
+/**
+ * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
+ * @inode:  ...
+ * @policy: ...
+ *
+ * Return ...
+ */
+static int ext4_is_encryption_context_consistent_with_policy(
+	struct inode *inode, const struct ext4_encryption_policy *policy)
+{
+	struct ext4_encryption_context ctx;
+	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+				 sizeof(ctx));
+	if (res != sizeof(ctx))
+		return 0;
+	return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
+			EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+		(ctx.contents_encryption_mode ==
+		 policy->contents_encryption_mode) &&
+		(ctx.filenames_encryption_mode ==
+		 policy->filenames_encryption_mode));
+}
+
+static int ext4_create_encryption_context_from_policy(
+	struct inode *inode, const struct ext4_encryption_policy *policy)
+{
+	struct ext4_encryption_context ctx;
+	int res = 0;
+
+	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
+	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
+	       EXT4_KEY_DESCRIPTOR_SIZE);
+	ctx.contents_encryption_mode = policy->contents_encryption_mode;
+	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
+	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
+	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
+
+	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+			     sizeof(ctx), 0);
+	if (!res)
+		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
+	return res;
+}
+
+int ext4_process_policy(const struct ext4_encryption_policy *policy,
+			struct inode *inode)
+{
+	int res = 0;
+
+	if (!ext4_inode_has_encryption_context(inode)) {
+		res = ext4_create_encryption_context_from_policy(inode, policy);
+		goto out;
+	}
+
+	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
+		printk(KERN_WARNING
+		       "%s: Policy inconsistent with encryption context\n",
+		       __func__);
+		res = -EINVAL;
+	}
+out:
+	return res;
+}
+
+int ext4_is_child_context_consistent_with_parent(struct inode *parent,
+						 struct inode *child)
+{
+	struct ext4_encryption_context parent_ctx, child_ctx;
+	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+				 &parent_ctx, sizeof(parent_ctx));
+
+	if (res != sizeof(parent_ctx))
+		return 0;
+	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+			     &child_ctx, sizeof(child_ctx));
+	if (res != sizeof(child_ctx))
+		return 0;
+	return (memcmp(parent_ctx.master_key_descriptor,
+		       child_ctx.master_key_descriptor,
+		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+		(parent_ctx.contents_encryption_mode ==
+		 child_ctx.contents_encryption_mode) &&
+		(parent_ctx.filenames_encryption_mode ==
+		 child_ctx.filenames_encryption_mode));
+}
+
+/**
+ * ext4_inherit_context() - Sets a child context from its parent
+ * @parent: Parent inode from which the context is inherited.
+ * @child:  Child inode that inherits the context from @parent.
+ *
+ * Return: Zero on success, non-zero otherwise
+ */
+int ext4_inherit_context(struct inode *parent, struct inode *child)
+{
+	struct ext4_encryption_context ctx;
+	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+				 &ctx, sizeof(ctx));
+
+	if (res != sizeof(ctx))
+		return -ENOENT;
+
+	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
+	res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+			     sizeof(ctx), 0);
+	if (!res)
+		ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
+	return res;
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 2f3808e..fd2f3dd 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -588,6 +588,8 @@ enum {
 #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
 #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
 
+#include "ext4_crypto.h"
+
 /*
  * ioctl commands
  */
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
new file mode 100644
index 0000000..984ff38
--- /dev/null
+++ b/fs/ext4/ext4_crypto.h
@@ -0,0 +1,54 @@
+/*
+ * linux/fs/ext4/ext4_crypto.h
+ *
+ * This contains encryption header content for ext4
+ *
+ * Written by Michael Halcrow, 2015.
+ */
+
+#ifndef _EXT4_CRYPTO_H
+#define _EXT4_CRYPTO_H
+
+#include <linux/fs.h>
+
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+
+/* Policy provided via an ioctl on the topmost directory */
+struct ext4_encryption_policy {
+	char version;
+	char contents_encryption_mode;
+	char filenames_encryption_mode;
+	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
+#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
+
+/**
+ * Encryption context for inode
+ *
+ * Protector format:
+ *  1 byte: Protector format (0 = this version)
+ *  1 byte: File contents encryption mode
+ *  1 byte: File names encryption mode
+ *  1 byte: Reserved
+ *  8 bytes: Master Key descriptor
+ *  16 bytes: Encryption Key derivation nonce
+ */
+struct ext4_encryption_context {
+	char format;
+	char contents_encryption_mode;
+	char filenames_encryption_mode;
+	char reserved;
+	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
+} __attribute__((__packed__));
+
+int ext4_is_child_context_consistent_with_parent(struct inode *parent,
+						 struct inode *child);
+int ext4_inherit_context(struct inode *parent, struct inode *child);
+void ext4_to_hex(char *dst, char *src, size_t src_size);
+int ext4_process_policy(const struct ext4_encryption_policy *policy,
+			struct inode *inode);
+
+#endif	/* _EXT4_CRYPTO_H */
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 07/22] ext4 crypto: add ioctl to set encryption policy
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (5 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 06/22] ext4 crypto: add encryption policy checking Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 08/22] ext4 crypto: add ext4 encryption facilities Theodore Ts'o
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Change-Id: I6da224be190c29d5315039f921229db92243f306
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  |  1 +
 fs/ext4/ioctl.c | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index fd2f3dd..2d7fcb6 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -611,6 +611,7 @@ enum {
 #define EXT4_IOC_RESIZE_FS		_IOW('f', 16, __u64)
 #define EXT4_IOC_SWAP_BOOT		_IO('f', 17)
 #define EXT4_IOC_PRECACHE_EXTENTS	_IO('f', 18)
+#define EXT4_IOC_ENCRYPTION_POLICY	_IOW('f', 19, struct ext4_encryption_policy)
 
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index f58a0d1..e4ae8f9 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -615,7 +615,26 @@ resizefs_out:
 	}
 	case EXT4_IOC_PRECACHE_EXTENTS:
 		return ext4_ext_precache(inode);
+	case EXT4_IOC_ENCRYPTION_POLICY:
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	{
+		struct ext4_encryption_policy policy;
+		int err = 0;
+
+		if (copy_from_user(&policy,
+				   (struct ext4_encryption_policy __user *)arg,
+				   sizeof(policy))) {
+			err = -EFAULT;
+			goto encryption_policy_out;
+		}
 
+		err = ext4_process_policy(&policy, inode);
+encryption_policy_out:
+		return err;
+	}
+#else
+		return -EOPNOTSUPP;
+#endif
 	default:
 		return -ENOTTY;
 	}
@@ -680,6 +699,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case FITRIM:
 	case EXT4_IOC_RESIZE_FS:
 	case EXT4_IOC_PRECACHE_EXTENTS:
+	case EXT4_IOC_ENCRYPTION_POLICY:
 		break;
 	default:
 		return -ENOIOCTLCMD;
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 08/22] ext4 crypto: add ext4 encryption facilities
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (6 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 07/22] ext4 crypto: add ioctl to set encryption policy Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-09 12:54   ` Maurizio Lombardi
  2015-04-02 22:10 ` [PATCH 09/22] ext4 crypto: add encryption key management facilities Theodore Ts'o
                   ` (15 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

On encrypt, we will re-assign the buffer_heads to point to a bounce
page rather than the control_page (which is the original page to write
that contains the plaintext). The block I/O occurs against the bounce
page.  On write completion, we re-assign the buffer_heads to the
original plaintext page.

On decrypt, we will attach a read completion callback to the bio
struct. This read completion will decrypt the read contents in-place
prior to setting the page up-to-date.

The current encryption mode, AES-256-XTS, lacks cryptographic
integrity. AES-256-GCM is in-plan, but we will need to devise a
mechanism for handling the integrity data.

Change-Id: I5ed4c913d49971d7f7e9b10bb4e694df86f960d7
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/Makefile        |   2 +-
 fs/ext4/crypto.c        | 601 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/crypto_policy.c |  21 +-
 fs/ext4/ext4.h          |  39 ++++
 fs/ext4/ext4_crypto.h   |  43 ++++
 fs/ext4/super.c         |  11 +
 6 files changed, 714 insertions(+), 3 deletions(-)
 create mode 100644 fs/ext4/crypto.c

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 3886ee4..1b1c561 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,4 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
-ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o
+ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
new file mode 100644
index 0000000..5b62bb1
--- /dev/null
+++ b/fs/ext4/crypto.c
@@ -0,0 +1,601 @@
+/*
+ * linux/fs/ext4/crypto.c
+ *
+ * This contains encryption functions for ext4
+ *
+ * Written by Michael Halcrow, 2014.
+ *
+ * Filename encryption additions
+ *	Uday Savagaonkar, 2014
+ * Encryption policy handling additions
+ *	Ildar Muslukhov, 2014
+ *
+ * This has not yet undergone a rigorous security audit.
+ *
+ * The usage of AES-XTS should conform to recommendations in NIST
+ * Special Publication 800-38E. The usage of AES-GCM should conform to
+ * the recommendations in NIST Special Publication 800-38D. Further
+ * guidance for block-oriented storage is in IEEE P1619/D16. The key
+ * derivation code implements an HKDF (see RFC 5869).
+ */
+
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <keys/user-type.h>
+#include <keys/encrypted-type.h>
+#include <linux/crypto.h>
+#include <linux/ecryptfs.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock_types.h>
+
+#include "ext4.h"
+#include "xattr.h"
+
+/* Encryption added and removed here! (L: */
+
+static unsigned int num_prealloc_crypto_pages = 32;
+static unsigned int num_prealloc_crypto_ctxs = 128;
+
+module_param(num_prealloc_crypto_pages, uint, 0444);
+MODULE_PARM_DESC(num_prealloc_crypto_pages,
+		 "Number of crypto pages to preallocate");
+module_param(num_prealloc_crypto_ctxs, uint, 0444);
+MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
+		 "Number of crypto contexts to preallocate");
+
+static mempool_t *ext4_bounce_page_pool;
+
+static LIST_HEAD(ext4_free_crypto_ctxs);
+static DEFINE_SPINLOCK(ext4_crypto_ctx_lock);
+
+/**
+ * ext4_release_crypto_ctx() - Releases an encryption context
+ * @ctx: The encryption context to release.
+ *
+ * If the encryption context was allocated from the pre-allocated pool, returns
+ * it to that pool. Else, frees it.
+ *
+ * If there's a bounce page in the context, this frees that.
+ */
+void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx)
+{
+	unsigned long flags;
+
+	if (ctx->bounce_page) {
+		if (ctx->flags & EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
+			__free_page(ctx->bounce_page);
+		else
+			mempool_free(ctx->bounce_page, ext4_bounce_page_pool);
+		ctx->bounce_page = NULL;
+	}
+	ctx->control_page = NULL;
+	if (ctx->flags & EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL) {
+		if (ctx->tfm)
+			crypto_free_tfm(ctx->tfm);
+		kfree(ctx);
+	} else {
+		spin_lock_irqsave(&ext4_crypto_ctx_lock, flags);
+		list_add(&ctx->free_list, &ext4_free_crypto_ctxs);
+		spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags);
+	}
+}
+
+/**
+ * ext4_alloc_and_init_crypto_ctx() - Allocates and inits an encryption context
+ * @mask: The allocation mask.
+ *
+ * Return: An allocated and initialized encryption context on success. An error
+ * value or NULL otherwise.
+ */
+static struct ext4_crypto_ctx *ext4_alloc_and_init_crypto_ctx(gfp_t mask)
+{
+	struct ext4_crypto_ctx *ctx = kzalloc(sizeof(struct ext4_crypto_ctx),
+					      mask);
+
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+	return ctx;
+}
+
+/**
+ * ext4_get_crypto_ctx() - Gets an encryption context
+ * @inode:       The inode for which we are doing the crypto
+ *
+ * Allocates and initializes an encryption context.
+ *
+ * Return: An allocated and initialized encryption context on success; error
+ * value or NULL otherwise.
+ */
+struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode)
+{
+	struct ext4_crypto_ctx *ctx = NULL;
+	int res = 0;
+	unsigned long flags;
+	struct ext4_encryption_key *key = &EXT4_I(inode)->i_encryption_key;
+
+	/* We first try getting the ctx from a free list because in the common
+	 * case the ctx will have an allocated and initialized crypto tfm, so
+	 * it's probably a worthwhile optimization. For the bounce page, we
+	 * first try getting it from the kernel allocator because that's just
+	 * about as fast as getting it from a list and because a cache of free
+	 * pages should generally be a "last resort" option for a filesystem to
+	 * be able to do its job. */
+	spin_lock_irqsave(&ext4_crypto_ctx_lock, flags);
+	ctx = list_first_entry_or_null(&ext4_free_crypto_ctxs,
+				       struct ext4_crypto_ctx, free_list);
+	if (ctx)
+		list_del(&ctx->free_list);
+	spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags);
+	if (!ctx) {
+		ctx = ext4_alloc_and_init_crypto_ctx(GFP_NOFS);
+		if (IS_ERR(ctx)) {
+			res = PTR_ERR(ctx);
+			goto out;
+		}
+		ctx->flags |= EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL;
+	} else {
+		ctx->flags &= ~EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL;
+	}
+
+	/* Allocate a new Crypto API context if we don't already have one or if
+	 * it isn't the right mode. */
+	BUG_ON(key->mode == EXT4_ENCRYPTION_MODE_INVALID);
+	if (ctx->tfm && (ctx->mode != key->mode)) {
+		crypto_free_tfm(ctx->tfm);
+		ctx->tfm = NULL;
+		ctx->mode = EXT4_ENCRYPTION_MODE_INVALID;
+	}
+	if (!ctx->tfm) {
+		switch (key->mode) {
+		case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+			ctx->tfm = crypto_ablkcipher_tfm(
+				crypto_alloc_ablkcipher("xts(aes)", 0, 0));
+			break;
+		case EXT4_ENCRYPTION_MODE_AES_256_GCM:
+			/* TODO(mhalcrow): AEAD w/ gcm(aes);
+			 * crypto_aead_setauthsize() */
+			ctx->tfm = ERR_PTR(-ENOTSUPP);
+			break;
+		default:
+			BUG();
+		}
+		if (IS_ERR_OR_NULL(ctx->tfm)) {
+			res = PTR_ERR(ctx->tfm);
+			ctx->tfm = NULL;
+			goto out;
+		}
+		ctx->mode = key->mode;
+	}
+	BUG_ON(key->size != ext4_encryption_key_size(key->mode));
+
+	/* There shouldn't be a bounce page attached to the crypto
+	 * context at this point. */
+	BUG_ON(ctx->bounce_page);
+
+out:
+	if (res) {
+		if (!IS_ERR_OR_NULL(ctx))
+			ext4_release_crypto_ctx(ctx);
+		ctx = ERR_PTR(res);
+	}
+	return ctx;
+}
+
+struct workqueue_struct *ext4_read_workqueue;
+static DEFINE_MUTEX(crypto_init);
+
+/**
+ * ext4_exit_crypto() - Shutdown the ext4 encryption system
+ */
+void ext4_exit_crypto(void)
+{
+	struct ext4_crypto_ctx *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &ext4_free_crypto_ctxs, free_list) {
+		if (pos->bounce_page) {
+			if (pos->flags &
+			    EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL) {
+				__free_page(pos->bounce_page);
+			} else {
+				mempool_free(pos->bounce_page,
+					     ext4_bounce_page_pool);
+			}
+		}
+		if (pos->tfm)
+			crypto_free_tfm(pos->tfm);
+		kfree(pos);
+	}
+	INIT_LIST_HEAD(&ext4_free_crypto_ctxs);
+	if (ext4_bounce_page_pool)
+		mempool_destroy(ext4_bounce_page_pool);
+	ext4_bounce_page_pool = NULL;
+	if (ext4_read_workqueue)
+		destroy_workqueue(ext4_read_workqueue);
+	ext4_read_workqueue = NULL;
+}
+
+/**
+ * ext4_init_crypto() - Set up for ext4 encryption.
+ *
+ * We call this when we mount a file system which has the encryption
+ * feature enabled, since it results in memory getting allocated that
+ * won't be used unless we are using encryption.
+ *
+ * Return: Zero on success, non-zero otherwise.
+ */
+int ext4_init_crypto(void)
+{
+	int i, res = 0;
+
+	mutex_lock(&crypto_init);
+	if (ext4_read_workqueue)
+		goto already_initialized;
+	ext4_read_workqueue = alloc_workqueue("ext4_crypto", WQ_HIGHPRI, 0);
+	if (!ext4_read_workqueue) {
+		res = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
+		struct ext4_crypto_ctx *ctx;
+
+		ctx = ext4_alloc_and_init_crypto_ctx(GFP_KERNEL);
+		if (IS_ERR(ctx)) {
+			res = PTR_ERR(ctx);
+			goto fail;
+		}
+		list_add(&ctx->free_list, &ext4_free_crypto_ctxs);
+	}
+
+	ext4_bounce_page_pool =
+		mempool_create_page_pool(num_prealloc_crypto_pages, 0);
+	if (!ext4_bounce_page_pool)
+		goto fail;
+already_initialized:
+	mutex_unlock(&crypto_init);
+	return 0;
+fail:
+	ext4_exit_crypto();
+	mutex_unlock(&crypto_init);
+	return res;
+}
+
+/**
+ * ext4_xts_tweak_for_page() - Generates an XTS tweak for a page
+ * @xts_tweak: Buffer into which this writes the XTS tweak.
+ * @page:      The page for which this generates a tweak.
+ *
+ * Generates an XTS tweak value for the given page.
+ */
+static void ext4_xts_tweak_for_page(u8 xts_tweak[EXT4_XTS_TWEAK_SIZE],
+				    const struct page *page)
+{
+	/* Only do this for XTS tweak values. For other modes (CBC,
+	 * GCM, etc.), you most likely will need to do something
+	 * different. */
+	BUILD_BUG_ON(EXT4_XTS_TWEAK_SIZE < sizeof(page->index));
+	memcpy(xts_tweak, &page->index, sizeof(page->index));
+	memset(&xts_tweak[sizeof(page->index)], 0,
+	       EXT4_XTS_TWEAK_SIZE - sizeof(page->index));
+}
+
+void ext4_restore_control_page(struct page *data_page)
+{
+	struct ext4_crypto_ctx *ctx =
+		(struct ext4_crypto_ctx *)page_private(data_page);
+
+	set_page_private(data_page, (unsigned long)NULL);
+	ClearPagePrivate(data_page);
+	unlock_page(data_page);
+	ext4_release_crypto_ctx(ctx);
+}
+
+struct ext4_crypt_result {
+	struct completion completion;
+	int res;
+};
+
+/**
+ * ext4_crypt_complete() - The completion callback for page encryption
+ * @req: The asynchronous encryption request context
+ * @res: The result of the encryption operation
+ */
+static void ext4_crypt_complete(struct crypto_async_request *req, int res)
+{
+	struct ext4_crypt_result *ecr = req->data;
+
+	if (res == -EINPROGRESS)
+		return;
+	ecr->res = res;
+	complete(&ecr->completion);
+}
+
+/**
+ * ext4_prep_pages_for_write() - Prepares pages for write
+ * @ciphertext_page: Ciphertext page that will actually be written.
+ * @plaintext_page:  Plaintext page that acts as a control page.
+ * @ctx:             Encryption context for the pages.
+ */
+static void ext4_prep_pages_for_write(struct page *ciphertext_page,
+				      struct page *plaintext_page,
+				      struct ext4_crypto_ctx *ctx)
+{
+	SetPageDirty(ciphertext_page);
+	SetPagePrivate(ciphertext_page);
+	ctx->control_page = plaintext_page;
+	set_page_private(ciphertext_page, (unsigned long)ctx);
+	lock_page(ciphertext_page);
+}
+
+/**
+ * ext4_xts_encrypt() - Encrypts a page using AES-256-XTS
+ * @ctx:            The encryption context.
+ * @plaintext_page: The page to encrypt. Must be locked.
+ *
+ * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
+ * encryption context. Uses AES-256-XTS.
+ *
+ * Called on the page write path.
+ *
+ * Return: An allocated page with the encrypted content on success. Else, an
+ * error value or NULL.
+ */
+static struct page *ext4_xts_encrypt(struct ext4_crypto_ctx *ctx,
+				     struct page *plaintext_page)
+{
+	struct page *ciphertext_page = ctx->bounce_page;
+	u8 xts_tweak[EXT4_XTS_TWEAK_SIZE];
+	struct ablkcipher_request *req = NULL;
+	struct ext4_crypt_result ecr;
+	struct scatterlist dst, src;
+	struct ext4_inode_info *ei = EXT4_I(plaintext_page->mapping->host);
+	struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm);
+	int res = 0;
+
+	BUG_ON(!ciphertext_page);
+	BUG_ON(!ctx->tfm);
+	BUG_ON(ei->i_encryption_key.mode != EXT4_ENCRYPTION_MODE_AES_256_XTS);
+	crypto_ablkcipher_clear_flags(atfm, ~0);
+	crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+
+	/* Since in AES-256-XTS mode we only perform one cryptographic operation
+	 * on each block and there are no constraints about how many blocks a
+	 * single key can encrypt, we directly use the inode master key */
+	res = crypto_ablkcipher_setkey(atfm, ei->i_encryption_key.raw,
+				       ei->i_encryption_key.size);
+	req = ablkcipher_request_alloc(atfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(KERN_ERR
+				   "%s: crypto_request_alloc() failed\n",
+				   __func__);
+		ciphertext_page = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+	ablkcipher_request_set_callback(
+		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		ext4_crypt_complete, &ecr);
+	ext4_xts_tweak_for_page(xts_tweak, plaintext_page);
+	sg_init_table(&dst, 1);
+	sg_set_page(&dst, ciphertext_page, PAGE_CACHE_SIZE, 0);
+	sg_init_table(&src, 1);
+	sg_set_page(&src, plaintext_page, PAGE_CACHE_SIZE, 0);
+	ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
+				     xts_tweak);
+	res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+	ablkcipher_request_free(req);
+	if (res) {
+		printk_ratelimited(
+			KERN_ERR
+			"%s: crypto_ablkcipher_encrypt() returned %d\n",
+			__func__, res);
+		ciphertext_page = ERR_PTR(res);
+		goto out;
+	}
+out:
+	return ciphertext_page;
+}
+
+/**
+ * ext4_encrypt() - Encrypts a page
+ * @ctx:            The encryption context.
+ * @plaintext_page: The page to encrypt. Must be locked.
+ *
+ * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
+ * encryption context.
+ *
+ * Called on the page write path.
+ *
+ * Return: An allocated page with the encrypted content on success. Else, an
+ * error value or NULL.
+ */
+struct page *ext4_encrypt(struct inode *inode,
+			  struct page *plaintext_page)
+{
+	struct ext4_crypto_ctx *ctx;
+	struct page *ciphertext_page = NULL;
+
+	BUG_ON(!PageLocked(plaintext_page));
+
+	ctx = ext4_get_crypto_ctx(inode);
+	if (IS_ERR(ctx))
+		return (struct page *) ctx;
+
+	/* The encryption operation will require a bounce page. */
+	ctx->bounce_page = alloc_page(GFP_NOFS);
+	if (!ctx->bounce_page) {
+		/* This is a potential bottleneck, but at least we'll have
+		 * forward progress. */
+		ctx->bounce_page = mempool_alloc(ext4_bounce_page_pool,
+						 GFP_NOFS);
+		if (WARN_ON_ONCE(!ctx->bounce_page)) {
+			ctx->bounce_page = mempool_alloc(ext4_bounce_page_pool,
+							 GFP_NOFS | __GFP_WAIT);
+		}
+		ctx->flags &= ~EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
+	} else {
+		ctx->flags |= EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
+	}
+
+	switch (ctx->mode) {
+	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+		ciphertext_page = ext4_xts_encrypt(ctx, plaintext_page);
+		break;
+	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
+		/* TODO(mhalcrow): We'll need buffers for the
+		 * generated IV and/or auth tag for this mode and the
+		 * ones below */
+		ciphertext_page = ERR_PTR(-ENOTSUPP);
+		break;
+	default:
+		BUG();
+	}
+	if (IS_ERR_OR_NULL(ciphertext_page))
+		ext4_release_crypto_ctx(ctx);
+	else
+		ext4_prep_pages_for_write(ciphertext_page, plaintext_page, ctx);
+	return ciphertext_page;
+}
+
+/**
+ * ext4_xts_decrypt() - Decrypts a page using AES-256-XTS
+ * @ctx:  The encryption context.
+ * @page: The page to decrypt. Must be locked.
+ *
+ * Return: Zero on success, non-zero otherwise.
+ */
+static int ext4_xts_decrypt(struct ext4_crypto_ctx *ctx, struct page *page)
+{
+	u8 xts_tweak[EXT4_XTS_TWEAK_SIZE];
+	struct ablkcipher_request *req = NULL;
+	struct ext4_crypt_result ecr;
+	struct scatterlist sg;
+	struct ext4_inode_info *ei = EXT4_I(page->mapping->host);
+	struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm);
+	int res = 0;
+
+	BUG_ON(!ctx->tfm);
+	BUG_ON(ei->i_encryption_key.mode != EXT4_ENCRYPTION_MODE_AES_256_XTS);
+	crypto_ablkcipher_clear_flags(atfm, ~0);
+	crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+
+	/* Since in AES-256-XTS mode we only perform one cryptographic operation
+	 * on each block and there are no constraints about how many blocks a
+	 * single key can encrypt, we directly use the inode master key */
+	res = crypto_ablkcipher_setkey(atfm, ei->i_encryption_key.raw,
+				       ei->i_encryption_key.size);
+	req = ablkcipher_request_alloc(atfm, GFP_NOFS);
+	if (!req) {
+		res = -ENOMEM;
+		goto out;
+	}
+	ablkcipher_request_set_callback(
+		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		ext4_crypt_complete, &ecr);
+	ext4_xts_tweak_for_page(xts_tweak, page);
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, page, PAGE_CACHE_SIZE, 0);
+	ablkcipher_request_set_crypt(req, &sg, &sg, PAGE_CACHE_SIZE, xts_tweak);
+	res = crypto_ablkcipher_decrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+	ablkcipher_request_free(req);
+out:
+	if (res)
+		printk_ratelimited(KERN_ERR "%s: res = %d\n", __func__, res);
+	return res;
+}
+
+/**
+ * ext4_decrypt() - Decrypts a page in-place
+ * @ctx:  The encryption context.
+ * @page: The page to decrypt. Must be locked.
+ *
+ * Decrypts page in-place using the ctx encryption context.
+ *
+ * Called from the read completion callback.
+ *
+ * Return: Zero on success, non-zero otherwise.
+ */
+int ext4_decrypt(struct ext4_crypto_ctx *ctx, struct page *page)
+{
+	int res = 0;
+
+	BUG_ON(!PageLocked(page));
+
+	switch (ctx->mode) {
+	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+		res = ext4_xts_decrypt(ctx, page);
+		break;
+	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
+		res = -ENOTSUPP;
+		break;
+	default:
+		BUG();
+	}
+	return res;
+}
+
+/*
+ * Convenience function which takes care of allocating and
+ * deallocating the encryption context
+ */
+int ext4_decrypt_one(struct inode *inode, struct page *page)
+{
+	int ret;
+
+	struct ext4_crypto_ctx *ctx = ext4_get_crypto_ctx(inode);
+	if (!ctx)
+		return -ENOMEM;
+	ret = ext4_decrypt(ctx, page);
+	ext4_release_crypto_ctx(ctx);
+	return ret;
+}
+
+/**
+ * ext4_validate_encryption_mode() - Validates the encryption key mode
+ * @mode: The key mode to validate.
+ *
+ * Return: The validated key mode. EXT4_ENCRYPTION_MODE_INVALID if invalid.
+ */
+uint32_t ext4_validate_encryption_mode(uint32_t mode)
+{
+	switch (mode) {
+	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+		return mode;
+	case EXT4_ENCRYPTION_MODE_AES_256_CBC:
+		return mode;
+	default:
+		break;
+	}
+	return EXT4_ENCRYPTION_MODE_INVALID;
+}
+
+/**
+ * ext4_validate_encryption_key_size() - Validate the encryption key size
+ * @mode: The key mode.
+ * @size: The key size to validate.
+ *
+ * Return: The validated key size for @mode. Zero if invalid.
+ */
+uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size)
+{
+	if (size == ext4_encryption_key_size(mode))
+		return size;
+	return 0;
+}
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index 5cb4e74..3ff4c75 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -71,14 +71,31 @@ static int ext4_create_encryption_context_from_policy(
 	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
 	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
 	       EXT4_KEY_DESCRIPTOR_SIZE);
-	ctx.contents_encryption_mode = policy->contents_encryption_mode;
-	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
+	ctx.contents_encryption_mode = ext4_validate_encryption_mode(
+		policy->contents_encryption_mode);
+	if (ctx.contents_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) {
+		printk(KERN_WARNING
+		       "%s: Invalid contents encryption mode %d\n", __func__,
+			policy->contents_encryption_mode);
+		res = -EINVAL;
+		goto out;
+	}
+	ctx.filenames_encryption_mode = ext4_validate_encryption_mode(
+		policy->filenames_encryption_mode);
+	if (ctx.filenames_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) {
+		printk(KERN_WARNING
+		       "%s: Invalid filenames encryption mode %d\n", __func__,
+			policy->filenames_encryption_mode);
+		res = -EINVAL;
+		goto out;
+	}
 	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
 	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
 
 	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
 			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
 			     sizeof(ctx), 0);
+out:
 	if (!res)
 		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
 	return res;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 2d7fcb6..f7ee6c0 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -948,6 +948,11 @@ struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	/* Encryption params */
+	struct ext4_encryption_key i_encryption_key;
+#endif
 };
 
 /*
@@ -1349,6 +1354,12 @@ struct ext4_sb_info {
 	struct ratelimit_state s_err_ratelimit_state;
 	struct ratelimit_state s_warning_ratelimit_state;
 	struct ratelimit_state s_msg_ratelimit_state;
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	/* Encryption */
+	uint32_t s_file_encryption_mode;
+	uint32_t s_dir_encryption_mode;
+#endif
 };
 
 static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1998,6 +2009,34 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
 					      struct ext4_group_desc *gdp);
 ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
 
+/* crypto.c */
+uint32_t ext4_validate_encryption_mode(uint32_t mode);
+uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
+extern struct workqueue_struct *ext4_read_workqueue;
+struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode);
+void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx);
+void ext4_restore_control_page(struct page *data_page);
+struct page *ext4_encrypt(struct inode *inode,
+			  struct page *plaintext_page);
+int ext4_decrypt(struct ext4_crypto_ctx *ctx, struct page *page);
+int ext4_decrypt_one(struct inode *inode, struct page *page);
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+int ext4_init_crypto(void);
+void ext4_exit_crypto(void);
+static inline int ext4_sb_has_crypto(struct super_block *sb)
+{
+	return EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT);
+}
+#else
+static inline int ext4_init_crypto(void) { return 0; }
+static inline void ext4_exit_crypto(void) { }
+static inline int ext4_sb_has_crypto(struct super_block *sb)
+{
+	return 0;
+}
+#endif
+
 /* dir.c */
 extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
 				  struct file *,
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index 984ff38..fb73935 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -51,4 +51,47 @@ void ext4_to_hex(char *dst, char *src, size_t src_size);
 int ext4_process_policy(const struct ext4_encryption_policy *policy,
 			struct inode *inode);
 
+/* Encryption parameters */
+#define EXT4_AES_256_XTS_KEY_SIZE 64
+#define EXT4_XTS_TWEAK_SIZE 16
+#define EXT4_AES_128_ECB_KEY_SIZE 16
+#define EXT4_AES_256_GCM_KEY_SIZE 32
+#define EXT4_AES_256_CBC_KEY_SIZE 32
+#define EXT4_MAX_KEY_SIZE 64
+
+struct ext4_encryption_key {
+	uint32_t mode;
+	char raw[EXT4_MAX_KEY_SIZE];
+	uint32_t size;
+};
+
+#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL             0x00000001
+#define EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL     0x00000002
+
+struct ext4_crypto_ctx {
+	struct crypto_tfm *tfm;         /* Crypto API context */
+	struct page *bounce_page;       /* Ciphertext page on write path */
+	struct page *control_page;      /* Original page on write path */
+	struct bio *bio;                /* The bio for this context */
+	struct work_struct work;        /* Work queue for read complete path */
+	struct list_head free_list;     /* Free list */
+	int flags;                      /* Flags */
+	int mode;                       /* Encryption mode for tfm */
+};
+
+static inline int ext4_encryption_key_size(int mode)
+{
+	switch (mode) {
+	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
+		return EXT4_AES_256_XTS_KEY_SIZE;
+	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
+		return EXT4_AES_256_GCM_KEY_SIZE;
+	case EXT4_ENCRYPTION_MODE_AES_256_CBC:
+		return EXT4_AES_256_CBC_KEY_SIZE;
+	default:
+		BUG();
+	}
+	return 0;
+}
+
 #endif	/* _EXT4_CRYPTO_H */
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 74c5f53..3dcafe9 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -893,6 +893,9 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
 	atomic_set(&ei->i_ioend_count, 0);
 	atomic_set(&ei->i_unwritten, 0);
 	INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	ei->i_encryption_key.mode = EXT4_ENCRYPTION_MODE_INVALID;
+#endif
 
 	return &ei->vfs_inode;
 }
@@ -3439,6 +3442,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	if (sb->s_bdev->bd_part)
 		sbi->s_sectors_written_start =
 			part_stat_read(sb->s_bdev->bd_part, sectors[1]);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	/* Modes of operations for file and directory encryption. */
+	sbi->s_file_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+	sbi->s_dir_encryption_mode = EXT4_ENCRYPTION_MODE_INVALID;
+#endif
 
 	/* Cleanup superblock name */
 	for (cp = sb->s_id; (cp = strchr(cp, '/'));)
@@ -4052,6 +4060,9 @@ no_journal:
 		goto failed_mount4;
 	}
 
+	if (ext4_sb_has_crypto(sb))
+		ext4_init_crypto();
+
 	/*
 	 * The jbd2_journal_load will have done any necessary log recovery,
 	 * so we can safely mount the rest of the filesystem now.
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 09/22] ext4 crypto: add encryption key management facilities
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (7 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 08/22] ext4 crypto: add ext4 encryption facilities Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 10/22] ext4 crypto: validate context consistency on lookup Theodore Ts'o
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: I0cb5711a628554d3b05cefd15740d8346115cbaa
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/Makefile      |   2 +-
 fs/ext4/crypto_key.c  | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/ext4.h        |  25 ++++++++
 fs/ext4/ext4_crypto.h |   3 +
 4 files changed, 196 insertions(+), 1 deletion(-)
 create mode 100644 fs/ext4/crypto_key.c

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 1b1c561..4e5af21 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,4 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
-ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o
+ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o crypto_key.o
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
new file mode 100644
index 0000000..3620e16
--- /dev/null
+++ b/fs/ext4/crypto_key.c
@@ -0,0 +1,167 @@
+/*
+ * linux/fs/ext4/crypto_key.c
+ *
+ * This contains encryption key functions for ext4
+ *
+ * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ */
+
+#include <keys/encrypted-type.h>
+#include <keys/user-type.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <uapi/linux/keyctl.h>
+
+#include "ext4.h"
+#include "xattr.h"
+
+struct derive_crypt_result {
+	struct completion completion;
+	int rc;
+};
+
+static void derive_crypt_complete(struct crypto_async_request *req, int rc)
+{
+	struct derive_crypt_result *dcr = req->data;
+
+	if (rc == -EINPROGRESS)
+		return;
+
+	dcr->rc = rc;
+	complete(&dcr->completion);
+}
+
+/**
+ * ext4_derive_key_aes() - Derive a key using AES-128-ECB
+ * @deriving_key: Encryption key used for derivatio.
+ * @source_key:   Source key to which to apply derivation.
+ * @derived_key:  Derived key.
+ *
+ * Return: Zero on success; non-zero otherwise.
+ */
+static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE],
+			       char source_key[EXT4_AES_256_XTS_KEY_SIZE],
+			       char derived_key[EXT4_AES_256_XTS_KEY_SIZE])
+{
+	int res = 0;
+	struct ablkcipher_request *req = NULL;
+	struct derive_crypt_result dcr;
+	struct scatterlist src_sg, dst_sg;
+	struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0,
+								0);
+
+	if (IS_ERR(tfm)) {
+		res = PTR_ERR(tfm);
+		tfm = NULL;
+		goto out;
+	}
+	crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		res = -ENOMEM;
+		goto out;
+	}
+	ablkcipher_request_set_callback(req,
+			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+			derive_crypt_complete, &dcr);
+	res = crypto_ablkcipher_setkey(tfm, deriving_key,
+				       EXT4_AES_128_ECB_KEY_SIZE);
+	if (res < 0)
+		goto out;
+	sg_init_one(&src_sg, source_key, EXT4_AES_256_XTS_KEY_SIZE);
+	sg_init_one(&dst_sg, derived_key, EXT4_AES_256_XTS_KEY_SIZE);
+	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg,
+				     EXT4_AES_256_XTS_KEY_SIZE, NULL);
+	res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		struct derive_crypt_result *dcr = req->base.data;
+
+		wait_for_completion(&dcr->completion);
+		res = dcr->rc;
+	}
+
+out:
+	if (req)
+		ablkcipher_request_free(req);
+	if (tfm)
+		crypto_free_ablkcipher(tfm);
+	return res;
+}
+
+/**
+ * ext4_generate_encryption_key() - generates an encryption key
+ * @inode: The inode to generate the encryption key for.
+ */
+int ext4_generate_encryption_key(struct inode *inode)
+{
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	struct ext4_encryption_key *crypt_key = &ei->i_encryption_key;
+	char full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
+				 (EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1];
+	struct key *keyring_key = NULL;
+	struct ext4_encryption_key *master_key;
+	struct ext4_encryption_context ctx;
+	struct user_key_payload *ukp;
+	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+				 &ctx, sizeof(ctx));
+
+	if (res != sizeof(ctx)) {
+		if (res > 0)
+			res = -EINVAL;
+		goto out;
+	}
+	res = 0;
+
+	memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX,
+	       EXT4_KEY_DESC_PREFIX_SIZE);
+	ext4_to_hex(&full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE],
+		    ctx.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
+	full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE +
+			    (2 * EXT4_KEY_DESCRIPTOR_SIZE)] = '\0';
+	keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
+	if (IS_ERR(keyring_key)) {
+		res = PTR_ERR(keyring_key);
+		keyring_key = NULL;
+		goto out;
+	}
+	BUG_ON(keyring_key->type != &key_type_logon);
+	ukp = ((struct user_key_payload *)keyring_key->payload.data);
+	if (ukp->datalen != sizeof(struct ext4_encryption_key)) {
+		res = -EINVAL;
+		goto out;
+	}
+	master_key = (struct ext4_encryption_key *)ukp->data;
+
+	if (S_ISREG(inode->i_mode))
+		crypt_key->mode = ctx.contents_encryption_mode;
+	else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+		crypt_key->mode = ctx.filenames_encryption_mode;
+	else {
+		printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n");
+		BUG();
+	}
+	crypt_key->size = ext4_encryption_key_size(crypt_key->mode);
+	BUG_ON(!crypt_key->size);
+	BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE !=
+		     EXT4_KEY_DERIVATION_NONCE_SIZE);
+	BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE);
+	BUG_ON(crypt_key->size < EXT4_AES_256_CBC_KEY_SIZE);
+	res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw);
+out:
+	if (keyring_key)
+		key_put(keyring_key);
+	if (res < 0)
+		crypt_key->mode = EXT4_ENCRYPTION_MODE_INVALID;
+	return res;
+}
+
+int ext4_has_encryption_key(struct inode *inode)
+{
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	struct ext4_encryption_key *crypt_key = &ei->i_encryption_key;
+
+	return (crypt_key->mode != EXT4_ENCRYPTION_MODE_INVALID);
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f7ee6c0..ecb7be83 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1475,6 +1475,18 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define EXT4_SB(sb)	(sb)
 #endif
 
+/*
+ * Returns true if the inode is inode is encrypted
+ */
+static inline int ext4_encrypted_inode(struct inode *inode)
+{
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT);
+#else
+	return 0;
+#endif
+}
+
 #define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
 
 /*
@@ -2037,6 +2049,19 @@ static inline int ext4_sb_has_crypto(struct super_block *sb)
 }
 #endif
 
+/* crypto_key.c */
+int ext4_generate_encryption_key(struct inode *inode);
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+int ext4_has_encryption_key(struct inode *inode);
+#else
+static inline int ext4_has_encryption_key(struct inode *inode)
+{
+	return 0;
+}
+#endif
+
+
 /* dir.c */
 extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
 				  struct file *,
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index fb73935..3bff49c 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -59,6 +59,9 @@ int ext4_process_policy(const struct ext4_encryption_policy *policy,
 #define EXT4_AES_256_CBC_KEY_SIZE 32
 #define EXT4_MAX_KEY_SIZE 64
 
+#define EXT4_KEY_DESC_PREFIX "ext4:"
+#define EXT4_KEY_DESC_PREFIX_SIZE 5
+
 struct ext4_encryption_key {
 	uint32_t mode;
 	char raw[EXT4_MAX_KEY_SIZE];
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 10/22] ext4 crypto: validate context consistency on lookup
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (8 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 09/22] ext4 crypto: add encryption key management facilities Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 11/22] ext4 crypto: inherit encryption policies on inode and directory create Theodore Ts'o
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Change-Id: Ifb904b2bec9300b178062ee70cbdfd333f03f865
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/namei.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2fb55fd..12d2592 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1418,6 +1418,13 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
 			return ERR_PTR(-EIO);
 		}
 	}
+	if (ext4_encrypted_inode(dir) &&
+	    !ext4_is_child_context_consistent_with_parent(dir,
+							  dentry->d_inode)) {
+		printk(KERN_ERR "%s: Security warning: Inconsistent contexts\n",
+		       __func__);
+		return ERR_PTR(-EINVAL);
+	}
 	return d_splice_alias(inode, dentry);
 }
 
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 11/22] ext4 crypto: inherit encryption policies on inode and directory create
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (9 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 10/22] ext4 crypto: validate context consistency on lookup Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path Theodore Ts'o
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: Ibeeafc70352b39d1d5b3b17158a41d8fb54ed136
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/namei.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 12d2592..262aa1c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -48,6 +48,8 @@
 #define NAMEI_RA_BLOCKS  4
 #define NAMEI_RA_SIZE	     (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
 
+static int ext4_unlink(struct inode *dir, struct dentry *dentry);
+
 static struct buffer_head *ext4_append(handle_t *handle,
 					struct inode *inode,
 					ext4_lblk_t *block)
@@ -2247,6 +2249,13 @@ retry:
 		err = ext4_add_nondir(handle, dentry, inode);
 		if (!err && IS_DIRSYNC(dir))
 			ext4_handle_sync(handle);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		if (!err && ext4_encrypted_inode(dir)) {
+			err = ext4_inherit_context(dir, inode);
+			if (err)
+				ext4_unlink(dir, dentry);
+		}
+#endif
 	}
 	if (handle)
 		ext4_journal_stop(handle);
@@ -2445,6 +2454,13 @@ out_clear_inode:
 	d_instantiate(dentry, inode);
 	if (IS_DIRSYNC(dir))
 		ext4_handle_sync(handle);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ext4_encrypted_inode(dir)) {
+		err = ext4_inherit_context(dir, inode);
+		if (err)
+			ext4_unlink(dir, dentry);
+	}
+#endif
 
 out_stop:
 	if (handle)
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (10 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 11/22] ext4 crypto: inherit encryption policies on inode and directory create Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-09 21:44   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path Theodore Ts'o
                   ` (11 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Pulls block_write_begin() into fs/ext4/inode.c because it might need
to do a low-level read of the existing data, in which case we need to
decrypt it.

Change-Id: I2337918809c43e18454a1d5621024d2699a98666
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/extents.c |   6 +++
 fs/ext4/ialloc.c  |   5 +++
 fs/ext4/inode.c   | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/ext4/page-io.c |  46 +++++++++++++++++++---
 4 files changed, 164 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index bed4308..1f1c0ea 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4922,6 +4922,12 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
 	ext4_lblk_t lblk;
 	unsigned int blkbits = inode->i_blkbits;
 
+	/*
+	 * TODO: We don't yet support fallocate with encrypted files.
+	 */
+	if (ext4_encrypted_inode(inode))
+		return -EOPNOTSUPP;
+
 	/* Return error if mode is not supported */
 	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
 		     FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ac644c3..e554ca3 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -997,6 +997,11 @@ got:
 	ei->i_block_group = group;
 	ei->i_last_alloc_group = ~0;
 
+	/* If the directory encrypted, then we should encrypt the inode. */
+	if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) &&
+	    ext4_encrypted_inode(dir))
+		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
+
 	ext4_set_inode_flags(inode);
 	if (IS_DIRSYNC(inode))
 		ext4_handle_sync(handle);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index a68cacc..dcc836c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -41,6 +41,7 @@
 #include <linux/bitops.h>
 
 #include "ext4_jbd2.h"
+#include "ext4_crypto.h"
 #include "xattr.h"
 #include "acl.h"
 #include "truncate.h"
@@ -871,6 +872,95 @@ int do_journal_get_write_access(handle_t *handle,
 
 static int ext4_get_block_write_nolock(struct inode *inode, sector_t iblock,
 		   struct buffer_head *bh_result, int create);
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
+				  get_block_t *get_block)
+{
+	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+	unsigned to = from + len;
+	struct inode *inode = page->mapping->host;
+	unsigned block_start, block_end;
+	sector_t block;
+	int err = 0;
+	unsigned blocksize = inode->i_sb->s_blocksize;
+	unsigned bbits;
+	struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
+	bool decrypt = false;
+
+	BUG_ON(!PageLocked(page));
+	BUG_ON(from > PAGE_CACHE_SIZE);
+	BUG_ON(to > PAGE_CACHE_SIZE);
+	BUG_ON(from > to);
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, blocksize, 0);
+	head = page_buffers(page);
+	bbits = ilog2(blocksize);
+	block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits);
+
+	for (bh = head, block_start = 0; bh != head || !block_start;
+	    block++, block_start = block_end, bh = bh->b_this_page) {
+		block_end = block_start + blocksize;
+		if (block_end <= from || block_start >= to) {
+			if (PageUptodate(page)) {
+				if (!buffer_uptodate(bh))
+					set_buffer_uptodate(bh);
+			}
+			continue;
+		}
+		if (buffer_new(bh))
+			clear_buffer_new(bh);
+		if (!buffer_mapped(bh)) {
+			WARN_ON(bh->b_size != blocksize);
+			err = get_block(inode, block, bh, 1);
+			if (err)
+				break;
+			if (buffer_new(bh)) {
+				unmap_underlying_metadata(bh->b_bdev,
+							  bh->b_blocknr);
+				if (PageUptodate(page)) {
+					clear_buffer_new(bh);
+					set_buffer_uptodate(bh);
+					mark_buffer_dirty(bh);
+					continue;
+				}
+				if (block_end > to || block_start < from)
+					zero_user_segments(page, to, block_end,
+							   block_start, from);
+				continue;
+			}
+		}
+		if (PageUptodate(page)) {
+			if (!buffer_uptodate(bh))
+				set_buffer_uptodate(bh);
+			continue;
+		}
+		if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
+		    !buffer_unwritten(bh) &&
+		    (block_start < from || block_end > to)) {
+			ll_rw_block(READ, 1, &bh);
+			*wait_bh++ = bh;
+			decrypt = ext4_encrypted_inode(inode) &&
+				S_ISREG(inode->i_mode);
+		}
+	}
+	/*
+	 * If we issued read requests, let them complete.
+	 */
+	while (wait_bh > wait) {
+		wait_on_buffer(*--wait_bh);
+		if (!buffer_uptodate(*wait_bh))
+			err = -EIO;
+	}
+	if (unlikely(err))
+		page_zero_new_buffers(page, from, to);
+	else if (decrypt)
+		err = ext4_decrypt_one(inode, page);
+	return err;
+}
+#endif
+
 static int ext4_write_begin(struct file *file, struct address_space *mapping,
 			    loff_t pos, unsigned len, unsigned flags,
 			    struct page **pagep, void **fsdata)
@@ -933,11 +1023,19 @@ retry_journal:
 	/* In case writeback began while the page was unlocked */
 	wait_for_stable_page(page);
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ext4_should_dioread_nolock(inode))
+		ret = ext4_block_write_begin(page, pos, len,
+					     ext4_get_block_write);
+	else
+		ret = ext4_block_write_begin(page, pos, len,
+					     ext4_get_block);
+#else
 	if (ext4_should_dioread_nolock(inode))
 		ret = __block_write_begin(page, pos, len, ext4_get_block_write);
 	else
 		ret = __block_write_begin(page, pos, len, ext4_get_block);
-
+#endif
 	if (!ret && ext4_should_journal_data(inode)) {
 		ret = ext4_walk_page_buffers(handle, page_buffers(page),
 					     from, to, NULL,
@@ -2552,7 +2650,12 @@ retry_journal:
 	/* In case writeback began while the page was unlocked */
 	wait_for_stable_page(page);
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	ret = ext4_block_write_begin(page, pos, len,
+				     ext4_da_get_block_prep);
+#else
 	ret = __block_write_begin(page, pos, len, ext4_da_get_block_prep);
+#endif
 	if (ret < 0) {
 		unlock_page(page);
 		ext4_journal_stop(handle);
@@ -3010,6 +3113,9 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
 		get_block_func = ext4_get_block_write;
 		dio_flags = DIO_LOCKING;
 	}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
+#endif
 	ret = __blockdev_direct_IO(rw, iocb, inode,
 				   inode->i_sb->s_bdev, iter,
 				   offset,
@@ -3073,6 +3179,11 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
 	size_t count = iov_iter_count(iter);
 	ssize_t ret;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+		return 0;
+#endif
+
 	/*
 	 * If we are doing data journalling we don't support O_DIRECT
 	 */
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index b24a254..71fb0e6 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -28,6 +28,7 @@
 #include <linux/ratelimit.h>
 
 #include "ext4_jbd2.h"
+#include "ext4_crypto.h"
 #include "xattr.h"
 #include "acl.h"
 
@@ -69,6 +70,10 @@ static void ext4_finish_bio(struct bio *bio)
 
 	bio_for_each_segment_all(bvec, bio, i) {
 		struct page *page = bvec->bv_page;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		struct page *data_page = NULL;
+		struct ext4_crypto_ctx *ctx = NULL;
+#endif
 		struct buffer_head *bh, *head;
 		unsigned bio_start = bvec->bv_offset;
 		unsigned bio_end = bio_start + bvec->bv_len;
@@ -78,6 +83,15 @@ static void ext4_finish_bio(struct bio *bio)
 		if (!page)
 			continue;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		if (!page->mapping) {
+			/* The bounce data pages are unmapped. */
+			data_page = page;
+			ctx = (struct ext4_crypto_ctx *)page_private(data_page);
+			page = ctx->control_page;
+		}
+#endif
+
 		if (error) {
 			SetPageError(page);
 			set_bit(AS_EIO, &page->mapping->flags);
@@ -102,8 +116,13 @@ static void ext4_finish_bio(struct bio *bio)
 		} while ((bh = bh->b_this_page) != head);
 		bit_spin_unlock(BH_Uptodate_Lock, &head->b_state);
 		local_irq_restore(flags);
-		if (!under_io)
+		if (!under_io) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+			if (ctx)
+				ext4_restore_control_page(data_page);
+#endif
 			end_page_writeback(page);
+		}
 	}
 }
 
@@ -378,6 +397,7 @@ static int io_submit_init_bio(struct ext4_io_submit *io,
 
 static int io_submit_add_bh(struct ext4_io_submit *io,
 			    struct inode *inode,
+			    struct page *page,
 			    struct buffer_head *bh)
 {
 	int ret;
@@ -391,7 +411,7 @@ submit_and_retry:
 		if (ret)
 			return ret;
 	}
-	ret = bio_add_page(io->io_bio, bh->b_page, bh->b_size, bh_offset(bh));
+	ret = bio_add_page(io->io_bio, page, bh->b_size, bh_offset(bh));
 	if (ret != bh->b_size)
 		goto submit_and_retry;
 	io->io_next_block++;
@@ -404,6 +424,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
 			struct writeback_control *wbc,
 			bool keep_towrite)
 {
+	struct page *data_page = NULL;
 	struct inode *inode = page->mapping->host;
 	unsigned block_start, blocksize;
 	struct buffer_head *bh, *head;
@@ -463,19 +484,29 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
 		set_buffer_async_write(bh);
 	} while ((bh = bh->b_this_page) != head);
 
-	/* Now submit buffers to write */
 	bh = head = page_buffers(page);
+
+	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
+		data_page = ext4_encrypt(inode, page);
+		if (IS_ERR(data_page)) {
+			ret = PTR_ERR(data_page);
+			data_page = NULL;
+			goto out;
+		}
+	}
+
+	/* Now submit buffers to write */
 	do {
 		if (!buffer_async_write(bh))
 			continue;
-		ret = io_submit_add_bh(io, inode, bh);
+		ret = io_submit_add_bh(io, inode,
+				       data_page ? data_page : page, bh);
 		if (ret) {
 			/*
 			 * We only get here on ENOMEM.  Not much else
 			 * we can do but mark the page as dirty, and
 			 * better luck next time.
 			 */
-			redirty_page_for_writepage(wbc, page);
 			break;
 		}
 		nr_submitted++;
@@ -484,6 +515,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
 
 	/* Error stopped previous loop? Clean up buffers... */
 	if (ret) {
+	out:
+		if (data_page)
+			ext4_restore_control_page(data_page);
+		printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret);
+		redirty_page_for_writepage(wbc, page);
 		do {
 			clear_buffer_async_write(bh);
 			bh = bh->b_this_page;
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (11 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-08 18:51   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 14/22] ext4 crypto: filename encryption facilities Theodore Ts'o
                   ` (10 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: Ie9c043a132a01da60d1617662cd30307639f5599
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/file.c     | 22 +++++++++++++++----
 fs/ext4/inode.c    | 10 +++++++++
 fs/ext4/readpage.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 8131be8..4cacc30 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -28,6 +28,7 @@
 #include <linux/pagevec.h>
 #include "ext4.h"
 #include "ext4_jbd2.h"
+#include "ext4_crypto.h"
 #include "xattr.h"
 #include "acl.h"
 
@@ -200,9 +201,15 @@ static const struct vm_operations_struct ext4_file_vm_ops = {
 
 static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
+	int res = 0;
+	struct inode *inode = file->f_mapping->host;
+
+	if (ext4_encrypted_inode(inode))
+		res = ext4_generate_encryption_key(inode);
 	file_accessed(file);
-	vma->vm_ops = &ext4_file_vm_ops;
-	return 0;
+	if (!res)
+		vma->vm_ops = &ext4_file_vm_ops;
+	return res;
 }
 
 static int ext4_file_open(struct inode * inode, struct file * filp)
@@ -212,6 +219,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
 	struct vfsmount *mnt = filp->f_path.mnt;
 	struct path path;
 	char buf[64], *cp;
+	int ret;
 
 	if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
 		     !(sb->s_flags & MS_RDONLY))) {
@@ -250,11 +258,17 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
 	 * writing and the journal is present
 	 */
 	if (filp->f_mode & FMODE_WRITE) {
-		int ret = ext4_inode_attach_jinode(inode);
+		ret = ext4_inode_attach_jinode(inode);
 		if (ret < 0)
 			return ret;
 	}
-	return dquot_file_open(inode, filp);
+	ret = dquot_file_open(inode, filp);
+	if (!ret && ext4_encrypted_inode(inode)) {
+		ret = ext4_generate_encryption_key(inode);
+		if (ret)
+			ret = -EACCES;
+	}
+	return ret;
 }
 
 /*
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index dcc836c..b033405 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -39,6 +39,7 @@
 #include <linux/ratelimit.h>
 #include <linux/aio.h>
 #include <linux/bitops.h>
+#include <linux/prefetch.h>
 
 #include "ext4_jbd2.h"
 #include "ext4_crypto.h"
@@ -3363,6 +3364,15 @@ static int ext4_block_zero_page_range(handle_t *handle,
 		/* Uhhuh. Read error. Complain and punt. */
 		if (!buffer_uptodate(bh))
 			goto unlock;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		if (S_ISREG(inode->i_mode) &&
+		    ext4_encrypted_inode(inode)) {
+			/* We expect the key to be set. */
+			BUG_ON(!ext4_has_encryption_key(inode));
+			BUG_ON(blocksize != PAGE_CACHE_SIZE);
+			WARN_ON_ONCE(ext4_decrypt_one(inode, page));
+		}
+#endif
 	}
 	if (ext4_should_journal_data(inode)) {
 		BUFFER_TRACE(bh, "get write access");
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index 9ca4dfc..8978b1d 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -43,6 +43,35 @@
 
 #include "ext4.h"
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+/*
+ * Call ext4_decrypt on every single page, reusing the encryption
+ * context.
+ */
+static void completion_pages(struct work_struct *work)
+{
+	struct ext4_crypto_ctx *ctx =
+		container_of(work, struct ext4_crypto_ctx, work);
+	struct bio	*bio	= ctx->bio;
+	struct bio_vec	*bv;
+	int		i;
+
+	bio_for_each_segment_all(bv, bio, i) {
+		struct page *page = bv->bv_page;
+
+		int ret = ext4_decrypt(ctx, page);
+		if (ret) {
+			WARN_ON_ONCE(1);
+			SetPageError(page);
+		} else
+			SetPageUptodate(page);
+		unlock_page(page);
+	}
+	ext4_release_crypto_ctx(ctx);
+	bio_put(bio);
+}
+#endif
+
 /*
  * I/O completion handler for multipage BIOs.
  *
@@ -60,6 +89,20 @@ static void mpage_end_io(struct bio *bio, int err)
 	struct bio_vec *bv;
 	int i;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (bio->bi_private) {
+		struct ext4_crypto_ctx *ctx = bio->bi_private;
+
+		if (err)
+			ext4_release_crypto_ctx(ctx);
+		else {
+			INIT_WORK(&ctx->work, completion_pages);
+			ctx->bio = bio;
+			queue_work(ext4_read_workqueue, &ctx->work);
+			return;
+		}
+	}
+#endif
 	bio_for_each_segment_all(bv, bio, i) {
 		struct page *page = bv->bv_page;
 
@@ -94,9 +137,15 @@ int ext4_mpage_readpages(struct address_space *mapping,
 	unsigned page_block;
 	struct block_device *bdev = inode->i_sb->s_bdev;
 	int length;
+	int do_decryption = 0;
 	unsigned relative_block = 0;
 	struct ext4_map_blocks map;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+		do_decryption = 1;
+#endif
+
 	map.m_pblk = 0;
 	map.m_lblk = 0;
 	map.m_len = 0;
@@ -220,13 +269,24 @@ int ext4_mpage_readpages(struct address_space *mapping,
 			bio = NULL;
 		}
 		if (bio == NULL) {
+			struct ext4_crypto_ctx *ctx = NULL;
+
+			if (do_decryption) {
+				ctx = ext4_get_crypto_ctx(inode);
+				if (IS_ERR(ctx))
+					goto set_error_page;
+			}
 			bio = bio_alloc(GFP_KERNEL,
 				min_t(int, nr_pages, bio_get_nr_vecs(bdev)));
-			if (!bio)
+			if (!bio) {
+				if (ctx)
+					ext4_release_crypto_ctx(ctx);
 				goto set_error_page;
+			}
 			bio->bi_bdev = bdev;
 			bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
 			bio->bi_end_io = mpage_end_io;
+			bio->bi_private = ctx;
 		}
 
 		length = first_hole << blkbits;
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 14/22] ext4 crypto: filename encryption facilities
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (12 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 15/22] ext4: teach ext4_htree_store_dirent() to store decrypted filenames Theodore Ts'o
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Uday Savagaonkar, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: Ia924500cc0395bc96b7099962041641eb336276b
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/Makefile       |   3 +-
 fs/ext4/crypto_fname.c | 831 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/ext4.h         |  41 +++
 fs/ext4/ext4_crypto.h  |  20 ++
 4 files changed, 894 insertions(+), 1 deletion(-)
 create mode 100644 fs/ext4/crypto_fname.c

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 4e5af21..75285ea 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,4 +12,5 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
-ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o crypto_key.o
+ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o \
+		crypto_key.o crypto_fname.o
diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c
new file mode 100644
index 0000000..771aa8c
--- /dev/null
+++ b/fs/ext4/crypto_fname.c
@@ -0,0 +1,831 @@
+/*
+ * linux/fs/ext4/ext4_fname_crypto.c
+ *
+ * This contains functions for filename crypto management in ext4
+ *
+ * Written by Uday Savagaonkar, 2014.
+ *
+ * This has not yet undergone a rigorous security audit.
+ *
+ */
+
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <keys/encrypted-type.h>
+#include <keys/user-type.h>
+#include <linux/crypto.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/key.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock_types.h>
+
+#include "ext4.h"
+#include "ext4_crypto.h"
+#include "xattr.h"
+
+/**
+ * copy_bufs_to_page() - copies cstr bufs into a page
+ * @pagebuf: Receives the concatenated contents of the @bufs array.
+ * @bufs:    An array of cstr buffers.
+ * @tlen:    Number of elements in the @bufs array.
+ */
+static void copy_bufs_to_page(char *pagebuf, const struct ext4_str *bufs,
+			      u32 tlen)
+{
+	u32 tcopied = 0, remaining = tlen, bytes_this_iter;
+	int i = 0;
+
+	while (remaining) {
+		bytes_this_iter = (remaining > bufs[i].len ?
+				   bufs[i].len : remaining);
+		memcpy(pagebuf + tcopied, bufs[i].name, bytes_this_iter);
+		tcopied += bytes_this_iter;
+		remaining -= bytes_this_iter;
+		++i;
+	}
+}
+
+struct ext4_dir_crypt_result {
+	struct completion completion;
+	int res;
+};
+
+/**
+ * ext4_dir_crypt_complete() -
+ */
+static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res)
+{
+	struct ext4_dir_crypt_result *ecr = req->data;
+
+	if (res == -EINPROGRESS)
+		return;
+	ecr->res = res;
+	complete(&ecr->completion);
+}
+
+/**
+ * ext4_cbc_encrypt() -
+ */
+static int ext4_cbc_encrypt(struct ext4_fname_crypto_ctx *ctx,
+			    struct ext4_str *oname,
+			    const struct ext4_str *ibufs, u32 ilen)
+{
+	struct ablkcipher_request *req = NULL;
+	struct ext4_dir_crypt_result ecr;
+	struct scatterlist sg[1];
+	struct crypto_ablkcipher *tfm = ctx->ctfm;
+	int res = 0;
+	int ciphertext_len;
+	char iv[EXT4_CRYPTO_BLOCK_SIZE];
+	char *workbuf;
+
+	/* Allocate request */
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(
+		    KERN_ERR "%s: crypto_request_alloc() failed\n", __func__);
+		return -ENOMEM;
+	}
+	ablkcipher_request_set_callback(req,
+		CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		ext4_dir_crypt_complete, &ecr);
+
+	ciphertext_len = ext4_fname_crypto_round_up(ilen,
+			EXT4_CRYPTO_BLOCK_SIZE);
+	ciphertext_len = (ciphertext_len > ctx->lim)
+			? ctx->lim : ciphertext_len;
+
+	/* Map the workpage */
+	workbuf = kmap(ctx->workpage);
+
+	/* Copy the input */
+	copy_bufs_to_page(workbuf, ibufs, ilen);
+	if (ilen < ciphertext_len)
+		memset(workbuf+ilen, 0, ciphertext_len-ilen);
+
+	/* Initialize IV */
+	memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);
+
+	/* Create encryption request */
+	sg_init_table(sg, 1);
+	sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0);
+	ablkcipher_request_set_crypt(req, sg, sg, ciphertext_len, iv);
+	res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+		if (res)
+			goto out;
+	}
+	/* Copy the result to output */
+	memcpy(oname->name, workbuf, ciphertext_len);
+	oname->len = ciphertext_len;
+	res = ciphertext_len;
+
+out:
+	kunmap(ctx->workpage);
+	ablkcipher_request_free(req);
+	if (res < 0) {
+		printk_ratelimited(
+		    KERN_ERR "%s: Error (error code %d)\n", __func__, res);
+	}
+	return res;
+}
+
+/**
+ * ext4_cbc_decrypt() -
+ */
+static int ext4_cbc_decrypt(struct ext4_fname_crypto_ctx *ctx,
+			    struct ext4_str *oname,
+			    const struct ext4_str *ibufs, u32 ilen)
+{
+	struct ablkcipher_request *req = NULL;
+	struct ext4_dir_crypt_result ecr;
+	struct scatterlist sg[1];
+	struct crypto_ablkcipher *tfm = ctx->ctfm;
+	int res = 0;
+	char iv[EXT4_CRYPTO_BLOCK_SIZE];
+	char *workbuf;
+
+	if ((ilen&(EXT4_CRYPTO_BLOCK_SIZE-1)) != 0) {
+		printk_ratelimited(
+		    KERN_ERR "%s: ilen is not integer multiple of EXT4_CRYPTO_BLOCK_SIZE\n",
+		    __func__);
+		return -EIO;
+	}
+	/* Allocate request */
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(
+		    KERN_ERR "%s: crypto_request_alloc() failed\n",  __func__);
+		return -ENOMEM;
+	}
+	ablkcipher_request_set_callback(req,
+		CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		ext4_dir_crypt_complete, &ecr);
+
+	/* Map the workpage */
+	workbuf = kmap(ctx->workpage);
+
+	/* Copy the input */
+	copy_bufs_to_page(workbuf, ibufs, ilen);
+
+	/* Initialize IV */
+	memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE);
+
+	/* Create encryption request */
+	sg_init_table(sg, 1);
+	sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0);
+	ablkcipher_request_set_crypt(req, sg, sg, ilen, iv);
+	res = crypto_ablkcipher_decrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+		if (res)
+			goto out;
+	}
+	/* Copy the result to output */
+	memcpy(oname->name, workbuf, ilen);
+	oname->len = ilen;
+	res = ilen;
+
+out:
+	kunmap(ctx->workpage);
+	ablkcipher_request_free(req);
+	if (res < 0) {
+		printk_ratelimited(
+		    KERN_ERR "%s: Error in ext4_fname_encrypt (error code %d)\n",
+		    __func__, res);
+	}
+	return res;
+}
+
+/**
+ * ext4_fname_encrypt() -
+ *
+ * This function encrypts the input filename, and returns the length of the
+ * ciphertext. Errors are returned as negative numbers.  We trust the caller to
+ * allocate sufficient memory to oname string.
+ */
+static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx,
+			      const struct qstr *iname,
+			      struct ext4_str *oname)
+{
+	struct ext4_str tmp_in[2], tmp_out[1];
+	u32 ciphertext_len, first_output_len, first_input_len,
+		num_reused_bytes, input_trailing_bytes, output_trailing_bytes;
+	int res;
+
+	if (iname->len <= 0 || iname->len > ctx->lim)
+		return -EIO;
+
+	ciphertext_len = ext4_fname_crypto_round_up(iname->len,
+		EXT4_CRYPTO_BLOCK_SIZE);
+	ciphertext_len = (ciphertext_len > ctx->lim)
+		? ctx->lim : ciphertext_len;
+
+	/* Figure out whether special handling in the last block is
+	 * needed because the output size is limited to non-integer multiple
+	 * of EXT4_CRYPTO_BLOCK_SIZE */
+	output_trailing_bytes = ciphertext_len & (EXT4_CRYPTO_BLOCK_SIZE-1);
+	first_output_len = ciphertext_len ^ output_trailing_bytes;
+	first_input_len = (first_output_len < iname->len)
+				? first_output_len : iname->len;
+	num_reused_bytes = EXT4_CRYPTO_BLOCK_SIZE - output_trailing_bytes;
+	input_trailing_bytes = iname->len - first_input_len;
+
+	tmp_in[0].name = (unsigned char *) iname->name;
+	tmp_in[0].len = first_input_len;
+	tmp_out[0].name = oname->name;
+
+	res = ext4_cbc_encrypt(ctx, tmp_out, tmp_in, first_input_len);
+	if (res < 0 || output_trailing_bytes == 0) {
+		oname->len = res;
+		return res;
+	}
+	BUG_ON(first_output_len != first_input_len);
+	BUG_ON(input_trailing_bytes <= 0 || output_trailing_bytes <= 0);
+	BUG_ON(input_trailing_bytes >= EXT4_CRYPTO_BLOCK_SIZE
+			|| output_trailing_bytes >= EXT4_CRYPTO_BLOCK_SIZE);
+
+	tmp_in[0].name = oname->name + first_output_len-num_reused_bytes;
+	tmp_in[0].len = num_reused_bytes;
+	tmp_in[1].name = (unsigned char *) iname->name + first_input_len;
+	tmp_in[1].len = input_trailing_bytes;
+	tmp_out[0].name = oname->name + first_output_len-num_reused_bytes;
+	res = ext4_cbc_encrypt(ctx, tmp_out, tmp_in,
+			       num_reused_bytes + input_trailing_bytes);
+	if (res < 0)
+		return res;
+
+	oname->len = ciphertext_len;
+	return ciphertext_len;
+}
+/*
+ * ext4_fname_decrypt()
+ *	This function decrypts the input filename, and returns
+ *	the length of the plaintext.
+ *	Errors are returned as negative numbers.
+ *	We trust the caller to allocate sufficient memory to oname string.
+ */
+static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx,
+			      const struct ext4_str *iname,
+			      struct ext4_str *oname)
+{
+	struct ext4_str tmp_in[2], tmp_out[1];
+	char *tmp_blk = ctx->tmp_buf;
+	u32 input_trailing_bytes, second_input_len, num_reused_bytes;
+	int res;
+
+	if (iname->len <= 0 || iname->len > ctx->lim)
+		return -EIO;
+
+	input_trailing_bytes = iname->len & (EXT4_CRYPTO_BLOCK_SIZE-1);
+	num_reused_bytes = EXT4_CRYPTO_BLOCK_SIZE - input_trailing_bytes;
+	second_input_len = iname->len ^ input_trailing_bytes;
+	/* We allow odd inputs only if we are running into limit */
+	if (input_trailing_bytes != 0 && iname->len != ctx->lim)
+		return -EIO;
+
+	if (input_trailing_bytes != 0) {
+		/* Perform the last-block dance */
+		tmp_in[0].name =
+			iname->name+iname->len - EXT4_CRYPTO_BLOCK_SIZE;
+		tmp_in[0].len = EXT4_CRYPTO_BLOCK_SIZE;
+		tmp_out[0].name = tmp_blk;
+		res = ext4_cbc_decrypt(ctx, tmp_out, tmp_in,
+				       EXT4_CRYPTO_BLOCK_SIZE);
+		if (res < 0)
+			return res;
+		tmp_in[0].name = iname->name;
+		tmp_in[0].len = iname->len-EXT4_CRYPTO_BLOCK_SIZE;
+		tmp_in[1].name = tmp_blk;
+		tmp_in[1].len = num_reused_bytes;
+	} else {
+		tmp_in[0].name = iname->name;
+		tmp_in[0].len = iname->len;
+	}
+
+	tmp_out[0].name = oname->name;
+
+	res = ext4_cbc_decrypt(ctx, tmp_out, tmp_in, second_input_len);
+	if (res < 0)
+		return res;
+	if (input_trailing_bytes != 0) {
+		/* Copy the trailing bytes */
+		memcpy(oname->name+second_input_len, tmp_blk+num_reused_bytes,
+				input_trailing_bytes);
+	}
+	oname->len = strnlen(oname->name, iname->len);
+	return oname->len;
+}
+
+/**
+ * ext4_fname_encode_digest() -
+ *
+ * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ */
+int ext4_fname_encode_digest(char *dst, char *src, u32 len)
+{
+	static const char *lookup_table =
+		"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+";
+	u32 current_chunk, num_chunks, i;
+	char tmp_buf[3];
+	u32 c0, c1, c2, c3;
+
+	current_chunk = 0;
+	num_chunks = len/3;
+	for (i = 0; i < num_chunks; i++) {
+		c0 = src[3*i] & 0x3f;
+		c1 = (((src[3*i]>>6)&0x3) | ((src[3*i+1] & 0xf)<<2)) & 0x3f;
+		c2 = (((src[3*i+1]>>4)&0xf) | ((src[3*i+2] & 0x3)<<4)) & 0x3f;
+		c3 = (src[3*i+2]>>2) & 0x3f;
+		dst[4*i] = lookup_table[c0];
+		dst[4*i+1] = lookup_table[c1];
+		dst[4*i+2] = lookup_table[c2];
+		dst[4*i+3] = lookup_table[c3];
+	}
+	if (i*3 < len) {
+		memset(tmp_buf, 0, 3);
+		memcpy(tmp_buf, &src[3*i], len-3*i);
+		c0 = tmp_buf[0] & 0x3f;
+		c1 = (((tmp_buf[0]>>6)&0x3) | ((tmp_buf[1] & 0xf)<<2)) & 0x3f;
+		c2 = (((tmp_buf[1]>>4)&0xf) | ((tmp_buf[2] & 0x3)<<4)) & 0x3f;
+		c3 = (tmp_buf[2]>>2) & 0x3f;
+		dst[4*i] = lookup_table[c0];
+		dst[4*i+1] = lookup_table[c1];
+		dst[4*i+2] = lookup_table[c2];
+		dst[4*i+3] = lookup_table[c3];
+	}
+	return 4*(i+1);
+}
+
+/**
+ * ext4_fname_hash() -
+ *
+ * This function computes the hash of the input filename, and sets the output
+ * buffer to the *encoded* digest.  It returns the length of the digest as its
+ * return value.  Errors are returned as negative numbers.  We trust the caller
+ * to allocate sufficient memory to oname string.
+ */
+static int ext4_fname_hash(struct ext4_fname_crypto_ctx *ctx,
+			   const struct ext4_str *iname,
+			   struct ext4_str *oname)
+{
+	struct scatterlist sg;
+	struct hash_desc desc = {
+		.tfm = (struct crypto_hash *)ctx->htfm,
+		.flags = CRYPTO_TFM_REQ_MAY_SLEEP
+	};
+	int res = 0;
+
+	if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) {
+		res = ext4_fname_encode_digest(oname->name, iname->name,
+					       iname->len);
+		oname->len = res;
+		return res;
+	}
+
+	sg_init_one(&sg, iname->name, iname->len);
+	res = crypto_hash_init(&desc);
+	if (res) {
+		printk(KERN_ERR
+		       "%s: Error initializing crypto hash; res = [%d]\n",
+		       __func__, res);
+		goto out;
+	}
+	res = crypto_hash_update(&desc, &sg, iname->len);
+	if (res) {
+		printk(KERN_ERR
+		       "%s: Error updating crypto hash; res = [%d]\n",
+		       __func__, res);
+		goto out;
+	}
+	res = crypto_hash_final(&desc,
+		&oname->name[EXT4_FNAME_CRYPTO_DIGEST_SIZE]);
+	if (res) {
+		printk(KERN_ERR
+		       "%s: Error finalizing crypto hash; res = [%d]\n",
+		       __func__, res);
+		goto out;
+	}
+	/* Encode the digest as a printable string--this will increase the
+	 * size of the digest */
+	oname->name[0] = 'I';
+	res = ext4_fname_encode_digest(oname->name+1,
+		&oname->name[EXT4_FNAME_CRYPTO_DIGEST_SIZE],
+		EXT4_FNAME_CRYPTO_DIGEST_SIZE) + 1;
+	oname->len = res;
+out:
+	return res;
+}
+
+/**
+ * ext4_free_fname_crypto_ctx() -
+ *
+ * Frees up a crypto context.
+ */
+void ext4_free_fname_crypto_ctx(struct ext4_fname_crypto_ctx *ctx)
+{
+	if (ctx == NULL || IS_ERR(ctx))
+		return;
+
+	if (ctx->ctfm && !IS_ERR(ctx->ctfm))
+		crypto_free_ablkcipher(ctx->ctfm);
+	if (ctx->htfm && !IS_ERR(ctx->htfm))
+		crypto_free_hash(ctx->htfm);
+	if (ctx->workpage && !IS_ERR(ctx->workpage))
+		__free_page(ctx->workpage);
+	kfree(ctx);
+}
+
+/**
+ * ext4_put_fname_crypto_ctx() -
+ *
+ * Return: The crypto context onto free list. If the free list is above a
+ * threshold, completely frees up the context, and returns the memory.
+ *
+ * TODO: Currently we directly free the crypto context. Eventually we should
+ * add code it to return to free list. Such an approach will increase
+ * efficiency of directory lookup.
+ */
+void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx)
+{
+	if (*ctx == NULL || IS_ERR(*ctx))
+		return;
+	ext4_free_fname_crypto_ctx(*ctx);
+	*ctx = NULL;
+}
+
+/**
+ * ext4_search_fname_crypto_ctx() -
+ */
+static struct ext4_fname_crypto_ctx *ext4_search_fname_crypto_ctx(
+		const struct ext4_encryption_key *key)
+{
+	return NULL;
+}
+
+/**
+ * ext4_alloc_fname_crypto_ctx() -
+ */
+struct ext4_fname_crypto_ctx *ext4_alloc_fname_crypto_ctx(
+	const struct ext4_encryption_key *key)
+{
+	struct ext4_fname_crypto_ctx *ctx;
+
+	ctx = kmalloc(sizeof(struct ext4_fname_crypto_ctx), GFP_NOFS);
+	if (ctx == NULL)
+		return ERR_PTR(-ENOMEM);
+	if (key->mode == EXT4_ENCRYPTION_MODE_INVALID) {
+		/* This will automatically set key mode to invalid
+		 * As enum for ENCRYPTION_MODE_INVALID is zero */
+		memset(&ctx->key, 0, sizeof(ctx->key));
+	} else {
+		memcpy(&ctx->key, key, sizeof(struct ext4_encryption_key));
+	}
+	ctx->has_valid_key = (EXT4_ENCRYPTION_MODE_INVALID == key->mode)
+		? 0 : 1;
+	ctx->ctfm_key_is_ready = 0;
+	ctx->ctfm = NULL;
+	ctx->htfm = NULL;
+	ctx->workpage = NULL;
+	return ctx;
+}
+
+/**
+ * ext4_get_fname_crypto_ctx() -
+ *
+ * Allocates a free crypto context and initializes it to hold
+ * the crypto material for the inode.
+ *
+ * Return: NULL if not encrypted. Error value on error. Valid pointer otherwise.
+ */
+struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(
+	struct inode *inode, u32 max_ciphertext_len)
+{
+	struct ext4_fname_crypto_ctx *ctx;
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	int res;
+
+	/* Check if the crypto policy is set on the inode */
+	res = ext4_encrypted_inode(inode);
+	if (res == 0)
+		return NULL;
+
+	if (!ext4_has_encryption_key(inode))
+		ext4_generate_encryption_key(inode);
+
+	/* Get a crypto context based on the key.
+	 * A new context is allocated if no context matches the requested key.
+	 */
+	ctx = ext4_search_fname_crypto_ctx(&(ei->i_encryption_key));
+	if (ctx == NULL)
+		ctx = ext4_alloc_fname_crypto_ctx(&(ei->i_encryption_key));
+	if (IS_ERR(ctx))
+		return ctx;
+
+	if (ctx->has_valid_key) {
+		/* As a first cut, we will allocate new tfm in every call.
+		 * later, we will keep the tfm around, in case the key gets
+		 * re-used */
+		if (ctx->ctfm == NULL) {
+			ctx->ctfm = crypto_alloc_ablkcipher("cbc(aes)",
+					0, 0);
+		}
+		if (IS_ERR(ctx->ctfm)) {
+			res = PTR_ERR(ctx->ctfm);
+			printk(
+			    KERN_DEBUG "%s: error (%d) allocating crypto tfm\n",
+			    __func__, res);
+			ctx->ctfm = NULL;
+			ext4_put_fname_crypto_ctx(&ctx);
+			return ERR_PTR(res);
+		}
+		if (ctx->ctfm == NULL) {
+			printk(
+			    KERN_DEBUG "%s: could not allocate crypto tfm\n",
+			    __func__);
+			ext4_put_fname_crypto_ctx(&ctx);
+			return ERR_PTR(-ENOMEM);
+		}
+		if (ctx->workpage == NULL)
+			ctx->workpage = alloc_page(GFP_NOFS);
+		if (IS_ERR(ctx->workpage)) {
+			res = PTR_ERR(ctx->workpage);
+			printk(
+			    KERN_DEBUG "%s: error (%d) allocating work page\n",
+			    __func__, res);
+			ctx->workpage = NULL;
+			ext4_put_fname_crypto_ctx(&ctx);
+			return ERR_PTR(res);
+		}
+		if (ctx->workpage == NULL) {
+			printk(
+			    KERN_DEBUG "%s: could not allocate work page\n",
+			    __func__);
+			ext4_put_fname_crypto_ctx(&ctx);
+			return ERR_PTR(-ENOMEM);
+		}
+		ctx->lim = max_ciphertext_len;
+		BUG_ON(ctx->key.mode != EXT4_ENCRYPTION_MODE_AES_256_CBC);
+		crypto_ablkcipher_clear_flags(ctx->ctfm, ~0);
+		crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctx->ctfm),
+			CRYPTO_TFM_REQ_WEAK_KEY);
+
+		/* If we are lucky, we will get a context that is already
+		 * set up with the right key. Else, we will have to
+		 * set the key */
+		if (!ctx->ctfm_key_is_ready) {
+			/* Since our crypto objectives for filename encryption
+			 * are pretty weak,
+			 * we directly use the inode master key */
+			res = crypto_ablkcipher_setkey(ctx->ctfm,
+					ctx->key.raw, ctx->key.size);
+			if (res) {
+				ext4_put_fname_crypto_ctx(&ctx);
+				return ERR_PTR(-EIO);
+			}
+			ctx->ctfm_key_is_ready = 1;
+		} else {
+			/* In the current implementation, key should never be
+			 * marked "ready" for a context that has just been
+			 * allocated. So we should never reach here */
+			 BUG();
+		}
+	}
+	if (ctx->htfm == NULL)
+		ctx->htfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(ctx->htfm)) {
+		res = PTR_ERR(ctx->htfm);
+		printk(KERN_DEBUG "%s: error (%d) allocating hash tfm\n",
+			__func__, res);
+		ctx->htfm = NULL;
+		ext4_put_fname_crypto_ctx(&ctx);
+		return ERR_PTR(res);
+	}
+	if (ctx->htfm == NULL) {
+		printk(KERN_DEBUG "%s: could not allocate hash tfm\n",
+				__func__);
+		ext4_put_fname_crypto_ctx(&ctx);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return ctx;
+}
+
+/**
+ * ext4_fname_crypto_round_up() -
+ *
+ * Return: The next multiple of block size
+ */
+u32 ext4_fname_crypto_round_up(u32 size, u32 blksize)
+{
+	return ((size+blksize-1)/blksize)*blksize;
+}
+
+/**
+ * ext4_fname_crypto_namelen_on_disk() -
+ */
+int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
+				      u32 namelen)
+{
+	u32 ciphertext_len;
+
+	if (ctx == NULL)
+		return -EIO;
+	if (!(ctx->has_valid_key))
+		return -EACCES;
+	ciphertext_len = ext4_fname_crypto_round_up(namelen,
+						    EXT4_CRYPTO_BLOCK_SIZE);
+	ciphertext_len = (ciphertext_len > ctx->lim)
+		? ctx->lim : ciphertext_len;
+	return (int) ciphertext_len;
+}
+
+/**
+ * ext4_fname_crypto_alloc_obuff() -
+ *
+ * Allocates an output buffer that is sufficient for the crypto operation
+ * specified by the context and the direction.
+ */
+int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
+				   unsigned char **obuf, u32 *olen, u32 ilen)
+{
+	if (!ctx)
+		return -EIO;
+	*olen = ext4_fname_crypto_round_up(ilen, EXT4_CRYPTO_BLOCK_SIZE);
+	if (*olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
+		*olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2;
+	/* Allocated buffer can hold one more character to null-terminate the
+	 * string */
+	*obuf = kmalloc(*olen, GFP_NOFS);
+	if (!(*obuf))
+		return -ENOMEM;
+	return 0;
+}
+
+/**
+ * ext4_fname_crypto_free_buffer() -
+ *
+ * Frees the buffer allocated for crypto operation.
+ * NOTE: This function is called from a loop defined in
+ * ext4_fname_crypto_free_buffer() macro.
+ */
+void ext4_fname_crypto_free_buffer(void **buf)
+{
+	if (*buf == NULL || IS_ERR(buf))
+		return;
+	kfree(*buf);
+	*buf = NULL;
+}
+
+/**
+ * ext4_fname_disk_to_usr() - converts a filename from disk space to user space
+ */
+int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+			   const struct ext4_str *iname,
+			   struct ext4_str *oname)
+{
+	if (ctx == NULL)
+		return -EIO;
+	if (iname->len < 3) {
+		/*Check for . and .. */
+		if (iname->name[0] == '.' && iname->name[iname->len-1] == '.') {
+			oname->name[0] = '.';
+			oname->name[iname->len-1] = '.';
+			oname->len = iname->len;
+			return oname->len;
+		}
+	}
+	if (ctx->has_valid_key)
+		return ext4_fname_decrypt(ctx, iname, oname);
+	else
+		return ext4_fname_hash(ctx, iname, oname);
+}
+
+int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+			   const struct ext4_dir_entry_2 * de,
+			   struct ext4_str *oname)
+{
+	struct ext4_str iname = {.name = (unsigned char *) de->name,
+				 .len = de->name_len };
+
+	return _ext4_fname_disk_to_usr(ctx, &iname, oname);
+}
+
+
+/**
+ * ext4_fname_usr_to_disk() - converts a filename from user space to disk space
+ */
+int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
+			   const struct qstr *iname,
+			   struct ext4_str *oname)
+{
+	int res;
+
+	if (ctx == NULL)
+		return -EIO;
+	if (iname->len < 3) {
+		/*Check for . and .. */
+		if (iname->name[0] == '.' &&
+				iname->name[iname->len-1] == '.') {
+			oname->name[0] = '.';
+			oname->name[iname->len-1] = '.';
+			oname->len = iname->len;
+			return oname->len;
+		}
+	}
+	if (ctx->has_valid_key) {
+		res = ext4_fname_encrypt(ctx, iname, oname);
+		return res;
+	}
+	/* Without a proper key, a user is not allowed to modify the filenames
+	 * in a directory. Consequently, a user space name cannot be mapped to
+	 * a disk-space name */
+	return -EACCES;
+}
+
+/*
+ * Calculate the htree hash from a filename from user space
+ */
+int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
+			    const struct qstr *iname,
+			    struct dx_hash_info *hinfo)
+{
+	struct ext4_str tmp, tmp2;
+	int ret = 0;
+
+	if (!ctx || !ctx->has_valid_key ||
+	    ((iname->name[0] == '.') &&
+	     ((iname->len == 1) ||
+	      ((iname->name[1] == '.') && (iname->len == 2))))) {
+		ext4fs_dirhash(iname->name, iname->len, hinfo);
+		return 0;
+	}
+
+	/* First encrypt the plaintext name */
+	ret = ext4_fname_crypto_alloc_buffer(ctx, &tmp.name,
+					     &tmp.len, iname->len);
+	if (ret < 0)
+		return ret;
+
+	ret = ext4_fname_encrypt(ctx, iname, &tmp);
+	if (ret < 0)
+		goto out;
+
+	tmp2.len = (4 * ((EXT4_FNAME_CRYPTO_DIGEST_SIZE + 2) / 3)) + 1;
+	tmp2.name = kmalloc(tmp2.len + 1, GFP_KERNEL);
+	if (tmp2.name == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = ext4_fname_hash(ctx, &tmp, &tmp2);
+	if (ret > 0)
+		ext4fs_dirhash(tmp2.name, tmp2.len, hinfo);
+	ext4_fname_crypto_free_buffer((void **)&tmp2.name);
+out:
+	ext4_fname_crypto_free_buffer((void **)&tmp.name);
+	return ret;
+}
+
+/**
+ * ext4_fname_disk_to_htree() - converts a filename from disk space to htree-access string
+ */
+int ext4_fname_disk_to_hash(struct ext4_fname_crypto_ctx *ctx,
+			    const struct ext4_dir_entry_2 * de,
+			    struct dx_hash_info *hinfo)
+{
+	struct ext4_str iname = {.name = (unsigned char *) de->name,
+				 .len = de->name_len};
+	struct ext4_str tmp;
+	int ret;
+
+	if (!ctx || !ctx->has_valid_key ||
+	    ((iname.name[0] == '.') &&
+	     ((iname.len == 1) ||
+	      ((iname.name[1] == '.') && (iname.len == 2))))) {
+		ext4fs_dirhash(iname.name, iname.len, hinfo);
+		return 0;
+	}
+
+	tmp.len = (4 * ((EXT4_FNAME_CRYPTO_DIGEST_SIZE + 2) / 3)) + 1;
+	tmp.name = kmalloc(tmp.len + 1, GFP_KERNEL);
+	if (tmp.name == NULL)
+		return -ENOMEM;
+
+	ret = ext4_fname_hash(ctx, &iname, &tmp);
+	if (ret > 0)
+		ext4fs_dirhash(tmp.name, tmp.len, hinfo);
+	ext4_fname_crypto_free_buffer((void **)&tmp.name);
+	return ret;
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index ecb7be83..fe1a807 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2049,6 +2049,47 @@ static inline int ext4_sb_has_crypto(struct super_block *sb)
 }
 #endif
 
+/* crypto_fname.c */
+u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
+int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
+				   unsigned char **obuf,
+				   u32 *olen, u32 ilen);
+int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+			    const struct ext4_str *iname,
+			    struct ext4_str *oname);
+int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+			   const struct ext4_dir_entry_2 * de,
+			   struct ext4_str *oname);
+int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
+			   const struct qstr *iname,
+			   struct ext4_str *oname);
+int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
+			   const struct qstr *iname,
+			   struct dx_hash_info *hinfo);
+int ext4_fname_disk_to_hash(struct ext4_fname_crypto_ctx *ctx,
+			    const struct ext4_dir_entry_2 * de,
+			    struct dx_hash_info *hinfo);
+int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
+				      u32 namelen);
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
+struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
+							u32 max_len);
+void ext4_fname_crypto_free_buffer(void **buf);
+#else
+static inline
+void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { }
+static inline
+struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
+							u32 max_len)
+{
+	return NULL;
+}
+static inline void ext4_fname_crypto_free_buffer(void **buf) { }
+#endif
+
+
 /* crypto_key.c */
 int ext4_generate_encryption_key(struct inode *inode);
 
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index 3bff49c..68e95d8 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -97,4 +97,24 @@ static inline int ext4_encryption_key_size(int mode)
 	return 0;
 }
 
+#define EXT4_FNAME_NUM_SCATTER_ENTRIES	4
+#define EXT4_CRYPTO_BLOCK_SIZE		16
+#define EXT4_FNAME_CRYPTO_DIGEST_SIZE	32
+
+struct ext4_str {
+	unsigned char *name;
+	u32 len;
+};
+
+struct ext4_fname_crypto_ctx {
+	u32 lim;
+	char tmp_buf[EXT4_CRYPTO_BLOCK_SIZE];
+	struct crypto_ablkcipher *ctfm;
+	struct crypto_hash *htfm;
+	struct page *workpage;
+	struct ext4_encryption_key key;
+	unsigned has_valid_key : 1;
+	unsigned ctfm_key_is_ready : 1;
+};
+
 #endif	/* _EXT4_CRYPTO_H */
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 15/22] ext4: teach ext4_htree_store_dirent() to store decrypted filenames
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (13 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 14/22] ext4 crypto: filename encryption facilities Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 16/22] ext4 crypto: insert encrypted filenames into a leaf directory block Theodore Ts'o
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

For encrypted directories, we need to pass in a separate parameter for
the decrypted filename, since the directory entry contains the
encrypted filename.

Change-Id: I2d1a78e65cb81152cae108ce7faadc28b52e0abe
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/dir.c    | 15 ++++++++++-----
 fs/ext4/ext4.h   |  5 +++--
 fs/ext4/inline.c |  7 +++++--
 fs/ext4/namei.c  | 21 +++++++++++++++++----
 4 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index c24143e..f67f955 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -384,10 +384,15 @@ void ext4_htree_free_dir_info(struct dir_private_info *p)
 
 /*
  * Given a directory entry, enter it into the fname rb tree.
+ *
+ * When filename encryption is enabled, the dirent will hold the
+ * encrypted filename, while the htree will hold decrypted filename.
+ * The decrypted filename is passed in via ent_name.  parameter.
  */
 int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
 			     __u32 minor_hash,
-			     struct ext4_dir_entry_2 *dirent)
+			    struct ext4_dir_entry_2 *dirent,
+			    struct ext4_str *ent_name)
 {
 	struct rb_node **p, *parent = NULL;
 	struct fname *fname, *new_fn;
@@ -398,17 +403,17 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
 	p = &info->root.rb_node;
 
 	/* Create and allocate the fname structure */
-	len = sizeof(struct fname) + dirent->name_len + 1;
+	len = sizeof(struct fname) + ent_name->len + 1;
 	new_fn = kzalloc(len, GFP_KERNEL);
 	if (!new_fn)
 		return -ENOMEM;
 	new_fn->hash = hash;
 	new_fn->minor_hash = minor_hash;
 	new_fn->inode = le32_to_cpu(dirent->inode);
-	new_fn->name_len = dirent->name_len;
+	new_fn->name_len = ent_name->len;
 	new_fn->file_type = dirent->file_type;
-	memcpy(new_fn->name, dirent->name, dirent->name_len);
-	new_fn->name[dirent->name_len] = 0;
+	memcpy(new_fn->name, ent_name->name, ent_name->len);
+	new_fn->name[ent_name->len] = 0;
 
 	while (*p) {
 		parent = *p;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index fe1a807..576321c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2113,8 +2113,9 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
 	unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
 					(de), (bh), (buf), (size), (offset)))
 extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
-				    __u32 minor_hash,
-				    struct ext4_dir_entry_2 *dirent);
+				__u32 minor_hash,
+				struct ext4_dir_entry_2 *dirent,
+				struct ext4_str *ent_name);
 extern void ext4_htree_free_dir_info(struct dir_private_info *p);
 extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 			     struct buffer_head *bh,
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 4b143fe..056ef06 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1327,6 +1327,7 @@ int htree_inlinedir_to_tree(struct file *dir_file,
 	struct ext4_iloc iloc;
 	void *dir_buf = NULL;
 	struct ext4_dir_entry_2 fake;
+	struct ext4_str tmp_str;
 
 	ret = ext4_get_inode_loc(inode, &iloc);
 	if (ret)
@@ -1398,8 +1399,10 @@ int htree_inlinedir_to_tree(struct file *dir_file,
 			continue;
 		if (de->inode == 0)
 			continue;
-		err = ext4_htree_store_dirent(dir_file,
-				   hinfo->hash, hinfo->minor_hash, de);
+		tmp_str.name = de->name;
+		tmp_str.len = de->name_len;
+		err = ext4_htree_store_dirent(dir_file, hinfo->hash,
+					      hinfo->minor_hash, de, &tmp_str);
 		if (err) {
 			count = err;
 			goto out;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 262aa1c..57cae22 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -880,6 +880,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 	struct buffer_head *bh;
 	struct ext4_dir_entry_2 *de, *top;
 	int err = 0, count = 0;
+	struct ext4_str tmp_str;
 
 	dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
 							(unsigned long)block));
@@ -906,8 +907,11 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 			continue;
 		if (de->inode == 0)
 			continue;
-		if ((err = ext4_htree_store_dirent(dir_file,
-				   hinfo->hash, hinfo->minor_hash, de)) != 0) {
+		tmp_str.name = de->name;
+		tmp_str.len = de->name_len;
+		err = ext4_htree_store_dirent(dir_file,
+			   hinfo->hash, hinfo->minor_hash, de, &tmp_str);
+		if (err != 0) {
 			brelse(bh);
 			return err;
 		}
@@ -937,6 +941,7 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
 	int count = 0;
 	int ret, err;
 	__u32 hashval;
+	struct ext4_str tmp_str;
 
 	dxtrace(printk(KERN_DEBUG "In htree_fill_tree, start hash: %x:%x\n",
 		       start_hash, start_minor_hash));
@@ -972,14 +977,22 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
 	/* Add '.' and '..' from the htree header */
 	if (!start_hash && !start_minor_hash) {
 		de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data;
-		if ((err = ext4_htree_store_dirent(dir_file, 0, 0, de)) != 0)
+		tmp_str.name = de->name;
+		tmp_str.len = de->name_len;
+		err = ext4_htree_store_dirent(dir_file, 0, 0,
+					      de, &tmp_str);
+		if (err != 0)
 			goto errout;
 		count++;
 	}
 	if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) {
 		de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data;
 		de = ext4_next_entry(de, dir->i_sb->s_blocksize);
-		if ((err = ext4_htree_store_dirent(dir_file, 2, 0, de)) != 0)
+		tmp_str.name = de->name;
+		tmp_str.len = de->name_len;
+		err = ext4_htree_store_dirent(dir_file, 2, 0,
+					      de, &tmp_str);
+		if (err != 0)
 			goto errout;
 		count++;
 	}
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 16/22] ext4 crypto: insert encrypted filenames into a leaf directory block
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (14 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 15/22] ext4: teach ext4_htree_store_dirent() to store decrypted filenames Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto Theodore Ts'o
                   ` (7 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Uday Savagaonkar, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: Iea5da045383d41e3912eed7e63292096c24668e4
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h   |   4 ++-
 fs/ext4/inline.c |  10 ++++--
 fs/ext4/namei.c  | 104 +++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 105 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 576321c..421c065 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2122,9 +2122,11 @@ extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 			     void *buf, int buf_size,
 			     const char *name, int namelen,
 			     struct ext4_dir_entry_2 **dest_de);
-void ext4_insert_dentry(struct inode *inode,
+int ext4_insert_dentry(struct inode *dir,
+			struct inode *inode,
 			struct ext4_dir_entry_2 *de,
 			int buf_size,
+		       const struct qstr *iname,
 			const char *name, int namelen);
 static inline void ext4_update_dx_flag(struct inode *inode)
 {
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 056ef06..5184111 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -11,11 +11,16 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#include <linux/fiemap.h>
+
 #include "ext4_jbd2.h"
 #include "ext4.h"
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+#include "ext4_crypto.h"
+#endif
 #include "xattr.h"
 #include "truncate.h"
-#include <linux/fiemap.h>
 
 #define EXT4_XATTR_SYSTEM_DATA	"data"
 #define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -1014,7 +1019,8 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
 	err = ext4_journal_get_write_access(handle, iloc->bh);
 	if (err)
 		return err;
-	ext4_insert_dentry(inode, de, inline_size, name, namelen);
+	ext4_insert_dentry(dir, inode, de, inline_size, &dentry->d_name,
+			   name, namelen);
 
 	ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
 
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 57cae22..cbedeb0 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1663,19 +1663,51 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 	return 0;
 }
 
-void ext4_insert_dentry(struct inode *inode,
-			struct ext4_dir_entry_2 *de,
-			int buf_size,
-			const char *name, int namelen)
+int ext4_insert_dentry(struct inode *dir,
+		       struct inode *inode,
+		       struct ext4_dir_entry_2 *de,
+		       int buf_size,
+		       const struct qstr *iname,
+		       const char *name, int namelen)
 {
 
 	int nlen, rlen;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+	struct ext4_str tmp_str;
+	int res;
+
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx))
+		return -EIO;
+	/* By default, the input name would be written to the disk */
+	tmp_str.name = (unsigned char *)name;
+	tmp_str.len = namelen;
+	if (ctx != NULL) {
+		/* Directory is encrypted */
+		res = ext4_fname_crypto_alloc_buffer(ctx,
+			&fname_crypto_str.name, &fname_crypto_str.len,
+			EXT4_NAME_LEN);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			return -ENOMEM;
+		}
+		res = ext4_fname_usr_to_disk(ctx, iname, &fname_crypto_str);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			ext4_fname_crypto_free_buffer(
+			    (void **)&fname_crypto_str.name);
+			return res;
+		}
+		tmp_str.name = fname_crypto_str.name;
+		tmp_str.len = fname_crypto_str.len;
+	}
 
 	nlen = EXT4_DIR_REC_LEN(de->name_len);
 	rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
 	if (de->inode) {
 		struct ext4_dir_entry_2 *de1 =
-				(struct ext4_dir_entry_2 *)((char *)de + nlen);
+			(struct ext4_dir_entry_2 *)((char *)de + nlen);
 		de1->rec_len = ext4_rec_len_to_disk(rlen - nlen, buf_size);
 		de->rec_len = ext4_rec_len_to_disk(nlen, buf_size);
 		de = de1;
@@ -1683,9 +1715,14 @@ void ext4_insert_dentry(struct inode *inode,
 	de->file_type = EXT4_FT_UNKNOWN;
 	de->inode = cpu_to_le32(inode->i_ino);
 	ext4_set_de_type(inode->i_sb, de, inode->i_mode);
-	de->name_len = namelen;
-	memcpy(de->name, name, namelen);
+	de->name_len = tmp_str.len;
+
+	memcpy(de->name, tmp_str.name, tmp_str.len);
+	ext4_put_fname_crypto_ctx(&ctx);
+	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+	return 0;
 }
+
 /*
  * Add a new entry into a directory (leaf) block.  If de is non-NULL,
  * it points to a directory entry which is guaranteed to be large
@@ -1722,8 +1759,12 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
 		return err;
 	}
 
-	/* By now the buffer is marked for journaling */
-	ext4_insert_dentry(inode, de, blocksize, name, namelen);
+	/* By now the buffer is marked for journaling. Due to crypto operations,
+	 * the following function call may fail */
+	err = ext4_insert_dentry(dir, inode, de, blocksize, &dentry->d_name,
+				 name, namelen);
+	if (err < 0)
+		return err;
 
 	/*
 	 * XXX shouldn't update any times until successful
@@ -1770,7 +1811,26 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 	struct dx_hash_info hinfo;
 	ext4_lblk_t  block;
 	struct fake_dirent *fde;
-	int		csum_size = 0;
+	int csum_size = 0;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+	int res;
+
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx))
+		return -1;
+	if (ctx != NULL) {
+		/* Allocate buffer to hold maximum name length */
+		res = ext4_fname_crypto_alloc_buffer(ctx,
+			&fname_crypto_str.name, &fname_crypto_str.len,
+			EXT4_NAME_LEN);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			return -1;
+		}
+	}
+#endif
 
 	if (ext4_has_metadata_csum(inode->i_sb))
 		csum_size = sizeof(struct ext4_dir_entry_tail);
@@ -1837,7 +1897,31 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 	if (hinfo.hash_version <= DX_HASH_TEA)
 		hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
 	hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ctx == NULL) {
+		/* Directory is not encrypted */
+		ext4fs_dirhash(name, namelen, &hinfo);
+	} else {
+		/* Directory is encrypted */
+		res = ext4_fname_usr_to_htree(ctx, &dentry->d_name,
+					      &fname_crypto_str);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			ext4_fname_crypto_free_buffer(
+			    (void **)&fname_crypto_str.name);
+			ext4_mark_inode_dirty(handle, dir);
+			brelse(bh);
+			return res;
+		}
+		ext4fs_dirhash(fname_crypto_str.name,
+			       fname_crypto_str.len,
+			       &hinfo);
+		ext4_put_fname_crypto_ctx(&ctx);
+		ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+	}
+#else
 	ext4fs_dirhash(name, namelen, &hinfo);
+#endif
 	memset(frames, 0, sizeof(frames));
 	frame = frames;
 	frame->entries = entries;
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (15 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 16/22] ext4 crypto: insert encrypted filenames into a leaf directory block Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-08 17:44   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 18/22] ext4 crypto: filename encryption modifications Theodore Ts'o
                   ` (6 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Uday Savagaonkar, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Modifies dx_show_leaf and dx_probe to support fname encryption.
Filename encryption not yet enabled.

Change-Id: I2058ea5cf6c3920a05c75e42acb2baab631fa1e8
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/namei.c | 161 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 122 insertions(+), 39 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index cbedeb0..4fccb76 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -588,8 +588,10 @@ struct stats
 	unsigned bcount;
 };
 
-static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_entry_2 *de,
-				 int size, int show_names)
+static struct stats dx_show_leaf(struct inode *dir,
+				struct dx_hash_info *hinfo,
+				struct ext4_dir_entry_2 *de,
+				int size, int show_names)
 {
 	unsigned names = 0, space = 0;
 	char *base = (char *) de;
@@ -602,12 +604,85 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent
 		{
 			if (show_names)
 			{
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+				int len;
+				char *name;
+				struct ext4_str fname_crypto_str
+					= {.name = NULL, .len = 0};
+				struct ext4_fname_crypto_ctx *ctx = NULL;
+				int res;
+
+				name  = de->name;
+				len = de->name_len;
+				ctx = ext4_get_fname_crypto_ctx(dir,
+								EXT4_NAME_LEN);
+				if (IS_ERR(ctx)) {
+					printk(KERN_WARNING "Error acquiring"
+					" crypto ctxt--skipping crypto\n");
+					ctx = NULL;
+				}
+				if (ctx == NULL) {
+					/* Directory is not encrypted */
+					while (len--)
+						printk("%c", *name++);
+					ext4fs_dirhash(de->name,
+						de->name_len, &h);
+					printk(":(U)%x.%u ", h.hash,
+						(unsigned) ((char *) de
+							- base));
+				} else {
+					/* Directory is encrypted */
+					res = ext4_fname_crypto_alloc_buffer(
+						ctx, &fname_crypto_str.name,
+						&fname_crypto_str.len,
+						de->name_len);
+					if (res < 0) {
+						printk(KERN_WARNING "Error "
+							"allocating crypto "
+							"buffer--skipping "
+							"crypto\n");
+						ext4_put_fname_crypto_ctx(&ctx);
+						ctx = NULL;
+					}
+					res = ext4_fname_disk_to_usr(ctx, de,
+							&fname_crypto_str);
+					if (res < 0) {
+						printk(KERN_WARNING "Error "
+							"converting filename "
+							"from disk to usr"
+							"\n");
+						name = "??";
+						len = 2;
+					} else {
+						name = fname_crypto_str.name;
+						len = fname_crypto_str.len;
+					}
+					while (len--)
+						printk("%c", *name++);
+					res = ext4_fname_disk_to_hash(ctx, de,
+								      &h);
+					if (res < 0) {
+						printk(KERN_WARNING "Error "
+							"converting filename "
+							"from disk to htree"
+							"\n");
+						h.hash = 0xDEADBEEF;
+					}
+					printk(":(E)%x.%u ", h.hash,
+						(unsigned) ((char *) de
+						- base));
+					ext4_put_fname_crypto_ctx(&ctx);
+					ext4_fname_crypto_free_buffer(
+						(void **)&fname_crypto_str.name);
+				}
+#else
 				int len = de->name_len;
 				char *name = de->name;
 				while (len--) printk("%c", *name++);
 				ext4fs_dirhash(de->name, de->name_len, &h);
 				printk(":%x.%u ", h.hash,
 				       (unsigned) ((char *) de - base));
+#endif
 			}
 			space += EXT4_DIR_REC_LEN(de->name_len);
 			names++;
@@ -639,7 +714,8 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
 			continue;
 		stats = levels?
 		   dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
-		   dx_show_leaf(hinfo, (struct ext4_dir_entry_2 *) bh->b_data, blocksize, 0);
+		   dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *)
+			bh->b_data, blocksize, 0);
 		names += stats.names;
 		space += stats.space;
 		bcount += stats.bcount;
@@ -672,6 +748,10 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
 	struct dx_frame *frame = frame_in;
 	struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR);
 	u32 hash;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	int res;
+#endif
 
 	frame->bh = ext4_read_dirblock(dir, 0, INDEX);
 	if (IS_ERR(frame->bh))
@@ -689,8 +769,25 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
 	if (hinfo->hash_version <= DX_HASH_TEA)
 		hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
 	hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (d_name) {
+		/* Check if the directory is encrypted */
+		ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+		if (IS_ERR(ctx)) {
+			ret_err = ERR_PTR(PTR_ERR(ctx));
+			goto fail;
+		}
+		res = ext4_fname_usr_to_hash(ctx, d_name, hinfo);
+		if (res < 0) {
+			ret_err = ERR_PTR(res);
+			goto fail;
+		}
+	}
+#else
 	if (d_name)
 		ext4fs_dirhash(d_name->name, d_name->len, hinfo);
+#endif
+
 	hash = hinfo->hash;
 
 	if (root->info.unused_flags & 1) {
@@ -775,6 +872,11 @@ fail:
 		brelse(frame->bh);
 		frame--;
 	}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	/* Free up the memory allocated for EXT4 crypto */
+	ext4_put_fname_crypto_ctx(&ctx);
+#endif
+
 	if (ret_err == ERR_PTR(ERR_BAD_DX_DIR))
 		ext4_warning(dir->i_sb,
 			     "Corrupt dir inode %lu, running e2fsck is "
@@ -1602,8 +1704,10 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
 		initialize_dirent_tail(t, blocksize);
 	}
 
-	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));
-	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1));
+	dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data1,
+			blocksize, 1));
+	dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data2,
+			blocksize, 1));
 
 	/* Which block gets the new entry? */
 	if (hinfo->hash >= hash2) {
@@ -1796,8 +1900,13 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 			    struct inode *inode, struct buffer_head *bh)
 {
 	struct inode	*dir = dentry->d_parent->d_inode;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	int res;
+#else
 	const char	*name = dentry->d_name.name;
 	int		namelen = dentry->d_name.len;
+#endif
 	struct buffer_head *bh2;
 	struct dx_root	*root;
 	struct dx_frame	frames[2], *frame;
@@ -1812,24 +1921,11 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 	ext4_lblk_t  block;
 	struct fake_dirent *fde;
 	int csum_size = 0;
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
-	struct ext4_fname_crypto_ctx *ctx = NULL;
-	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
-	int res;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
 	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
 	if (IS_ERR(ctx))
-		return -1;
-	if (ctx != NULL) {
-		/* Allocate buffer to hold maximum name length */
-		res = ext4_fname_crypto_alloc_buffer(ctx,
-			&fname_crypto_str.name, &fname_crypto_str.len,
-			EXT4_NAME_LEN);
-		if (res < 0) {
-			ext4_put_fname_crypto_ctx(&ctx);
-			return -1;
-		}
-	}
+		return PTR_ERR(ctx);
 #endif
 
 	if (ext4_has_metadata_csum(inode->i_sb))
@@ -1898,27 +1994,14 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
 		hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
 	hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
-	if (ctx == NULL) {
-		/* Directory is not encrypted */
-		ext4fs_dirhash(name, namelen, &hinfo);
-	} else {
-		/* Directory is encrypted */
-		res = ext4_fname_usr_to_htree(ctx, &dentry->d_name,
-					      &fname_crypto_str);
-		if (res < 0) {
-			ext4_put_fname_crypto_ctx(&ctx);
-			ext4_fname_crypto_free_buffer(
-			    (void **)&fname_crypto_str.name);
-			ext4_mark_inode_dirty(handle, dir);
-			brelse(bh);
-			return res;
-		}
-		ext4fs_dirhash(fname_crypto_str.name,
-			       fname_crypto_str.len,
-			       &hinfo);
+	res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo);
+	if (res < 0) {
 		ext4_put_fname_crypto_ctx(&ctx);
-		ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+		ext4_mark_inode_dirty(handle, dir);
+		brelse(bh);
+		return res;
 	}
+	ext4_put_fname_crypto_ctx(&ctx);
 #else
 	ext4fs_dirhash(name, namelen, &hinfo);
 #endif
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 18/22] ext4 crypto: filename encryption modifications
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (16 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 19/22] ext4 crypto: enable filename encryption Theodore Ts'o
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Uday Savagaonkar, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Modifies htree_dirblock_to_tree, dx_make_map, ext4_match search_dir,
and ext4_find_dest_de to support fname crypto.  Filename encryption
feature is not yet enabled at this patch.

Change-Id: I0170e0dac991a831f66a638a3bb5340d8dc775ae
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/namei.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 209 insertions(+), 44 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 4fccb76..80a3979 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -256,8 +256,9 @@ static struct dx_frame *dx_probe(const struct qstr *d_name,
 				 struct dx_hash_info *hinfo,
 				 struct dx_frame *frame);
 static void dx_release(struct dx_frame *frames);
-static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize,
-		       struct dx_hash_info *hinfo, struct dx_map_entry map[]);
+static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
+		       unsigned blocksize, struct dx_hash_info *hinfo,
+		       struct dx_map_entry map[]);
 static void dx_sort_map(struct dx_map_entry *map, unsigned count);
 static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
 		struct dx_map_entry *offsets, int count, unsigned blocksize);
@@ -982,7 +983,8 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 	struct buffer_head *bh;
 	struct ext4_dir_entry_2 *de, *top;
 	int err = 0, count = 0;
-	struct ext4_str tmp_str;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}, tmp_str;
 
 	dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
 							(unsigned long)block));
@@ -994,6 +996,25 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 	top = (struct ext4_dir_entry_2 *) ((char *) de +
 					   dir->i_sb->s_blocksize -
 					   EXT4_DIR_REC_LEN(0));
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	/* Check if the directory is encrypted */
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx)) {
+		err = PTR_ERR(ctx);
+		brelse(bh);
+		return err;
+	}
+	if (ctx != NULL) {
+		err = ext4_fname_crypto_alloc_buffer(ctx,
+			&fname_crypto_str.name, &fname_crypto_str.len,
+			EXT4_NAME_LEN);
+		if (err < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			brelse(bh);
+			return err;
+		}
+	}
+#endif
 	for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
 				bh->b_data, bh->b_size,
@@ -1002,24 +1023,52 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 			/* silently ignore the rest of the block */
 			break;
 		}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		err = ext4_fname_disk_to_hash(ctx, de, hinfo);
+		if (err < 0) {
+			count = err;
+			goto errout;
+		}
+#else
 		ext4fs_dirhash(de->name, de->name_len, hinfo);
+#endif
 		if ((hinfo->hash < start_hash) ||
 		    ((hinfo->hash == start_hash) &&
 		     (hinfo->minor_hash < start_minor_hash)))
 			continue;
 		if (de->inode == 0)
 			continue;
-		tmp_str.name = de->name;
-		tmp_str.len = de->name_len;
-		err = ext4_htree_store_dirent(dir_file,
-			   hinfo->hash, hinfo->minor_hash, de, &tmp_str);
+		if (ctx == NULL) {
+			/* Directory is not encrypted */
+			tmp_str.name = de->name;
+			tmp_str.len = de->name_len;
+			err = ext4_htree_store_dirent(dir_file,
+				   hinfo->hash, hinfo->minor_hash, de,
+				   &tmp_str);
+		} else {
+			/* Directory is encrypted */
+			err = ext4_fname_disk_to_usr(ctx, de,
+						     &fname_crypto_str);
+			if (err < 0) {
+				count = err;
+				goto errout;
+			}
+			err = ext4_htree_store_dirent(dir_file,
+				   hinfo->hash, hinfo->minor_hash, de,
+					&fname_crypto_str);
+		}
 		if (err != 0) {
-			brelse(bh);
-			return err;
+			count = err;
+			goto errout;
 		}
 		count++;
 	}
+errout:
 	brelse(bh);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	ext4_put_fname_crypto_ctx(&ctx);
+	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+#endif
 	return count;
 }
 
@@ -1152,17 +1201,33 @@ static inline int search_dirblock(struct buffer_head *bh,
  * Create map of hash values, offsets, and sizes, stored at end of block.
  * Returns number of entries mapped.
  */
-static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize,
-		       struct dx_hash_info *hinfo,
+static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
+		       unsigned blocksize, struct dx_hash_info *hinfo,
 		       struct dx_map_entry *map_tail)
 {
 	int count = 0;
 	char *base = (char *) de;
 	struct dx_hash_info h = *hinfo;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	int err;
+
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+#endif
 
 	while ((char *) de < base + blocksize) {
 		if (de->name_len && de->inode) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+			err = ext4_fname_disk_to_hash(ctx, de, &h);
+			if (err < 0) {
+				ext4_put_fname_crypto_ctx(&ctx);
+				return err;
+			}
+#else
 			ext4fs_dirhash(de->name, de->name_len, &h);
+#endif
 			map_tail--;
 			map_tail->hash = h.hash;
 			map_tail->offs = ((char *) de - base)>>2;
@@ -1173,6 +1238,9 @@ static int dx_make_map(struct ext4_dir_entry_2 *de, unsigned blocksize,
 		/* XXX: do we need to check rec_len == 0 case? -Chris */
 		de = ext4_next_entry(de, blocksize);
 	}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	ext4_put_fname_crypto_ctx(&ctx);
+#endif
 	return count;
 }
 
@@ -1223,57 +1291,109 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
  * `len <= EXT4_NAME_LEN' is guaranteed by caller.
  * `de != NULL' is guaranteed by caller.
  */
-static inline int ext4_match (int len, const char * const name,
-			      struct ext4_dir_entry_2 * de)
+static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx,
+			     struct ext4_str *fname_crypto_str,
+			     int len, const char * const name,
+			     struct ext4_dir_entry_2 *de)
 {
-	if (len != de->name_len)
-		return 0;
+	int res;
+
 	if (!de->inode)
 		return 0;
-	return !memcmp(name, de->name, len);
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if (ctx) {
+		/* Directory is encrypted */
+		res = ext4_fname_disk_to_usr(ctx, de, fname_crypto_str);
+		if (res < 0)
+			return res;
+		if (len != res)
+			return 0;
+		res = memcmp(name, fname_crypto_str->name, len);
+		return (res == 0) ? 1 : 0;
+	}
+#endif
+	if (len != de->name_len)
+		return 0;
+	res = memcmp(name, de->name, len);
+	return (res == 0) ? 1 : 0;
 }
 
 /*
  * Returns 0 if not found, -1 on failure, and 1 on success
  */
-int search_dir(struct buffer_head *bh,
-	       char *search_buf,
-	       int buf_size,
-	       struct inode *dir,
-	       const struct qstr *d_name,
-	       unsigned int offset,
-	       struct ext4_dir_entry_2 **res_dir)
+int search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
+	       struct inode *dir, const struct qstr *d_name,
+	       unsigned int offset, struct ext4_dir_entry_2 **res_dir)
 {
 	struct ext4_dir_entry_2 * de;
 	char * dlimit;
 	int de_len;
 	const char *name = d_name->name;
 	int namelen = d_name->len;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+	int res;
+
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx))
+		return -1;
+
+	if (ctx != NULL) {
+		/* Allocate buffer to hold maximum name length */
+		res = ext4_fname_crypto_alloc_buffer(ctx,
+				&fname_crypto_str.name,
+				&fname_crypto_str.len,
+				EXT4_NAME_LEN);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			return -1;
+		}
+	}
 
 	de = (struct ext4_dir_entry_2 *)search_buf;
 	dlimit = search_buf + buf_size;
 	while ((char *) de < dlimit) {
 		/* this code is executed quadratically often */
 		/* do minimal checking `by hand' */
+		if ((char *) de + de->name_len <= dlimit) {
+			res = ext4_match(ctx, &fname_crypto_str, namelen,
+					 name, de);
+			if (res < 0) {
+				res = -1;
+				goto return_result;
+			}
+			if (res > 0) {
+				/* found a match - just to be sure, do
+				 * a full check */
+				if (ext4_check_dir_entry(dir, NULL, de, bh,
+						bh->b_data,
+						 bh->b_size, offset)) {
+					res = -1;
+					goto return_result;
+				}
+				*res_dir = de;
+				res = 1;
+				goto return_result;
+			}
 
-		if ((char *) de + namelen <= dlimit &&
-		    ext4_match (namelen, name, de)) {
-			/* found a match - just to be sure, do a full check */
-			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
-						 bh->b_size, offset))
-				return -1;
-			*res_dir = de;
-			return 1;
 		}
 		/* prevent looping on a bad block */
 		de_len = ext4_rec_len_from_disk(de->rec_len,
 						dir->i_sb->s_blocksize);
-		if (de_len <= 0)
-			return -1;
+		if (de_len <= 0) {
+			res = -1;
+			goto return_result;
+		}
 		offset += de_len;
 		de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
 	}
-	return 0;
+
+	res = 0;
+return_result:
+	ext4_put_fname_crypto_ctx(&ctx);
+	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+	return res;
 }
 
 static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
@@ -1462,6 +1582,9 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
 	ext4_lblk_t block;
 	int retval;
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	*res_dir = NULL;
+#endif
 	frame = dx_probe(d_name, dir, &hinfo, frames);
 	if (IS_ERR(frame))
 		return (struct buffer_head *) frame;
@@ -1665,7 +1788,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
 
 	/* create map in the end of data2 block */
 	map = (struct dx_map_entry *) (data2 + blocksize);
-	count = dx_make_map((struct ext4_dir_entry_2 *) data1,
+	count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1,
 			     blocksize, hinfo, map);
 	map -= count;
 	dx_sort_map(map, count);
@@ -1688,7 +1811,8 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
 					hash2, split, count-split));
 
 	/* Fancy dance to stay within two buffers */
-	de2 = dx_move_dirents(data1, data2, map + split, count - split, blocksize);
+	de2 = dx_move_dirents(data1, data2, map + split, count - split,
+			      blocksize);
 	de = dx_pack_dirents(data1, blocksize);
 	de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) -
 					   (char *) de,
@@ -1744,15 +1868,50 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 	int nlen, rlen;
 	unsigned int offset = 0;
 	char *top;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+	int res;
+
+	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
+	if (IS_ERR(ctx))
+		return -1;
+
+	if (ctx != NULL) {
+		/* Calculate record length needed to store the entry */
+		res = ext4_fname_crypto_namelen_on_disk(ctx, namelen);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			return res;
+		}
+		reclen = EXT4_DIR_REC_LEN(res);
+
+		/* Allocate buffer to hold maximum name length */
+		res = ext4_fname_crypto_alloc_buffer(ctx,
+				&fname_crypto_str.name,
+				&fname_crypto_str.len,
+				EXT4_NAME_LEN);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			return -1;
+		}
+	}
 
 	de = (struct ext4_dir_entry_2 *)buf;
 	top = buf + buf_size - reclen;
 	while ((char *) de <= top) {
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
-					 buf, buf_size, offset))
-			return -EIO;
-		if (ext4_match(namelen, name, de))
-			return -EEXIST;
+					 buf, buf_size, offset)) {
+			res = -EIO;
+			goto return_result;
+		}
+		/* Provide crypto context and crypto buffer to ext4 match */
+		res = ext4_match(ctx, &fname_crypto_str, namelen, name, de);
+		if (res < 0)
+			goto return_result;
+		if (res > 0) {
+			res = -EEXIST;
+			goto return_result;
+		}
 		nlen = EXT4_DIR_REC_LEN(de->name_len);
 		rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
 		if ((de->inode ? rlen - nlen : rlen) >= reclen)
@@ -1760,11 +1919,17 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 		de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
 		offset += rlen;
 	}
-	if ((char *) de > top)
-		return -ENOSPC;
 
-	*dest_de = de;
-	return 0;
+	if ((char *) de > top)
+		res = -ENOSPC;
+	else {
+		*dest_de = de;
+		res = 0;
+	}
+return_result:
+	ext4_put_fname_crypto_ctx(&ctx);
+	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+	return res;
 }
 
 int ext4_insert_dentry(struct inode *dir,
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 19/22] ext4 crypto: enable filename encryption
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (17 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 18/22] ext4 crypto: filename encryption modifications Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-08 18:38   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 20/22] ext4 crypto: Add symlink encryption Theodore Ts'o
                   ` (4 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Uday Savagaonkar, Ildar Muslukhov, Theodore Ts'o

From: Michael Halcrow <mhalcrow@google.com>

Change-Id: I1057e08bf05741b963705f2850710ec5d7d8bd72
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/dir.c    | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 fs/ext4/ialloc.c | 21 ++++++++++++--
 2 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index f67f955..a77900f 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -111,6 +111,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
 	struct inode *inode = file_inode(file);
 	struct super_block *sb = inode->i_sb;
 	int dir_has_error = 0;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_fname_crypto_ctx *enc_ctx = NULL;
+	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
+#endif
+	int res;
 
 	if (is_dx_dir(inode)) {
 		err = ext4_dx_readdir(file, ctx);
@@ -127,11 +132,27 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
 
 	if (ext4_has_inline_data(inode)) {
 		int has_inline_data = 1;
-		int ret = ext4_read_inline_dir(file, ctx,
+		res = ext4_read_inline_dir(file, ctx,
 					   &has_inline_data);
 		if (has_inline_data)
-			return ret;
+			return res;
+	}
+
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	enc_ctx = ext4_get_fname_crypto_ctx(inode, EXT4_NAME_LEN);
+	if (IS_ERR(enc_ctx))
+		return PTR_ERR(enc_ctx);
+	if (enc_ctx) {
+		res = ext4_fname_crypto_alloc_buffer(enc_ctx,
+						     &fname_crypto_str.name,
+						     &fname_crypto_str.len,
+						     EXT4_NAME_LEN);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&enc_ctx);
+			return res;
+		}
 	}
+#endif
 
 	offset = ctx->pos & (sb->s_blocksize - 1);
 
@@ -226,13 +247,53 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
 			offset += ext4_rec_len_from_disk(de->rec_len,
 					sb->s_blocksize);
 			if (le32_to_cpu(de->inode)) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+				if (enc_ctx == NULL) {
+					/* Directory is not encrypted */
+					if (!dir_emit(ctx, de->name,
+					    de->name_len,
+					    le32_to_cpu(de->inode),
+					    get_dtype(sb, de->file_type))) {
+						ext4_put_fname_crypto_ctx(
+							&enc_ctx);
+						ext4_fname_crypto_free_buffer(
+							(void **)&fname_crypto_str.name);
+						brelse(bh);
+						return 0;
+					}
+				} else {
+					/* Directory is encrypted */
+					err = ext4_fname_disk_to_usr(enc_ctx,
+							de, &fname_crypto_str);
+					if (err < 0) {
+						ext4_put_fname_crypto_ctx(
+							&enc_ctx);
+						ext4_fname_crypto_free_buffer(
+							(void **)&fname_crypto_str.name);
+						brelse(bh);
+						return err;
+					}
+					if (!dir_emit(ctx,
+					    fname_crypto_str.name, err,
+					    le32_to_cpu(de->inode),
+					    get_dtype(sb, de->file_type))) {
+						ext4_put_fname_crypto_ctx(
+							&enc_ctx);
+						ext4_fname_crypto_free_buffer(
+							(void **)&fname_crypto_str.name);
+						brelse(bh);
+						return 0;
+					}
+				}
+#else
 				if (!dir_emit(ctx, de->name,
-						de->name_len,
-						le32_to_cpu(de->inode),
-						get_dtype(sb, de->file_type))) {
+					      de->name_len,
+					      le32_to_cpu(de->inode),
+					      get_dtype(sb, de->file_type))) {
 					brelse(bh);
 					return 0;
 				}
+#endif
 			}
 			ctx->pos += ext4_rec_len_from_disk(de->rec_len,
 						sb->s_blocksize);
@@ -240,10 +301,20 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
 		offset = 0;
 		brelse(bh);
 		if (ctx->pos < inode->i_size) {
-			if (!dir_relax(inode))
+			if (!dir_relax(inode)) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+				ext4_put_fname_crypto_ctx(&enc_ctx);
+				ext4_fname_crypto_free_buffer(
+						(void **)&fname_crypto_str.name);
+#endif
 				return 0;
+			}
 		}
 	}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	ext4_put_fname_crypto_ctx(&enc_ctx);
+	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
+#endif
 	return 0;
 }
 
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index e554ca3..8f37c9e 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1034,11 +1034,28 @@ got:
 	ext4_set_inode_state(inode, EXT4_STATE_NEW);
 
 	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
-
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	if ((sbi->s_file_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) &&
+	    (sbi->s_dir_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID)) {
+		ei->i_inline_off = 0;
+		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+			EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+			ext4_set_inode_state(inode,
+			EXT4_STATE_MAY_INLINE_DATA);
+	} else {
+		/* Inline data and encryption are incompatible
+		 * We turn off inline data since encryption is enabled */
+		ei->i_inline_off = 1;
+		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+			EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+			ext4_clear_inode_state(inode,
+			EXT4_STATE_MAY_INLINE_DATA);
+	}
+#else
 	ei->i_inline_off = 0;
 	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA))
 		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 20/22] ext4 crypto: Add symlink encryption
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (18 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 19/22] ext4 crypto: enable filename encryption Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-08 17:58   ` Andreas Dilger
  2015-04-02 22:10 ` [PATCH 21/22] ext4 crypto: enable encryption feature flag Theodore Ts'o
                   ` (3 subsequent siblings)
  23 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: jaegeuk, mhalcrow, Theodore Ts'o, Uday Savagaonkar

Change-Id: Ic92ebe4c615721650ccaf16b3175c2f4e931af2d
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4_crypto.h |  20 ++++++++++
 fs/ext4/namei.c       |  63 +++++++++++++++++++++++++++---
 fs/ext4/symlink.c     | 104 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 180 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index 68e95d8..4597530 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -117,4 +117,24 @@ struct ext4_fname_crypto_ctx {
 	unsigned ctfm_key_is_ready : 1;
 };
 
+/**
+ * For encrypted symlinks, the ciphertext length is stored at the beginning
+ * of the string in little-endian format.
+ */
+struct ext4_encrypted_symlink_data {
+	__le32 len;
+	char encrypted_path[1];
+} __attribute__((__packed__));
+
+/**
+ * This function is used to calculate the disk space required to
+ * store a filename of length l in encrypted symlink format.
+ */
+static inline u32 encrypted_symlink_data_len(u32 l)
+{
+	return ((l + EXT4_CRYPTO_BLOCK_SIZE - 1) / EXT4_CRYPTO_BLOCK_SIZE)
+		* EXT4_CRYPTO_BLOCK_SIZE
+		+ sizeof(struct ext4_encrypted_symlink_data) - 1;
+}
+
 #endif	/* _EXT4_CRYPTO_H */
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 80a3979..57db793 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3198,14 +3198,31 @@ static int ext4_symlink(struct inode *dir,
 	struct inode *inode;
 	int l, err, retries = 0;
 	int credits;
+	bool encryption_required = false;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	int l2;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct qstr istr;
+	struct ext4_str ostr;
+	struct ext4_encrypted_symlink_data *sd = NULL;
+	struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb);
+#endif
 
-	l = strlen(symname)+1;
+	l = strlen(symname) + 1;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	l2 = encrypted_symlink_data_len(l - 1);
+	encryption_required = (ext4_encrypted_inode(dir) ||
+			       unlikely(sbi->s_mount_flags &
+					EXT4_MF_TEST_DUMMY_ENCRYPTION));
+	if (encryption_required && l2 > dir->i_sb->s_blocksize)
+#else
 	if (l > dir->i_sb->s_blocksize)
+#endif
 		return -ENAMETOOLONG;
 
 	dquot_initialize(dir);
 
-	if (l > EXT4_N_BLOCKS * 4) {
+	if ((l > EXT4_N_BLOCKS * 4) || encryption_required) {
 		/*
 		 * For non-fast symlinks, we just allocate inode and put it on
 		 * orphan list in the first transaction => we need bitmap,
@@ -3233,7 +3250,7 @@ retry:
 	if (IS_ERR(inode))
 		goto out_stop;
 
-	if (l > EXT4_N_BLOCKS * 4) {
+	if ((l > EXT4_N_BLOCKS * 4) || encryption_required) {
 		inode->i_op = &ext4_symlink_inode_operations;
 		ext4_set_aops(inode);
 		/*
@@ -3251,9 +3268,40 @@ retry:
 		ext4_journal_stop(handle);
 		if (err)
 			goto err_drop_inode;
-		err = __page_symlink(inode, symname, l, 1);
-		if (err)
-			goto err_drop_inode;
+		if (!encryption_required) {
+			err = __page_symlink(inode, symname, l, 1);
+			if (err)
+				goto err_drop_inode;
+		}
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		else {
+			sd = kmalloc(l2 + 1, GFP_NOFS);
+			if (!sd) {
+				err = -ENOMEM;
+				goto err_drop_inode;
+			}
+			sd->encrypted_path[l2] = '\0';
+			err = ext4_inherit_context(dir, inode);
+			ctx = ext4_get_fname_crypto_ctx(
+				inode, inode->i_sb->s_blocksize);
+			if (IS_ERR_OR_NULL(ctx)) {
+				/* We just set the policy, so ctx should
+				   not be NULL */
+				err = (ctx == NULL) ? -EIO : PTR_ERR(ctx);
+				goto err_drop_inode;
+			}
+			istr.name = (const unsigned char *) symname;
+			istr.len = l - 1;
+			ostr.name = sd->encrypted_path;
+			err = ext4_fname_usr_to_disk(ctx, &istr, &ostr);
+			ext4_put_fname_crypto_ctx(&ctx);
+			if (err < 0)
+				goto err_drop_inode;
+			sd->len = cpu_to_le32(ostr.len);
+			err = __page_symlink(inode, (char *)sd, l2 + 1, 1);
+
+		}
+#endif
 		/*
 		 * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
 		 * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
@@ -3293,6 +3341,9 @@ out_stop:
 err_drop_inode:
 	unlock_new_inode(inode);
 	iput(inode);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	kfree(sd);
+#endif
 	return err;
 }
 
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index ff37119..d788891 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -22,9 +22,106 @@
 #include <linux/namei.h>
 #include "ext4.h"
 #include "xattr.h"
+#include "ext4_crypto.h"
 
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
 static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
+	struct page *cpage = NULL;
+	char *caddr, *paddr;
+	struct ext4_str cstr, pstr;
+	struct inode *inode = dentry->d_inode;
+	struct ext4_fname_crypto_ctx *ctx = NULL;
+	struct ext4_encrypted_symlink_data *sd;
+	loff_t size = min(inode->i_size, (loff_t) PAGE_SIZE-1);
+	int res;
+	u32 plen, plen2;
+
+	ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(cpage)) {
+		ext4_put_fname_crypto_ctx(&ctx);
+		return cpage;
+	}
+	caddr = kmap(cpage);
+	caddr[size] = 0;
+
+	if (!ctx) {
+		/* Symlink is unencrypted */
+		plen = strnlen((char *)caddr, inode->i_sb->s_blocksize);
+		plen2 = (plen < inode->i_sb->s_blocksize) ? plen + 1 : plen;
+		paddr = kmalloc(plen2, GFP_NOFS);
+		if (!paddr) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			kunmap(cpage);
+			page_cache_release(cpage);
+			return ERR_PTR(-ENOMEM);
+		}
+		memcpy(paddr, caddr, plen);
+		if (plen < inode->i_sb->s_blocksize)
+			paddr[plen] = '\0';
+	} else {
+		/* Symlink is encrypted */
+		sd = (struct ext4_encrypted_symlink_data *)caddr;
+		cstr.name = sd->encrypted_path;
+		cstr.len  = le32_to_cpu(sd->len);
+		if ((cstr.len + sizeof(struct ext4_encrypted_symlink_data) - 1)
+			> inode->i_sb->s_blocksize) {
+			/* Symlink data on the disk is corrupted */
+			kunmap(cpage);
+			page_cache_release(cpage);
+			return ERR_PTR(-EIO);
+		}
+		plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
+			? EXT4_FNAME_CRYPTO_DIGEST_SIZE*2
+			: cstr.len;
+		paddr = kmalloc(plen + 1, GFP_NOFS);
+		if (!paddr) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			kunmap(cpage);
+			page_cache_release(cpage);
+			return ERR_PTR(-ENOMEM);
+		}
+		pstr.name = paddr;
+		res = _ext4_fname_disk_to_usr(ctx, &cstr, &pstr);
+		if (res < 0) {
+			ext4_put_fname_crypto_ctx(&ctx);
+			kunmap(cpage);
+			page_cache_release(cpage);
+			kfree(paddr);
+			return ERR_PTR(res);
+		}
+		/* Null-terminate the name */
+		if (res <= plen)
+			paddr[res] = '\0';
+	}
+	nd_set_link(nd, paddr);
+	ext4_put_fname_crypto_ctx(&ctx);
+	return cpage;
+}
+
+static void ext4_put_link(struct dentry *dentry, struct nameidata *nd,
+			  void *cookie)
+{
+	struct page *page = cookie;
+	char *buf = nd_get_link(nd);
+
+	if (page) {
+		kunmap(page);
+		page_cache_release(page);
+	}
+	if (buf) {
+		nd_set_link(nd, NULL);
+		kfree(buf);
+	}
+}
+#endif
+
+static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd)
+{
 	struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
 	nd_set_link(nd, (char *) ei->i_data);
 	return NULL;
@@ -32,8 +129,13 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
 
 const struct inode_operations ext4_symlink_inode_operations = {
 	.readlink	= generic_readlink,
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	.follow_link    = ext4_follow_link,
+	.put_link       = ext4_put_link,
+#else
 	.follow_link	= page_follow_link_light,
 	.put_link	= page_put_link,
+#endif
 	.setattr	= ext4_setattr,
 	.setxattr	= generic_setxattr,
 	.getxattr	= generic_getxattr,
@@ -43,7 +145,7 @@ const struct inode_operations ext4_symlink_inode_operations = {
 
 const struct inode_operations ext4_fast_symlink_inode_operations = {
 	.readlink	= generic_readlink,
-	.follow_link	= ext4_follow_link,
+	.follow_link    = ext4_follow_fast_link,
 	.setattr	= ext4_setattr,
 	.setxattr	= generic_setxattr,
 	.getxattr	= generic_getxattr,
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 21/22] ext4 crypto: enable encryption feature flag
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (19 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 20/22] ext4 crypto: Add symlink encryption Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-02 22:10 ` [PATCH 22/22] ext4 crypto: add password salt support Theodore Ts'o
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

Also add the test dummy encryption mode flag so we can more easily
test the encryption patches using xfstests.

Change-Id: I63a7f5b969738eed81b2f12715cfff161a988d84
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/crypto_key.c    | 27 +++++++++++++++------------
 fs/ext4/crypto_policy.c | 20 +++++++++++++++++---
 fs/ext4/ext4.h          | 10 ++++++----
 fs/ext4/ialloc.c        |  4 +++-
 fs/ext4/namei.c         | 13 +++++++++++--
 fs/ext4/super.c         | 20 +++++++++++++++++++-
 6 files changed, 71 insertions(+), 23 deletions(-)

diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index 3620e16..50c3c1a 100644
--- a/fs/ext4/crypto_key.c
+++ b/fs/ext4/crypto_key.c
@@ -104,6 +104,7 @@ int ext4_generate_encryption_key(struct inode *inode)
 	struct ext4_encryption_key *master_key;
 	struct ext4_encryption_context ctx;
 	struct user_key_payload *ukp;
+	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
 	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
 				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
 				 &ctx, sizeof(ctx));
@@ -115,6 +116,20 @@ int ext4_generate_encryption_key(struct inode *inode)
 	}
 	res = 0;
 
+	if (S_ISREG(inode->i_mode))
+		crypt_key->mode = ctx.contents_encryption_mode;
+	else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+		crypt_key->mode = ctx.filenames_encryption_mode;
+	else {
+		printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n");
+		BUG();
+	}
+	crypt_key->size = ext4_encryption_key_size(crypt_key->mode);
+	BUG_ON(!crypt_key->size);
+	if (unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION)) {
+		memset(crypt_key->raw, 0x42, EXT4_AES_256_XTS_KEY_SIZE);
+		goto out;
+	}
 	memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX,
 	       EXT4_KEY_DESC_PREFIX_SIZE);
 	ext4_to_hex(&full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE],
@@ -134,21 +149,9 @@ int ext4_generate_encryption_key(struct inode *inode)
 		goto out;
 	}
 	master_key = (struct ext4_encryption_key *)ukp->data;
-
-	if (S_ISREG(inode->i_mode))
-		crypt_key->mode = ctx.contents_encryption_mode;
-	else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-		crypt_key->mode = ctx.filenames_encryption_mode;
-	else {
-		printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n");
-		BUG();
-	}
-	crypt_key->size = ext4_encryption_key_size(crypt_key->mode);
-	BUG_ON(!crypt_key->size);
 	BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE !=
 		     EXT4_KEY_DERIVATION_NONCE_SIZE);
 	BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE);
-	BUG_ON(crypt_key->size < EXT4_AES_256_CBC_KEY_SIZE);
 	res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw);
 out:
 	if (keyring_key)
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index 3ff4c75..7debb55 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -155,17 +155,31 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent,
 int ext4_inherit_context(struct inode *parent, struct inode *child)
 {
 	struct ext4_encryption_context ctx;
+	struct ext4_sb_info *sbi = EXT4_SB(parent->i_sb);
 	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
 				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
 				 &ctx, sizeof(ctx));
 
-	if (res != sizeof(ctx))
-		return -ENOENT;
-
+	if (res != sizeof(ctx)) {
+		if (unlikely(sbi->s_mount_flags &
+			     EXT4_MF_TEST_DUMMY_ENCRYPTION)) {
+			ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
+			ctx.contents_encryption_mode =
+				EXT4_ENCRYPTION_MODE_AES_256_XTS;
+			ctx.filenames_encryption_mode =
+				EXT4_ENCRYPTION_MODE_AES_256_CBC;
+			memset(ctx.master_key_descriptor, 0x42,
+			       EXT4_KEY_DESCRIPTOR_SIZE);
+			res = 0;
+		} else {
+			goto out;
+		}
+	}
 	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
 	res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
 			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
 			     sizeof(ctx), 0);
+out:
 	if (!res)
 		ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
 	return res;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 421c065..de3b1e4 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1183,8 +1183,9 @@ struct ext4_super_block {
 /*
  * run-time mount flags
  */
-#define EXT4_MF_MNTDIR_SAMPLED	0x0001
-#define EXT4_MF_FS_ABORTED	0x0002	/* Fatal error detected */
+#define EXT4_MF_MNTDIR_SAMPLED		0x0001
+#define EXT4_MF_FS_ABORTED		0x0002	/* Fatal error detected */
+#define EXT4_MF_TEST_DUMMY_ENCRYPTION	0x0004
 
 /* Number of quota types we support */
 #define EXT4_MAXQUOTAS 2
@@ -1595,8 +1596,9 @@ static inline int ext4_encrypted_inode(struct inode *inode)
 					 EXT4_FEATURE_INCOMPAT_EXTENTS| \
 					 EXT4_FEATURE_INCOMPAT_64BIT| \
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG| \
-					 EXT4_FEATURE_INCOMPAT_MMP |	\
-					 EXT4_FEATURE_INCOMPAT_INLINE_DATA)
+					 EXT4_FEATURE_INCOMPAT_MMP | \
+					 EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
+					 EXT4_FEATURE_INCOMPAT_ENCRYPT)
 #define EXT4_FEATURE_RO_COMPAT_SUPP	(EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
 					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 8f37c9e..68d0142 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -999,7 +999,9 @@ got:
 
 	/* If the directory encrypted, then we should encrypt the inode. */
 	if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) &&
-	    ext4_encrypted_inode(dir))
+	    (ext4_encrypted_inode(dir) ||
+	     unlikely(sbi->s_mount_flags &
+		      EXT4_MF_TEST_DUMMY_ENCRYPTION)))
 		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
 
 	ext4_set_inode_flags(inode);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 57db793..ae61416 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2577,6 +2577,9 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 	handle_t *handle;
 	struct inode *inode;
 	int err, credits, retries = 0;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb);
+#endif
 
 	dquot_initialize(dir);
 
@@ -2595,7 +2598,9 @@ retry:
 		if (!err && IS_DIRSYNC(dir))
 			ext4_handle_sync(handle);
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
-		if (!err && ext4_encrypted_inode(dir)) {
+		if (!err && (ext4_encrypted_inode(dir) ||
+			     unlikely(sbi->s_mount_flags &
+				      EXT4_MF_TEST_DUMMY_ENCRYPTION))) {
 			err = ext4_inherit_context(dir, inode);
 			if (err)
 				ext4_unlink(dir, dentry);
@@ -2757,6 +2762,9 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 	handle_t *handle;
 	struct inode *inode;
 	int err, credits, retries = 0;
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+	struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb);
+#endif
 
 	if (EXT4_DIR_LINK_MAX(dir))
 		return -EMLINK;
@@ -2800,7 +2808,8 @@ out_clear_inode:
 	if (IS_DIRSYNC(dir))
 		ext4_handle_sync(handle);
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
-	if (ext4_encrypted_inode(dir)) {
+	if (ext4_encrypted_inode(dir) ||
+	    unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION)) {
 		err = ext4_inherit_context(dir, inode);
 		if (err)
 			ext4_unlink(dir, dentry);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3dcafe9..d904e0a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1136,7 +1136,7 @@ enum {
 	Opt_commit, Opt_min_batch_time, Opt_max_batch_time, Opt_journal_dev,
 	Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
 	Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
-	Opt_data_err_abort, Opt_data_err_ignore,
+	Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
 	Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
 	Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
 	Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
@@ -1223,6 +1223,7 @@ static const match_table_t tokens = {
 	{Opt_init_itable, "init_itable"},
 	{Opt_noinit_itable, "noinit_itable"},
 	{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
+	{Opt_test_dummy_encryption, "test_dummy_encryption"},
 	{Opt_removed, "check=none"},	/* mount option from ext2/3 */
 	{Opt_removed, "nocheck"},	/* mount option from ext2/3 */
 	{Opt_removed, "reservation"},	/* mount option from ext2/3 */
@@ -1423,6 +1424,7 @@ static const struct mount_opts {
 	{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
 	{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
 	{Opt_max_dir_size_kb, 0, MOPT_GTE0},
+	{Opt_test_dummy_encryption, 0, MOPT_GTE0},
 	{Opt_err, 0, 0}
 };
 
@@ -1593,6 +1595,15 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 		}
 		*journal_ioprio =
 			IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
+	} else if (token == Opt_test_dummy_encryption) {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
+		ext4_msg(sb, KERN_WARNING,
+			 "Test dummy encryption mode enabled");
+#else
+		ext4_msg(sb, KERN_WARNING,
+			 "Test dummy encryption mount option ignored");
+#endif
 	} else if (m->flags & MOPT_DATAJ) {
 		if (is_remount) {
 			if (!sbi->s_journal)
@@ -4036,6 +4047,13 @@ no_journal:
 		}
 	}
 
+	if (unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION) &&
+	    !(sb->s_flags & MS_RDONLY) &&
+	    !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
+		EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT);
+		ext4_commit_super(sb, 1);
+	}
+
 	/*
 	 * Get the # of file system overhead blocks from the
 	 * superblock if present.
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 22/22] ext4 crypto: add password salt support
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (20 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 21/22] ext4 crypto: enable encryption feature flag Theodore Ts'o
@ 2015-04-02 22:10 ` Theodore Ts'o
  2015-04-03  1:57 ` [PATCH 00/22] ext4 encryption patches Theodore Ts'o
  2015-04-06 20:28 ` Jonathan Corbet
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-02 22:10 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow, Theodore Ts'o

The goal of the salt is to prevent rainbow table attacks on users'
passphrases.  The salt is fetched by e4crypto using an ioctl
interface; if the salt field in the superblock is not yet set, the
ioctl will generate a random UUID and use that as the salt for the
file system.

Change-Id: Icb8c901fb842eecd730f1bb3ef112e6607d77889
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  |  4 +++-
 fs/ext4/ioctl.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index de3b1e4..0b281aa 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -612,6 +612,7 @@ enum {
 #define EXT4_IOC_SWAP_BOOT		_IO('f', 17)
 #define EXT4_IOC_PRECACHE_EXTENTS	_IO('f', 18)
 #define EXT4_IOC_ENCRYPTION_POLICY	_IOW('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_PWSALT	_IOW('f', 20, __u8[16])
 
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
@@ -1172,7 +1173,8 @@ struct ext4_super_block {
 	__le32	s_overhead_clusters;	/* overhead blocks/clusters in fs */
 	__le32	s_backup_bgs[2];	/* groups with sparse_super2 SBs */
 	__u8	s_encrypt_algos[4];	/* Encryption algorithms in use  */
-	__le32	s_reserved[105];	/* Padding to the end of the block */
+	__u8	s_encrypt_pw_salt[16];	/* Salt used for string2key algorithm */
+	__le32	s_reserved[101];	/* Padding to the end of the block */
 	__le32	s_checksum;		/* crc32c(superblock) */
 };
 
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e4ae8f9..f5d8ec0 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -14,6 +14,7 @@
 #include <linux/compat.h>
 #include <linux/mount.h>
 #include <linux/file.h>
+#include <linux/random.h>
 #include <asm/uaccess.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
@@ -196,6 +197,16 @@ journal_err_out:
 	return err;
 }
 
+static int uuid_is_zero(__u8 u[16])
+{
+	int	i;
+
+	for (i=0; i < 16; i++)
+		if (u[i])
+			return 0;
+	return 1;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = file_inode(filp);
@@ -635,6 +646,43 @@ encryption_policy_out:
 #else
 		return -EOPNOTSUPP;
 #endif
+	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
+	{
+		int err, err2;
+		struct ext4_sb_info *sbi = EXT4_SB(sb);
+		handle_t *handle;
+
+		if (!ext4_sb_has_crypto(sb))
+			return -EOPNOTSUPP;
+		if (uuid_is_zero(sbi->s_es->s_encrypt_pw_salt)) {
+			err = mnt_want_write_file(filp);
+			if (err)
+				return err;
+			handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1);
+			if (IS_ERR(handle)) {
+				err = PTR_ERR(handle);
+				goto pwsalt_err_exit;
+			}
+			err = ext4_journal_get_write_access(handle, sbi->s_sbh);
+			if (err)
+				goto pwsalt_err_journal;
+			generate_random_uuid(sbi->s_es->s_encrypt_pw_salt);
+			err = ext4_handle_dirty_metadata(handle, NULL,
+							 sbi->s_sbh);
+		pwsalt_err_journal:
+			err2 = ext4_journal_stop(handle);
+			if (err2 && !err)
+				err = err2;
+		pwsalt_err_exit:
+			mnt_drop_write_file(filp);
+			if (err)
+				return err;
+		}
+		if (copy_to_user((void *) arg, sbi->s_es->s_encrypt_pw_salt,
+				 16))
+			return -EFAULT;
+		return 0;
+	}
 	default:
 		return -ENOTTY;
 	}
@@ -700,6 +748,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case EXT4_IOC_RESIZE_FS:
 	case EXT4_IOC_PRECACHE_EXTENTS:
 	case EXT4_IOC_ENCRYPTION_POLICY:
+	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
 		break;
 	default:
 		return -ENOIOCTLCMD;
-- 
2.3.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 00/22] ext4 encryption patches
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (21 preceding siblings ...)
  2015-04-02 22:10 ` [PATCH 22/22] ext4 crypto: add password salt support Theodore Ts'o
@ 2015-04-03  1:57 ` Theodore Ts'o
  2015-04-06 20:28 ` Jonathan Corbet
  23 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-03  1:57 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: jaegeuk, mhalcrow

On Thu, Apr 02, 2015 at 06:10:37PM -0400, Theodore Ts'o wrote:
> 
> There is a design document here.  It should hopefully be mostly up to
> date, but there are a few things that we might end up changing (for
> example, just using CTS all the time for protecting directory file
> names).
> 
> https://docs.google.com/document/d/1IsyQ9DU1gA6NUqS0jF4ni_NTvv-b0HfCkRk47Zkd7W0

Andreas pointed out this URL wasn't working due to access control
issues; my bad.  Please try this one instead.

https://docs.google.com/document/d/1ft26lUQyuSpiu6VleP70_npaWdRfXFoNnB8JYnykNTg

						- Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 00/22] ext4 encryption patches
  2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
                   ` (22 preceding siblings ...)
  2015-04-03  1:57 ` [PATCH 00/22] ext4 encryption patches Theodore Ts'o
@ 2015-04-06 20:28 ` Jonathan Corbet
  2015-04-08  3:07   ` Theodore Ts'o
  23 siblings, 1 reply; 44+ messages in thread
From: Jonathan Corbet @ 2015-04-06 20:28 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: Ext4 Developers List, jaegeuk, mhalcrow

On Thu,  2 Apr 2015 18:10:37 -0400
Theodore Ts'o <tytso@mit.edu> wrote:

> There is a design document here.  It should hopefully be mostly up to
> date, but there are a few things that we might end up changing (for
> example, just using CTS all the time for protecting directory file
> names).
> 
> https://docs.google.com/document/d/1IsyQ9DU1gA6NUqS0jF4ni_NTvv-b0HfCkRk47Zkd7W0

I just tried to take a look at this, but it tells me that I don't have
permission to do so.  Presumably that's not what was intended?

Thanks,

jon

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 01/22] ext4: add ext4_mpage_readpages()
  2015-04-02 22:10 ` [PATCH 01/22] ext4: add ext4_mpage_readpages() Theodore Ts'o
@ 2015-04-06 21:08   ` Andreas Dilger
  2015-04-08  3:04     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-06 21:08 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: Ext4 Developers List, jaegeuk, mhalcrow

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> This takes code from fs/mpage.c and optimizes it for ext4.  Its
> primary reason is to allow us to more easily add encryption to ext4's
> read path in an efficient manner.
> 
[snip]
> diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
> new file mode 100644
> index 0000000..9ca4dfc
> --- /dev/null
> +++ b/fs/ext4/readpage.c
> @@ -0,0 +1,261 @@
> +/*
> + * linux/fs/ext4/readpage.c
> + *
> + * This was originally taken from fs/mpage.c
> + *
> + * The intent is the ext4_mpage_readpages() function here is intended
> + * to replace mpage_readpages() in the general case, not just for
> + * encrypted files.  It has some limitations (see below), where it
> + * will fall back to read_block_full_page(), but these limitations
> + * should never be hit when page_size != block_size.

Shouldn't this be "... should never be hit when page_size == block_size"?
Otherwise, we'd hit them almost all the time...

Cheers, Andreas





^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-02 22:10 ` [PATCH 06/22] ext4 crypto: add encryption policy checking Theodore Ts'o
@ 2015-04-06 21:31   ` Andreas Dilger
  2015-04-11 13:06     ` Theodore Ts'o
  2015-04-08 18:07   ` Andreas Dilger
  1 sibling, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-06 21:31 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> The ext4_crypto.h header will get fleshed out as later patches in this
> patchset add functionality.
> 
> Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
> ---
> fs/ext4/Makefile        |   1 +
> fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/ext4.h          |   2 +
> fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
> 4 files changed, 212 insertions(+)
> create mode 100644 fs/ext4/crypto_policy.c
> create mode 100644 fs/ext4/ext4_crypto.h
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index cd6f50f..3886ee4 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,3 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
> 
> ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
> ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
> +ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o
> diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
> new file mode 100644
> index 0000000..5cb4e74
> --- /dev/null
> +++ b/fs/ext4/crypto_policy.c
> @@ -0,0 +1,155 @@
> +/*
> + * linux/fs/ext4/crypto_policy.c
> + *
> + * This contains encryption policy functions for ext4
> + *
> + * Written by Michael Halcrow, 2015.
> + */
> +
> +#include <linux/random.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#include "ext4.h"
> +#include "xattr.h"
> +
> +/**
> + * ext4_to_hex() - Converts to hexadecimal characters
> + * @dst: Buffer to take hex character representation of contents of
> + *       src. Must be at least of size (src_size * 2).
> + * @src: Buffer to be converted to a hex string respresentation.
> + * @src_size: Number of bytes to convert.
> + */
> +void ext4_to_hex(char *dst, char *src, size_t src_size)
> +{
> +	int x;
> +
> +	for (x = 0; x < src_size; x++)
> +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> +}

I think there is already some code in printk() to handle this?  Looking
at vsnprintf->hex_string() it looks like "%*ph" would print out up to 64
bytes as hex.

Cheers, Andreas

> +
> +/**
> + *
> + */
> +static int ext4_inode_has_encryption_context(struct inode *inode)
> +{
> +	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
> +	return (res > 0);
> +}
> +
> +/**
> + * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
> + * @inode:  ...
> + * @policy: ...
> + *
> + * Return ...
> + */
> +static int ext4_is_encryption_context_consistent_with_policy(
> +	struct inode *inode, const struct ext4_encryption_policy *policy)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +				 sizeof(ctx));
> +	if (res != sizeof(ctx))
> +		return 0;
> +	return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
> +			EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(ctx.contents_encryption_mode ==
> +		 policy->contents_encryption_mode) &&
> +		(ctx.filenames_encryption_mode ==
> +		 policy->filenames_encryption_mode));
> +}
> +
> +static int ext4_create_encryption_context_from_policy(
> +	struct inode *inode, const struct ext4_encryption_policy *policy)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = 0;
> +
> +	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
> +	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
> +	       EXT4_KEY_DESCRIPTOR_SIZE);
> +	ctx.contents_encryption_mode = policy->contents_encryption_mode;
> +	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
> +	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +
> +	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +			     sizeof(ctx), 0);
> +	if (!res)
> +		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
> +	return res;
> +}
> +
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode)
> +{
> +	int res = 0;
> +
> +	if (!ext4_inode_has_encryption_context(inode)) {
> +		res = ext4_create_encryption_context_from_policy(inode, policy);
> +		goto out;
> +	}
> +
> +	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
> +		printk(KERN_WARNING
> +		       "%s: Policy inconsistent with encryption context\n",
> +		       __func__);
> +		res = -EINVAL;
> +	}
> +out:
> +	return res;
> +}
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child)
> +{
> +	struct ext4_encryption_context parent_ctx, child_ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &parent_ctx, sizeof(parent_ctx));
> +
> +	if (res != sizeof(parent_ctx))
> +		return 0;
> +	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +			     &child_ctx, sizeof(child_ctx));
> +	if (res != sizeof(child_ctx))
> +		return 0;
> +	return (memcmp(parent_ctx.master_key_descriptor,
> +		       child_ctx.master_key_descriptor,
> +		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(parent_ctx.contents_encryption_mode ==
> +		 child_ctx.contents_encryption_mode) &&
> +		(parent_ctx.filenames_encryption_mode ==
> +		 child_ctx.filenames_encryption_mode));
> +}
> +
> +/**
> + * ext4_inherit_context() - Sets a child context from its parent
> + * @parent: Parent inode from which the context is inherited.
> + * @child:  Child inode that inherits the context from @parent.
> + *
> + * Return: Zero on success, non-zero otherwise
> + */
> +int ext4_inherit_context(struct inode *parent, struct inode *child)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &ctx, sizeof(ctx));
> +
> +	if (res != sizeof(ctx))
> +		return -ENOENT;
> +
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +			     sizeof(ctx), 0);
> +	if (!res)
> +		ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
> +	return res;
> +}
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2f3808e..fd2f3dd 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -588,6 +588,8 @@ enum {
> #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
> #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
> 
> +#include "ext4_crypto.h"
> +
> /*
>  * ioctl commands
>  */
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> new file mode 100644
> index 0000000..984ff38
> --- /dev/null
> +++ b/fs/ext4/ext4_crypto.h
> @@ -0,0 +1,54 @@
> +/*
> + * linux/fs/ext4/ext4_crypto.h
> + *
> + * This contains encryption header content for ext4
> + *
> + * Written by Michael Halcrow, 2015.
> + */
> +
> +#ifndef _EXT4_CRYPTO_H
> +#define _EXT4_CRYPTO_H
> +
> +#include <linux/fs.h>
> +
> +#define EXT4_KEY_DESCRIPTOR_SIZE 8
> +
> +/* Policy provided via an ioctl on the topmost directory */
> +struct ext4_encryption_policy {
> +	char version;
> +	char contents_encryption_mode;
> +	char filenames_encryption_mode;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +} __attribute__((__packed__));
> +
> +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
> +#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
> +
> +/**
> + * Encryption context for inode
> + *
> + * Protector format:
> + *  1 byte: Protector format (0 = this version)
> + *  1 byte: File contents encryption mode
> + *  1 byte: File names encryption mode
> + *  1 byte: Reserved
> + *  8 bytes: Master Key descriptor
> + *  16 bytes: Encryption Key derivation nonce
> + */
> +struct ext4_encryption_context {
> +	char format;
> +	char contents_encryption_mode;
> +	char filenames_encryption_mode;
> +	char reserved;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
> +} __attribute__((__packed__));
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child);
> +int ext4_inherit_context(struct inode *parent, struct inode *child);
> +void ext4_to_hex(char *dst, char *src, size_t src_size);
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode);
> +
> +#endif	/* _EXT4_CRYPTO_H */
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 01/22] ext4: add ext4_mpage_readpages()
  2015-04-06 21:08   ` Andreas Dilger
@ 2015-04-08  3:04     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-08  3:04 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow

On Mon, Apr 06, 2015 at 03:08:51PM -0600, Andreas Dilger wrote:
> On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> > 
> > This takes code from fs/mpage.c and optimizes it for ext4.  Its
> > primary reason is to allow us to more easily add encryption to ext4's
> > read path in an efficient manner.
> > 
> [snip]
> > diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
> > new file mode 100644
> > index 0000000..9ca4dfc
> > --- /dev/null
> > +++ b/fs/ext4/readpage.c
> > @@ -0,0 +1,261 @@
> > +/*
> > + * linux/fs/ext4/readpage.c
> > + *
> > + * This was originally taken from fs/mpage.c
> > + *
> > + * The intent is the ext4_mpage_readpages() function here is intended
> > + * to replace mpage_readpages() in the general case, not just for
> > + * encrypted files.  It has some limitations (see below), where it
> > + * will fall back to read_block_full_page(), but these limitations
> > + * should never be hit when page_size != block_size.
> 
> Shouldn't this be "... should never be hit when page_size == block_size"?
> Otherwise, we'd hit them almost all the time...

Yes, or maybe "should only be hit when...".  Thanks, good catch.

          	      	      	      	  - Ted
				  				  

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 00/22] ext4 encryption patches
  2015-04-06 20:28 ` Jonathan Corbet
@ 2015-04-08  3:07   ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-08  3:07 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: Ext4 Developers List, jaegeuk, mhalcrow

On Mon, Apr 06, 2015 at 10:28:00PM +0200, Jonathan Corbet wrote:
> On Thu,  2 Apr 2015 18:10:37 -0400
> Theodore Ts'o <tytso@mit.edu> wrote:
> 
> > There is a design document here.  It should hopefully be mostly up to
> > date, but there are a few things that we might end up changing (for
> > example, just using CTS all the time for protecting directory file
> > names).
> > 
> > https://docs.google.com/document/d/1IsyQ9DU1gA6NUqS0jF4ni_NTvv-b0HfCkRk47Zkd7W0
> 
> I just tried to take a look at this, but it tells me that I don't have
> permission to do so.  Presumably that's not what was intended?

Sorry, wrong URL.  It should have been:

https://docs.google.com/document/d/1ft26lUQyuSpiu6VleP70_npaWdRfXFoNnB8JYnykNTg

And Andreas has pointed out there are a few places where it may be
slightly out of date.  So it's good for checking the general idea, but
some of the details may not be quite right.  In particular, we are now
using CTS (Ciphertext Stealing) mode for directory entries, instead of
the weird thing described in the design doc.

							- Ted
							

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto
  2015-04-02 22:10 ` [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto Theodore Ts'o
@ 2015-04-08 17:44   ` Andreas Dilger
  2015-04-12  5:06     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-08 17:44 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Uday Savagaonkar,
	Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> Modifies dx_show_leaf and dx_probe to support fname encryption.
> Filename encryption not yet enabled.
> 
> Change-Id: I2058ea5cf6c3920a05c75e42acb2baab631fa1e8
> Signed-off-by: Uday Savagaonkar <savagaon@google.com>
> Signed-off-by: Ildar Muslukhov <ildarm@google.com>
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/namei.c | 161 ++++++++++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 122 insertions(+), 39 deletions(-)
> 
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index cbedeb0..4fccb76 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -588,8 +588,10 @@ struct stats
> 	unsigned bcount;
> };
> 
> -static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_entry_2 *de,
> -				 int size, int show_names)
> +static struct stats dx_show_leaf(struct inode *dir,
> +				struct dx_hash_info *hinfo,
> +				struct ext4_dir_entry_2 *de,
> +				int size, int show_names)
> {
> 	unsigned names = 0, space = 0;
> 	char *base = (char *) de;
> @@ -602,12 +604,85 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent
> 		{
> 			if (show_names)
> 			{
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +				int len;
> +				char *name;
> +				struct ext4_str fname_crypto_str
> +					= {.name = NULL, .len = 0};
> +				struct ext4_fname_crypto_ctx *ctx = NULL;
> +				int res;
> +
> +				name  = de->name;
> +				len = de->name_len;
> +				ctx = ext4_get_fname_crypto_ctx(dir,
> +								EXT4_NAME_LEN);
> +				if (IS_ERR(ctx)) {
> +					printk(KERN_WARNING "Error acquiring"
> +					" crypto ctxt--skipping crypto\n");
> +					ctx = NULL;
> +				}
> +				if (ctx == NULL) {
> +					/* Directory is not encrypted */
> +					while (len--)
> +						printk("%c", *name++);

This is a bit strange, why not use "printk("%*.s", len, name)"?

> +					ext4fs_dirhash(de->name,
> +						de->name_len, &h);
> +					printk(":(U)%x.%u ", h.hash,
> +						(unsigned) ((char *) de
> +							- base));

These two printk's could be combined into a single one.

> +				} else {
> +					/* Directory is encrypted */
> +					res = ext4_fname_crypto_alloc_buffer(
> +						ctx, &fname_crypto_str.name,
> +						&fname_crypto_str.len,
> +						de->name_len);
> +					if (res < 0) {
> +						printk(KERN_WARNING "Error "
> +							"allocating crypto "
> +							"buffer--skipping "
> +							"crypto\n");
> +						ext4_put_fname_crypto_ctx(&ctx);
> +						ctx = NULL;
> +					}
> +					res = ext4_fname_disk_to_usr(ctx, de,
> +							&fname_crypto_str);
> +					if (res < 0) {
> +						printk(KERN_WARNING "Error "
> +							"converting filename "
> +							"from disk to usr"
> +							"\n");
> +						name = "??";
> +						len = 2;
> +					} else {
> +						name = fname_crypto_str.name;
> +						len = fname_crypto_str.len;
> +					}
> +					while (len--)
> +						printk("%c", *name++);
> +					res = ext4_fname_disk_to_hash(ctx, de,
> +								      &h);
> +					if (res < 0) {
> +						printk(KERN_WARNING "Error "
> +							"converting filename "
> +							"from disk to htree"
> +							"\n");
> +						h.hash = 0xDEADBEEF;
> +					}
> +					printk(":(E)%x.%u ", h.hash,
> +						(unsigned) ((char *) de
> +						- base));
> +					ext4_put_fname_crypto_ctx(&ctx);
> +					ext4_fname_crypto_free_buffer(
> +						(void **)&fname_crypto_str.name);
> +				}
> +#else
> 				int len = de->name_len;
> 				char *name = de->name;
> 				while (len--) printk("%c", *name++);
> 				ext4fs_dirhash(de->name, de->name_len, &h);
> 				printk(":%x.%u ", h.hash,
> 				       (unsigned) ((char *) de - base));

I guess this could be fixed similarly, or possibly restructured so that
the code is not duplicated?

> +#endif
> 			}
> 			space += EXT4_DIR_REC_LEN(de->name_len);
> 			names++;
> @@ -639,7 +714,8 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
> 			continue;
> 		stats = levels?
> 		   dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
> -		   dx_show_leaf(hinfo, (struct ext4_dir_entry_2 *) bh->b_data, blocksize, 0);
> +		   dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *)
> +			bh->b_data, blocksize, 0);
> 		names += stats.names;
> 		space += stats.space;
> 		bcount += stats.bcount;
> @@ -672,6 +748,10 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
> 	struct dx_frame *frame = frame_in;
> 	struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR);
> 	u32 hash;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	struct ext4_fname_crypto_ctx *ctx = NULL;
> +	int res;
> +#endif
> 
> 	frame->bh = ext4_read_dirblock(dir, 0, INDEX);
> 	if (IS_ERR(frame->bh))
> @@ -689,8 +769,25 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
> 	if (hinfo->hash_version <= DX_HASH_TEA)
> 		hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
> 	hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if (d_name) {
> +		/* Check if the directory is encrypted */
> +		ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
> +		if (IS_ERR(ctx)) {
> +			ret_err = ERR_PTR(PTR_ERR(ctx));
> +			goto fail;
> +		}
> +		res = ext4_fname_usr_to_hash(ctx, d_name, hinfo);
> +		if (res < 0) {
> +			ret_err = ERR_PTR(res);
> +			goto fail;
> +		}
> +	}
> +#else
> 	if (d_name)
> 		ext4fs_dirhash(d_name->name, d_name->len, hinfo);
> +#endif
> +
> 	hash = hinfo->hash;
> 
> 	if (root->info.unused_flags & 1) {
> @@ -775,6 +872,11 @@ fail:
> 		brelse(frame->bh);
> 		frame--;
> 	}
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	/* Free up the memory allocated for EXT4 crypto */
> +	ext4_put_fname_crypto_ctx(&ctx);
> +#endif
> +
> 	if (ret_err == ERR_PTR(ERR_BAD_DX_DIR))
> 		ext4_warning(dir->i_sb,
> 			     "Corrupt dir inode %lu, running e2fsck is "
> @@ -1602,8 +1704,10 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
> 		initialize_dirent_tail(t, blocksize);
> 	}
> 
> -	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));
> -	dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1));
> +	dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data1,
> +			blocksize, 1));
> +	dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data2,
> +			blocksize, 1));
> 
> 	/* Which block gets the new entry? */
> 	if (hinfo->hash >= hash2) {
> @@ -1796,8 +1900,13 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
> 			    struct inode *inode, struct buffer_head *bh)
> {
> 	struct inode	*dir = dentry->d_parent->d_inode;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	struct ext4_fname_crypto_ctx *ctx = NULL;
> +	int res;
> +#else
> 	const char	*name = dentry->d_name.name;
> 	int		namelen = dentry->d_name.len;
> +#endif
> 	struct buffer_head *bh2;
> 	struct dx_root	*root;
> 	struct dx_frame	frames[2], *frame;
> @@ -1812,24 +1921,11 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
> 	ext4_lblk_t  block;
> 	struct fake_dirent *fde;
> 	int csum_size = 0;
> -#ifdef CONFIG_EXT4_FS_ENCRYPTION
> -	struct ext4_fname_crypto_ctx *ctx = NULL;
> -	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
> -	int res;
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> 	ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
> 	if (IS_ERR(ctx))
> -		return -1;
> -	if (ctx != NULL) {
> -		/* Allocate buffer to hold maximum name length */
> -		res = ext4_fname_crypto_alloc_buffer(ctx,
> -			&fname_crypto_str.name, &fname_crypto_str.len,
> -			EXT4_NAME_LEN);
> -		if (res < 0) {
> -			ext4_put_fname_crypto_ctx(&ctx);
> -			return -1;
> -		}
> -	}
> +		return PTR_ERR(ctx);
> #endif
> 
> 	if (ext4_has_metadata_csum(inode->i_sb))
> @@ -1898,27 +1994,14 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
> 		hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
> 	hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
> #ifdef CONFIG_EXT4_FS_ENCRYPTION
> -	if (ctx == NULL) {
> -		/* Directory is not encrypted */
> -		ext4fs_dirhash(name, namelen, &hinfo);
> -	} else {
> -		/* Directory is encrypted */
> -		res = ext4_fname_usr_to_htree(ctx, &dentry->d_name,
> -					      &fname_crypto_str);
> -		if (res < 0) {
> -			ext4_put_fname_crypto_ctx(&ctx);
> -			ext4_fname_crypto_free_buffer(
> -			    (void **)&fname_crypto_str.name);
> -			ext4_mark_inode_dirty(handle, dir);
> -			brelse(bh);
> -			return res;
> -		}
> -		ext4fs_dirhash(fname_crypto_str.name,
> -			       fname_crypto_str.len,
> -			       &hinfo);
> +	res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo);
> +	if (res < 0) {
> 		ext4_put_fname_crypto_ctx(&ctx);
> -		ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
> +		ext4_mark_inode_dirty(handle, dir);
> +		brelse(bh);
> +		return res;
> 	}
> +	ext4_put_fname_crypto_ctx(&ctx);
> #else
> 	ext4fs_dirhash(name, namelen, &hinfo);
> #endif
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 20/22] ext4 crypto: Add symlink encryption
  2015-04-02 22:10 ` [PATCH 20/22] ext4 crypto: Add symlink encryption Theodore Ts'o
@ 2015-04-08 17:58   ` Andreas Dilger
  2015-04-12  5:29     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-08 17:58 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Uday Savagaonkar

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> Change-Id: Ic92ebe4c615721650ccaf16b3175c2f4e931af2d
> Signed-off-by: Uday Savagaonkar <savagaon@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/ext4_crypto.h |  20 ++++++++++
> fs/ext4/namei.c       |  63 +++++++++++++++++++++++++++---
> fs/ext4/symlink.c     | 104 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 3 files changed, 180 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> index 68e95d8..4597530 100644
> --- a/fs/ext4/ext4_crypto.h
> +++ b/fs/ext4/ext4_crypto.h
> @@ -117,4 +117,24 @@ struct ext4_fname_crypto_ctx {
> 	unsigned ctfm_key_is_ready : 1;
> };
> 
> +/**
> + * For encrypted symlinks, the ciphertext length is stored at the beginning
> + * of the string in little-endian format.
> + */
> +struct ext4_encrypted_symlink_data {
> +	__le32 len;
> +	char encrypted_path[1];
> +} __attribute__((__packed__));

We can't have a symlink size larger than a block (or possibly PATH_MAX),
can we?  That would allow using __le16 for the symlink length, and
checkpatch.pl will complain about __attribute__((__packed__)) and
request the use of __packed instead.

> +
> +/**
> + * This function is used to calculate the disk space required to
> + * store a filename of length l in encrypted symlink format.
> + */
> +static inline u32 encrypted_symlink_data_len(u32 l)
> +{
> +	return ((l + EXT4_CRYPTO_BLOCK_SIZE - 1) / EXT4_CRYPTO_BLOCK_SIZE)
> +		* EXT4_CRYPTO_BLOCK_SIZE
> +		+ sizeof(struct ext4_encrypted_symlink_data) - 1;

Coding style has operators at the end of the line instead of the start.

> +}
> +
> #endif	/* _EXT4_CRYPTO_H */
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index 80a3979..57db793 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -3198,14 +3198,31 @@ static int ext4_symlink(struct inode *dir,
> 	struct inode *inode;
> 	int l, err, retries = 0;
> 	int credits;
> +	bool encryption_required = false;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	int l2;
> +	struct ext4_fname_crypto_ctx *ctx = NULL;
> +	struct qstr istr;
> +	struct ext4_str ostr;
> +	struct ext4_encrypted_symlink_data *sd = NULL;
> +	struct ext4_sb_info *sbi = EXT4_SB(dir->i_sb);
> +#endif
> 
> -	l = strlen(symname)+1;
> +	l = strlen(symname) + 1;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	l2 = encrypted_symlink_data_len(l - 1);
> +	encryption_required = (ext4_encrypted_inode(dir) ||
> +			       unlikely(sbi->s_mount_flags &
> +					EXT4_MF_TEST_DUMMY_ENCRYPTION));
> +	if (encryption_required && l2 > dir->i_sb->s_blocksize)
> +#else
> 	if (l > dir->i_sb->s_blocksize)
> +#endif
> 		return -ENAMETOOLONG;
> 
> 	dquot_initialize(dir);
> 
> -	if (l > EXT4_N_BLOCKS * 4) {
> +	if ((l > EXT4_N_BLOCKS * 4) || encryption_required) {
> 		/*
> 		 * For non-fast symlinks, we just allocate inode and put it on
> 		 * orphan list in the first transaction => we need bitmap,
> @@ -3233,7 +3250,7 @@ retry:
> 	if (IS_ERR(inode))
> 		goto out_stop;
> 
> -	if (l > EXT4_N_BLOCKS * 4) {
> +	if ((l > EXT4_N_BLOCKS * 4) || encryption_required) {
> 		inode->i_op = &ext4_symlink_inode_operations;
> 		ext4_set_aops(inode);
> 		/*
> @@ -3251,9 +3268,40 @@ retry:
> 		ext4_journal_stop(handle);
> 		if (err)
> 			goto err_drop_inode;
> -		err = __page_symlink(inode, symname, l, 1);
> -		if (err)
> -			goto err_drop_inode;
> +		if (!encryption_required) {
> +			err = __page_symlink(inode, symname, l, 1);
> +			if (err)
> +				goto err_drop_inode;
> +		}
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		else {
> +			sd = kmalloc(l2 + 1, GFP_NOFS);
> +			if (!sd) {
> +				err = -ENOMEM;
> +				goto err_drop_inode;
> +			}
> +			sd->encrypted_path[l2] = '\0';
> +			err = ext4_inherit_context(dir, inode);
> +			ctx = ext4_get_fname_crypto_ctx(
> +				inode, inode->i_sb->s_blocksize);
> +			if (IS_ERR_OR_NULL(ctx)) {
> +				/* We just set the policy, so ctx should
> +				   not be NULL */
> +				err = (ctx == NULL) ? -EIO : PTR_ERR(ctx);
> +				goto err_drop_inode;
> +			}
> +			istr.name = (const unsigned char *) symname;
> +			istr.len = l - 1;
> +			ostr.name = sd->encrypted_path;
> +			err = ext4_fname_usr_to_disk(ctx, &istr, &ostr);
> +			ext4_put_fname_crypto_ctx(&ctx);
> +			if (err < 0)
> +				goto err_drop_inode;
> +			sd->len = cpu_to_le32(ostr.len);
> +			err = __page_symlink(inode, (char *)sd, l2 + 1, 1);
> +

No blank line at the end of this scope.

Can "sd" moved inside this scope?  It doesn't appear to be used outside.

> +		}
> +#endif
> 		/*
> 		 * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
> 		 * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
> @@ -3293,6 +3341,9 @@ out_stop:
> err_drop_inode:
> 	unlock_new_inode(inode);
> 	iput(inode);
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	kfree(sd);
> +#endif
> 	return err;
> }
> 
> diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
> index ff37119..d788891 100644
> --- a/fs/ext4/symlink.c
> +++ b/fs/ext4/symlink.c
> @@ -22,9 +22,106 @@
> #include <linux/namei.h>
> #include "ext4.h"
> #include "xattr.h"
> +#include "ext4_crypto.h"
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
> {
> +	struct page *cpage = NULL;
> +	char *caddr, *paddr;
> +	struct ext4_str cstr, pstr;
> +	struct inode *inode = dentry->d_inode;
> +	struct ext4_fname_crypto_ctx *ctx = NULL;
> +	struct ext4_encrypted_symlink_data *sd;
> +	loff_t size = min(inode->i_size, (loff_t) PAGE_SIZE-1);

No space after typecast.  Should this use min_t() instead?

> +	int res;
> +	u32 plen, plen2;
> +
> +	ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize);
> +	if (IS_ERR(ctx))
> +		return ctx;
> +
> +	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
> +	if (IS_ERR(cpage)) {
> +		ext4_put_fname_crypto_ctx(&ctx);
> +		return cpage;
> +	}
> +	caddr = kmap(cpage);
> +	caddr[size] = 0;
> +
> +	if (!ctx) {
> +		/* Symlink is unencrypted */
> +		plen = strnlen((char *)caddr, inode->i_sb->s_blocksize);
> +		plen2 = (plen < inode->i_sb->s_blocksize) ? plen + 1 : plen;
> +		paddr = kmalloc(plen2, GFP_NOFS);
> +		if (!paddr) {
> +			ext4_put_fname_crypto_ctx(&ctx);
> +			kunmap(cpage);
> +			page_cache_release(cpage);
> +			return ERR_PTR(-ENOMEM);
> +		}
> +		memcpy(paddr, caddr, plen);
> +		if (plen < inode->i_sb->s_blocksize)
> +			paddr[plen] = '\0';
> +	} else {
> +		/* Symlink is encrypted */
> +		sd = (struct ext4_encrypted_symlink_data *)caddr;
> +		cstr.name = sd->encrypted_path;
> +		cstr.len  = le32_to_cpu(sd->len);
> +		if ((cstr.len + sizeof(struct ext4_encrypted_symlink_data) - 1)
> +			> inode->i_sb->s_blocksize) {

Operator at the end of the previous line.
Continued line should align after '(' from previous line.

> +			/* Symlink data on the disk is corrupted */
> +			kunmap(cpage);
> +			page_cache_release(cpage);
> +			return ERR_PTR(-EIO);
> +		}
> +		plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
> +			? EXT4_FNAME_CRYPTO_DIGEST_SIZE*2
> +			: cstr.len;

Operators at the end of the previous line.

> +		paddr = kmalloc(plen + 1, GFP_NOFS);
> +		if (!paddr) {
> +			ext4_put_fname_crypto_ctx(&ctx);
> +			kunmap(cpage);
> +			page_cache_release(cpage);
> +			return ERR_PTR(-ENOMEM);
> +		}
> +		pstr.name = paddr;
> +		res = _ext4_fname_disk_to_usr(ctx, &cstr, &pstr);
> +		if (res < 0) {
> +			ext4_put_fname_crypto_ctx(&ctx);
> +			kunmap(cpage);
> +			page_cache_release(cpage);
> +			kfree(paddr);
> +			return ERR_PTR(res);
> +		}
> +		/* Null-terminate the name */
> +		if (res <= plen)
> +			paddr[res] = '\0';
> +	}
> +	nd_set_link(nd, paddr);
> +	ext4_put_fname_crypto_ctx(&ctx);
> +	return cpage;
> +}
> +
> +static void ext4_put_link(struct dentry *dentry, struct nameidata *nd,
> +			  void *cookie)
> +{
> +	struct page *page = cookie;
> +	char *buf = nd_get_link(nd);
> +
> +	if (page) {
> +		kunmap(page);
> +		page_cache_release(page);
> +	}
> +	if (buf) {
> +		nd_set_link(nd, NULL);
> +		kfree(buf);
> +	}
> +}
> +#endif
> +
> +static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd)
> +{
> 	struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
> 	nd_set_link(nd, (char *) ei->i_data);
> 	return NULL;
> @@ -32,8 +129,13 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
> 
> const struct inode_operations ext4_symlink_inode_operations = {
> 	.readlink	= generic_readlink,
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	.follow_link    = ext4_follow_link,
> +	.put_link       = ext4_put_link,
> +#else

What about instantiating a different inode_operations struct for
encrypted symlinks?  That avoids the need to handle unencrypted
symlinks inline in ext4_follow_link(), and avoids overhead in the
more common unencrypted symlink case.  In that case, the name of
the new function should be ext4_follow_crypto_link() or similar.

> 	.follow_link	= page_follow_link_light,
> 	.put_link	= page_put_link,
> +#endif
> 	.setattr	= ext4_setattr,
> 	.setxattr	= generic_setxattr,
> 	.getxattr	= generic_getxattr,
> @@ -43,7 +145,7 @@ const struct inode_operations ext4_symlink_inode_operations = {
> 
> const struct inode_operations ext4_fast_symlink_inode_operations = {
> 	.readlink	= generic_readlink,
> -	.follow_link	= ext4_follow_link,
> +	.follow_link    = ext4_follow_fast_link,
> 	.setattr	= ext4_setattr,
> 	.setxattr	= generic_setxattr,
> 	.getxattr	= generic_getxattr,
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-02 22:10 ` [PATCH 06/22] ext4 crypto: add encryption policy checking Theodore Ts'o
  2015-04-06 21:31   ` Andreas Dilger
@ 2015-04-08 18:07   ` Andreas Dilger
  2015-04-11 13:10     ` Theodore Ts'o
  1 sibling, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-08 18:07 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> The ext4_crypto.h header will get fleshed out as later patches in this
> patchset add functionality.
> 
> Change-Id: I550d197184af04ed27e4c3abb759ca188a3f0de0
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
> ---
> fs/ext4/Makefile        |   1 +
> fs/ext4/crypto_policy.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/ext4.h          |   2 +
> fs/ext4/ext4_crypto.h   |  54 +++++++++++++++++
> 4 files changed, 212 insertions(+)
> create mode 100644 fs/ext4/crypto_policy.c
> create mode 100644 fs/ext4/ext4_crypto.h
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index cd6f50f..3886ee4 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,3 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
> 
> ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
> ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
> +ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o
> diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
> new file mode 100644
> index 0000000..5cb4e74
> --- /dev/null
> +++ b/fs/ext4/crypto_policy.c
> @@ -0,0 +1,155 @@
> +/*
> + * linux/fs/ext4/crypto_policy.c
> + *
> + * This contains encryption policy functions for ext4
> + *
> + * Written by Michael Halcrow, 2015.
> + */
> +
> +#include <linux/random.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#include "ext4.h"
> +#include "xattr.h"
> +
> +/**
> + * ext4_to_hex() - Converts to hexadecimal characters
> + * @dst: Buffer to take hex character representation of contents of
> + *       src. Must be at least of size (src_size * 2).
> + * @src: Buffer to be converted to a hex string respresentation.
> + * @src_size: Number of bytes to convert.
> + */
> +void ext4_to_hex(char *dst, char *src, size_t src_size)
> +{
> +	int x;
> +
> +	for (x = 0; x < src_size; x++)
> +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> +}
> +
> +/**
> + *
> + */
> +static int ext4_inode_has_encryption_context(struct inode *inode)
> +{
> +	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0);
> +	return (res > 0);
> +}
> +
> +/**
> + * ext4_is_encryption_context_consistent_with_policy() - Checks whether the policy is consistent with the encryption context for the inode
> + * @inode:  ...
> + * @policy: ...
> + *
> + * Return ...
> + */
> +static int ext4_is_encryption_context_consistent_with_policy(
> +	struct inode *inode, const struct ext4_encryption_policy *policy)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +				 sizeof(ctx));
> +	if (res != sizeof(ctx))
> +		return 0;
> +	return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
> +			EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(ctx.contents_encryption_mode ==
> +		 policy->contents_encryption_mode) &&
> +		(ctx.filenames_encryption_mode ==
> +		 policy->filenames_encryption_mode));
> +}
> +
> +static int ext4_create_encryption_context_from_policy(
> +	struct inode *inode, const struct ext4_encryption_policy *policy)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = 0;
> +
> +	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
> +	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
> +	       EXT4_KEY_DESCRIPTOR_SIZE);
> +	ctx.contents_encryption_mode = policy->contents_encryption_mode;
> +	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
> +	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +
> +	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +			     sizeof(ctx), 0);
> +	if (!res)
> +		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
> +	return res;
> +}
> +
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode)
> +{
> +	int res = 0;
> +
> +	if (!ext4_inode_has_encryption_context(inode)) {
> +		res = ext4_create_encryption_context_from_policy(inode, policy);
> +		goto out;
> +	}
> +
> +	if (!ext4_is_encryption_context_consistent_with_policy(inode, policy)) {
> +		printk(KERN_WARNING
> +		       "%s: Policy inconsistent with encryption context\n",
> +		       __func__);
> +		res = -EINVAL;
> +	}
> +out:
> +	return res;
> +}
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child)
> +{
> +	struct ext4_encryption_context parent_ctx, child_ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &parent_ctx, sizeof(parent_ctx));
> +
> +	if (res != sizeof(parent_ctx))
> +		return 0;
> +	res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +			     &child_ctx, sizeof(child_ctx));
> +	if (res != sizeof(child_ctx))
> +		return 0;
> +	return (memcmp(parent_ctx.master_key_descriptor,
> +		       child_ctx.master_key_descriptor,
> +		       EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
> +		(parent_ctx.contents_encryption_mode ==
> +		 child_ctx.contents_encryption_mode) &&
> +		(parent_ctx.filenames_encryption_mode ==
> +		 child_ctx.filenames_encryption_mode));
> +}
> +
> +/**
> + * ext4_inherit_context() - Sets a child context from its parent
> + * @parent: Parent inode from which the context is inherited.
> + * @child:  Child inode that inherits the context from @parent.
> + *
> + * Return: Zero on success, non-zero otherwise
> + */
> +int ext4_inherit_context(struct inode *parent, struct inode *child)
> +{
> +	struct ext4_encryption_context ctx;
> +	int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
> +				 EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
> +				 &ctx, sizeof(ctx));
> +
> +	if (res != sizeof(ctx))
> +		return -ENOENT;
> +
> +	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
> +	res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION,
> +			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
> +			     sizeof(ctx), 0);
> +	if (!res)
> +		ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT);
> +	return res;
> +}
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2f3808e..fd2f3dd 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -588,6 +588,8 @@ enum {
> #define EXT4_ENCRYPTION_MODE_AES_256_GCM	2
> #define EXT4_ENCRYPTION_MODE_AES_256_CBC	3
> 
> +#include "ext4_crypto.h"
> +
> /*
>  * ioctl commands
>  */
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> new file mode 100644
> index 0000000..984ff38
> --- /dev/null
> +++ b/fs/ext4/ext4_crypto.h
> @@ -0,0 +1,54 @@
> +/*
> + * linux/fs/ext4/ext4_crypto.h
> + *
> + * This contains encryption header content for ext4
> + *
> + * Written by Michael Halcrow, 2015.
> + */
> +
> +#ifndef _EXT4_CRYPTO_H
> +#define _EXT4_CRYPTO_H
> +
> +#include <linux/fs.h>
> +
> +#define EXT4_KEY_DESCRIPTOR_SIZE 8
> +
> +/* Policy provided via an ioctl on the topmost directory */
> +struct ext4_encryption_policy {
> +	char version;
> +	char contents_encryption_mode;
> +	char filenames_encryption_mode;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +} __attribute__((__packed__));

It wouldn't be bad to add a padding byte before master_key_descriptor,
even if this is only passed from the ioctl.  That allows casting the
key to a numeric value if desired without problems on some arches.

> +
> +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0

Per comments in the call, it is better not to use "0" as a defined
version, since this is much more likely to be seen accidentally
(e.g. uninitialized buffer, memory corruption, etc).  Better to
start with version 1.

> +#define EXT4_KEY_DERIVATION_NONCE_SIZE 16
> +
> +/**
> + * Encryption context for inode
> + *
> + * Protector format:
> + *  1 byte: Protector format (0 = this version)

Same here.

> + *  1 byte: File contents encryption mode
> + *  1 byte: File names encryption mode
> + *  1 byte: Reserved
> + *  8 bytes: Master Key descriptor
> + *  16 bytes: Encryption Key derivation nonce
> + */
> +struct ext4_encryption_context {
> +	char format;
> +	char contents_encryption_mode;
> +	char filenames_encryption_mode;
> +	char reserved;
> +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> +	char nonce[EXT4_KEY_DERIVATION_NONCE_SIZE];
> +} __attribute__((__packed__));
> +
> +int ext4_is_child_context_consistent_with_parent(struct inode *parent,
> +						 struct inode *child);
> +int ext4_inherit_context(struct inode *parent, struct inode *child);
> +void ext4_to_hex(char *dst, char *src, size_t src_size);
> +int ext4_process_policy(const struct ext4_encryption_policy *policy,
> +			struct inode *inode);
> +
> +#endif	/* _EXT4_CRYPTO_H */
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 19/22] ext4 crypto: enable filename encryption
  2015-04-02 22:10 ` [PATCH 19/22] ext4 crypto: enable filename encryption Theodore Ts'o
@ 2015-04-08 18:38   ` Andreas Dilger
  0 siblings, 0 replies; 44+ messages in thread
From: Andreas Dilger @ 2015-04-08 18:38 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Uday Savagaonkar,
	Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> Change-Id: I1057e08bf05741b963705f2850710ec5d7d8bd72
> Signed-off-by: Uday Savagaonkar <savagaon@google.com>
> Signed-off-by: Ildar Muslukhov <ildarm@google.com>
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/dir.c    | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
> fs/ext4/ialloc.c | 21 ++++++++++++--
> 2 files changed, 96 insertions(+), 8 deletions(-)
> 
> diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
> index f67f955..a77900f 100644
> --- a/fs/ext4/dir.c
> +++ b/fs/ext4/dir.c
> @@ -111,6 +111,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> 	struct inode *inode = file_inode(file);
> 	struct super_block *sb = inode->i_sb;
> 	int dir_has_error = 0;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	struct ext4_fname_crypto_ctx *enc_ctx = NULL;
> +	struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
> +#endif
> +	int res;

Having both "res" and "err" in the same function seems prone to mistakes.

> 
> 	if (is_dx_dir(inode)) {
> 		err = ext4_dx_readdir(file, ctx);
> @@ -127,11 +132,27 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> 
> 	if (ext4_has_inline_data(inode)) {
> 		int has_inline_data = 1;
> -		int ret = ext4_read_inline_dir(file, ctx,
> +		res = ext4_read_inline_dir(file, ctx,
> 					   &has_inline_data);
> 		if (has_inline_data)
> -			return ret;
> +			return res;

Is there any reason this can't use "err"?  Or for that matter, "ret" (or
more commonly "rc") could stay local to this scope since it isn't actually
used elsewhere.

> +	}
> +
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	enc_ctx = ext4_get_fname_crypto_ctx(inode, EXT4_NAME_LEN);
> +	if (IS_ERR(enc_ctx))
> +		return PTR_ERR(enc_ctx);
> +	if (enc_ctx) {
> +		res = ext4_fname_crypto_alloc_buffer(enc_ctx,
> +						     &fname_crypto_str.name,
> +						     &fname_crypto_str.len,
> +						     EXT4_NAME_LEN);
> +		if (res < 0) {
> +			ext4_put_fname_crypto_ctx(&enc_ctx);
> +			return res;
> +		}

Likewise here, "res" isn't used outside the scope of this if {} block
and could be declared locally.  That shouldn't increase stack usage, and
makes it more clear to the reader that this value isn't used elsewhere.

> 	}
> +#endif
> 
> 	offset = ctx->pos & (sb->s_blocksize - 1);
> 
> @@ -226,13 +247,53 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> 			offset += ext4_rec_len_from_disk(de->rec_len,
> 					sb->s_blocksize);
> 			if (le32_to_cpu(de->inode)) {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +				if (enc_ctx == NULL) {
> +					/* Directory is not encrypted */
> +					if (!dir_emit(ctx, de->name,
> +					    de->name_len,
> +					    le32_to_cpu(de->inode),
> +					    get_dtype(sb, de->file_type))) {
> +						ext4_put_fname_crypto_ctx(
> +							&enc_ctx);
> +						ext4_fname_crypto_free_buffer(
> +							(void **)&fname_crypto_str.name);
> +						brelse(bh);
> +						return 0;
> +					}

This #ifdef and code should be restructured to avoid duplicating the
unencrypted case.  Otherwise it is prone to bugs in one copy or the other.

> +				} else {
> +					/* Directory is encrypted */
> +					err = ext4_fname_disk_to_usr(enc_ctx,
> +							de, &fname_crypto_str);
> +					if (err < 0) {
> +						ext4_put_fname_crypto_ctx(
> +							&enc_ctx);
> +						ext4_fname_crypto_free_buffer(
> +							(void **)&fname_crypto_str.name);
> +						brelse(bh);
> +						return err;
> +					}

"err" isn't used outside the scope of this while {} block and could be
declared inside, preferably keeping the same name as the other local
temporary variables above.

> +					if (!dir_emit(ctx,
> +					    fname_crypto_str.name, err,
> +					    le32_to_cpu(de->inode),
> +					    get_dtype(sb, de->file_type))) {
> +						ext4_put_fname_crypto_ctx(
> +							&enc_ctx);
> +						ext4_fname_crypto_free_buffer(
> +							(void **)&fname_crypto_str.name);
> +						brelse(bh);
> +						return 0;
> +					}
> +				}
> +#else
> 				if (!dir_emit(ctx, de->name,
> -						de->name_len,
> -						le32_to_cpu(de->inode),
> -						get_dtype(sb, de->file_type))) {
> +					      de->name_len,
> +					      le32_to_cpu(de->inode),
> +					      get_dtype(sb, de->file_type))) {
> 					brelse(bh);
> 					return 0;
> 				}
> +#endif
> 			}
> 			ctx->pos += ext4_rec_len_from_disk(de->rec_len,
> 						sb->s_blocksize);
> @@ -240,10 +301,20 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
> 		offset = 0;
> 		brelse(bh);
> 		if (ctx->pos < inode->i_size) {
> -			if (!dir_relax(inode))
> +			if (!dir_relax(inode)) {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +				ext4_put_fname_crypto_ctx(&enc_ctx);
> +				ext4_fname_crypto_free_buffer(
> +						(void **)&fname_crypto_str.name);
> +#endif

Could ext4_put_fname_crypto_ctx() and ext4_fname_crypto_free_buffer()
be made no-ops in the !CONFIG_EXT4_FS_ENCRYPTION case?  That would avoid
the #ifdefs here.

In general, there are a LOT of #ifdefs being being added by this patch
series, and it would be good to avoid it as much as possible.

> 				return 0;
> +			}
> 		}
> 	}
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	ext4_put_fname_crypto_ctx(&enc_ctx);
> +	ext4_fname_crypto_free_buffer((void **)&fname_crypto_str.name);
> +#endif
> 	return 0;
> }
> 
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index e554ca3..8f37c9e 100644
> --- a/fs/ext4/ialloc.c
> +++ b/fs/ext4/ialloc.c
> @@ -1034,11 +1034,28 @@ got:
> 	ext4_set_inode_state(inode, EXT4_STATE_NEW);
> 
> 	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
> -
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if ((sbi->s_file_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) &&
> +	    (sbi->s_dir_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID)) {
> +		ei->i_inline_off = 0;
> +		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
> +			EXT4_FEATURE_INCOMPAT_INLINE_DATA))

This should be indented more, so it is more clear what is being continued.

> +			ext4_set_inode_state(inode,
> +			EXT4_STATE_MAY_INLINE_DATA);

Align after '(' so it is more clear what is being continued.

> +	} else {
> +		/* Inline data and encryption are incompatible
> +		 * We turn off inline data since encryption is enabled */
> +		ei->i_inline_off = 1;
> +		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
> +			EXT4_FEATURE_INCOMPAT_INLINE_DATA))
> +			ext4_clear_inode_state(inode,
> +			EXT4_STATE_MAY_INLINE_DATA);

Same.

> +	}
> +#else
> 	ei->i_inline_off = 0;
> 	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA))
> 		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);

It looks like the logic could be reversed here so that the non-encrypted
case doesn't need to be duplicated:

#ifdef CONFIG_EXT4_FS_ENCRYPTION
	if (file_mode != INVALID || dir_mode != INVALID) {
		/* Inline data and encryption are incompatible ... */
	} else
#endif
	{
		ei->i_inline_off = 0;
		if (EXT4_HAS_INCOMPAT_FEATURE(sb, ...)
			ext4_set_inode_state(inode, MAY_INLINE_DATA);
	}


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path
  2015-04-02 22:10 ` [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path Theodore Ts'o
@ 2015-04-08 18:51   ` Andreas Dilger
  2015-04-11 13:38     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-08 18:51 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> Change-Id: Ie9c043a132a01da60d1617662cd30307639f5599
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Ildar Muslukhov <ildarm@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/file.c     | 22 +++++++++++++++----
> fs/ext4/inode.c    | 10 +++++++++
> fs/ext4/readpage.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 3 files changed, 89 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> index 8131be8..4cacc30 100644
> --- a/fs/ext4/file.c
> +++ b/fs/ext4/file.c
> @@ -28,6 +28,7 @@
> #include <linux/pagevec.h>
> #include "ext4.h"
> #include "ext4_jbd2.h"
> +#include "ext4_crypto.h"
> #include "xattr.h"
> #include "acl.h"
> 
> @@ -200,9 +201,15 @@ static const struct vm_operations_struct ext4_file_vm_ops = {
> 
> static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
> {
> +	int res = 0;

This function is using "res", the one below "ret".  It would be nice to
have some consistency.  "rc" is probably the most common and would be
my preference for new/modified code.

> +	struct inode *inode = file->f_mapping->host;
> +
> +	if (ext4_encrypted_inode(inode))
> +		res = ext4_generate_encryption_key(inode);
> 	file_accessed(file);
> -	vma->vm_ops = &ext4_file_vm_ops;
> -	return 0;
> +	if (!res)
> +		vma->vm_ops = &ext4_file_vm_ops;
> +	return res;
> }
> 
> static int ext4_file_open(struct inode * inode, struct file * filp)
> @@ -212,6 +219,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
> 	struct vfsmount *mnt = filp->f_path.mnt;
> 	struct path path;
> 	char buf[64], *cp;
> +	int ret;
> 
> 	if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
> 		     !(sb->s_flags & MS_RDONLY))) {
> @@ -250,11 +258,17 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
> 	 * writing and the journal is present
> 	 */
> 	if (filp->f_mode & FMODE_WRITE) {
> -		int ret = ext4_inode_attach_jinode(inode);
> +		ret = ext4_inode_attach_jinode(inode);
> 		if (ret < 0)
> 			return ret;
> 	}
> -	return dquot_file_open(inode, filp);
> +	ret = dquot_file_open(inode, filp);
> +	if (!ret && ext4_encrypted_inode(inode)) {
> +		ret = ext4_generate_encryption_key(inode);
> +		if (ret)
> +			ret = -EACCES;
> +	}
> +	return ret;
> }
> 
> /*
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index dcc836c..b033405 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -39,6 +39,7 @@
> #include <linux/ratelimit.h>
> #include <linux/aio.h>
> #include <linux/bitops.h>
> +#include <linux/prefetch.h>
> 
> #include "ext4_jbd2.h"
> #include "ext4_crypto.h"
> @@ -3363,6 +3364,15 @@ static int ext4_block_zero_page_range(handle_t *handle,
> 		/* Uhhuh. Read error. Complain and punt. */
> 		if (!buffer_uptodate(bh))
> 			goto unlock;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		if (S_ISREG(inode->i_mode) &&
> +		    ext4_encrypted_inode(inode)) {
> +			/* We expect the key to be set. */
> +			BUG_ON(!ext4_has_encryption_key(inode));
> +			BUG_ON(blocksize != PAGE_CACHE_SIZE);
> +			WARN_ON_ONCE(ext4_decrypt_one(inode, page));
> +		}
> +#endif

This could avoid the #ifdef since ext4_encrypted_inode() is declared (0)
in the !CONFIG_EXT4_FS_ENCRYPTION case.  The compiler will optimize out
the resulting unreachable code in that case and make this code easier
to read.

> 	}
> 	if (ext4_should_journal_data(inode)) {
> 		BUFFER_TRACE(bh, "get write access");
> diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
> index 9ca4dfc..8978b1d 100644
> --- a/fs/ext4/readpage.c
> +++ b/fs/ext4/readpage.c
> @@ -43,6 +43,35 @@
> 
> #include "ext4.h"
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +/*
> + * Call ext4_decrypt on every single page, reusing the encryption
> + * context.
> + */
> +static void completion_pages(struct work_struct *work)
> +{
> +	struct ext4_crypto_ctx *ctx =
> +		container_of(work, struct ext4_crypto_ctx, work);
> +	struct bio	*bio	= ctx->bio;
> +	struct bio_vec	*bv;
> +	int		i;
> +
> +	bio_for_each_segment_all(bv, bio, i) {
> +		struct page *page = bv->bv_page;
> +
> +		int ret = ext4_decrypt(ctx, page);
> +		if (ret) {
> +			WARN_ON_ONCE(1);
> +			SetPageError(page);
> +		} else
> +			SetPageUptodate(page);
> +		unlock_page(page);
> +	}
> +	ext4_release_crypto_ctx(ctx);
> +	bio_put(bio);
> +}
> +#endif
> +
> /*
>  * I/O completion handler for multipage BIOs.
>  *
> @@ -60,6 +89,20 @@ static void mpage_end_io(struct bio *bio, int err)
> 	struct bio_vec *bv;
> 	int i;
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if (bio->bi_private) {

If the (bio->bi_private != NULL) check was moved to a helper function:

static inline bool ext4_bio_encrypted(struct bio *bio)
{
#ifdef CONFIG_EXT4_FS_ENCRYPTION
	return unlikely(bio->bi_private != NULL);
#else
	return false;
#endif
}

Then the inline #ifdefs could be removed here, since the resulting

	if (false) {
		...
	}

block would be optimized away by the caller.  It would also simplify
future changes if there is some other reason why bio->bi_private is
non-NULL besides crypto.

> +		struct ext4_crypto_ctx *ctx = bio->bi_private;
> +
> +		if (err)
> +			ext4_release_crypto_ctx(ctx);
> +		else {

The if/else should have matching {} blocks.

> +			INIT_WORK(&ctx->work, completion_pages);
> +			ctx->bio = bio;
> +			queue_work(ext4_read_workqueue, &ctx->work);
> +			return;
> +		}
> +	}
> +#endif
> 	bio_for_each_segment_all(bv, bio, i) {
> 		struct page *page = bv->bv_page;
> 
> @@ -94,9 +137,15 @@ int ext4_mpage_readpages(struct address_space *mapping,
> 	unsigned page_block;
> 	struct block_device *bdev = inode->i_sb->s_bdev;
> 	int length;
> +	int do_decryption = 0;

Can be bool instead of int.

> 	unsigned relative_block = 0;
> 	struct ext4_map_blocks map;
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
> +		do_decryption = 1;
> +#endif

#ifdef can be removed.

> +
> 	map.m_pblk = 0;
> 	map.m_lblk = 0;
> 	map.m_len = 0;
> @@ -220,13 +269,24 @@ int ext4_mpage_readpages(struct address_space *mapping,
> 			bio = NULL;
> 		}
> 		if (bio == NULL) {
> +			struct ext4_crypto_ctx *ctx = NULL;
> +
> +			if (do_decryption) {
> +				ctx = ext4_get_crypto_ctx(inode);
> +				if (IS_ERR(ctx))
> +					goto set_error_page;
> +			}
> 			bio = bio_alloc(GFP_KERNEL,
> 				min_t(int, nr_pages, bio_get_nr_vecs(bdev)));
> -			if (!bio)
> +			if (!bio) {
> +				if (ctx)
> +					ext4_release_crypto_ctx(ctx);
> 				goto set_error_page;
> +			}
> 			bio->bi_bdev = bdev;
> 			bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
> 			bio->bi_end_io = mpage_end_io;
> +			bio->bi_private = ctx;
> 		}
> 
> 		length = first_hole << blkbits;
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 08/22] ext4 crypto: add ext4 encryption facilities
  2015-04-02 22:10 ` [PATCH 08/22] ext4 crypto: add ext4 encryption facilities Theodore Ts'o
@ 2015-04-09 12:54   ` Maurizio Lombardi
  2015-04-11 12:50     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Maurizio Lombardi @ 2015-04-09 12:54 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Thu, 2015-04-02 at 18:10 -0400, Theodore Ts'o wrote:
> From: Michael Halcrow <mhalcrow@google.com>
> 
> On encrypt, we will re-assign the buffer_heads to point to a bounce
> page rather than the control_page (which is the original page to write
> that contains the plaintext). The block I/O occurs against the bounce
> page.  On write completion, we re-assign the buffer_heads to the
> original plaintext page.
> 
> On decrypt, we will attach a read completion callback to the bio
> struct. This read completion will decrypt the read contents in-place
> prior to setting the page up-to-date.
> 
> The current encryption mode, AES-256-XTS, lacks cryptographic
> integrity. AES-256-GCM is in-plan, but we will need to devise a
> mechanism for handling the integrity data.
> 
> Change-Id: I5ed4c913d49971d7f7e9b10bb4e694df86f960d7
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Ildar Muslukhov <ildarm@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
>  fs/ext4/Makefile        |   2 +-
>  fs/ext4/crypto.c        | 601 ++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/ext4/crypto_policy.c |  21 +-
>  fs/ext4/ext4.h          |  39 ++++
>  fs/ext4/ext4_crypto.h   |  43 ++++
>  fs/ext4/super.c         |  11 +
>  6 files changed, 714 insertions(+), 3 deletions(-)
>  create mode 100644 fs/ext4/crypto.c
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index 3886ee4..1b1c561 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,4 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
>  
>  ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
>  ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
> -ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o
> +ext4-$(CONFIG_EXT4_FS_ENCRYPTION)	+= crypto_policy.o crypto.o
> diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
> new file mode 100644
> index 0000000..5b62bb1
> --- /dev/null
> +++ b/fs/ext4/crypto.c
> @@ -0,0 +1,601 @@
> +/*
> + * linux/fs/ext4/crypto.c
> + *
> + * This contains encryption functions for ext4
> + *
> + * Written by Michael Halcrow, 2014.
> + *
> + * Filename encryption additions
> + *	Uday Savagaonkar, 2014
> + * Encryption policy handling additions
> + *	Ildar Muslukhov, 2014
> + *
> + * This has not yet undergone a rigorous security audit.
> + *
> + * The usage of AES-XTS should conform to recommendations in NIST
> + * Special Publication 800-38E. The usage of AES-GCM should conform to
> + * the recommendations in NIST Special Publication 800-38D. Further
> + * guidance for block-oriented storage is in IEEE P1619/D16. The key
> + * derivation code implements an HKDF (see RFC 5869).
> + */
> +
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> +#include <keys/user-type.h>
> +#include <keys/encrypted-type.h>
> +#include <linux/crypto.h>
> +#include <linux/ecryptfs.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/key.h>
> +#include <linux/list.h>
> +#include <linux/mempool.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/random.h>
> +#include <linux/scatterlist.h>
> +#include <linux/spinlock_types.h>
> +
> +#include "ext4.h"
> +#include "xattr.h"
> +
> +/* Encryption added and removed here! (L: */
> +
> +static unsigned int num_prealloc_crypto_pages = 32;
> +static unsigned int num_prealloc_crypto_ctxs = 128;
> +
> +module_param(num_prealloc_crypto_pages, uint, 0444);
> +MODULE_PARM_DESC(num_prealloc_crypto_pages,
> +		 "Number of crypto pages to preallocate");
> +module_param(num_prealloc_crypto_ctxs, uint, 0444);
> +MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
> +		 "Number of crypto contexts to preallocate");
> +
> +static mempool_t *ext4_bounce_page_pool;
> +
> +static LIST_HEAD(ext4_free_crypto_ctxs);
> +static DEFINE_SPINLOCK(ext4_crypto_ctx_lock);
> +
> +/**
> + * ext4_release_crypto_ctx() - Releases an encryption context
> + * @ctx: The encryption context to release.
> + *
> + * If the encryption context was allocated from the pre-allocated pool, returns
> + * it to that pool. Else, frees it.
> + *
> + * If there's a bounce page in the context, this frees that.
> + */
> +void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx)
> +{
> +	unsigned long flags;
> +
> +	if (ctx->bounce_page) {
> +		if (ctx->flags & EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
> +			__free_page(ctx->bounce_page);
> +		else
> +			mempool_free(ctx->bounce_page, ext4_bounce_page_pool);
> +		ctx->bounce_page = NULL;
> +	}
> +	ctx->control_page = NULL;
> +	if (ctx->flags & EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL) {
> +		if (ctx->tfm)
> +			crypto_free_tfm(ctx->tfm);
> +		kfree(ctx);
> +	} else {
> +		spin_lock_irqsave(&ext4_crypto_ctx_lock, flags);
> +		list_add(&ctx->free_list, &ext4_free_crypto_ctxs);
> +		spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags);
> +	}
> +}
> +
> +/**
> + * ext4_alloc_and_init_crypto_ctx() - Allocates and inits an encryption context
> + * @mask: The allocation mask.
> + *
> + * Return: An allocated and initialized encryption context on success. An error
> + * value or NULL otherwise.
> + */
> +static struct ext4_crypto_ctx *ext4_alloc_and_init_crypto_ctx(gfp_t mask)
> +{
> +	struct ext4_crypto_ctx *ctx = kzalloc(sizeof(struct ext4_crypto_ctx),
> +					      mask);
> +
> +	if (!ctx)
> +		return ERR_PTR(-ENOMEM);
> +	return ctx;
> +}
> +
> +/**
> + * ext4_get_crypto_ctx() - Gets an encryption context
> + * @inode:       The inode for which we are doing the crypto
> + *
> + * Allocates and initializes an encryption context.
> + *
> + * Return: An allocated and initialized encryption context on success; error
> + * value or NULL otherwise.
> + */
> +struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode)
> +{
> +	struct ext4_crypto_ctx *ctx = NULL;
> +	int res = 0;
> +	unsigned long flags;
> +	struct ext4_encryption_key *key = &EXT4_I(inode)->i_encryption_key;
> +
> +	/* We first try getting the ctx from a free list because in the common
> +	 * case the ctx will have an allocated and initialized crypto tfm, so
> +	 * it's probably a worthwhile optimization. For the bounce page, we
> +	 * first try getting it from the kernel allocator because that's just
> +	 * about as fast as getting it from a list and because a cache of free
> +	 * pages should generally be a "last resort" option for a filesystem to
> +	 * be able to do its job. */
> +	spin_lock_irqsave(&ext4_crypto_ctx_lock, flags);
> +	ctx = list_first_entry_or_null(&ext4_free_crypto_ctxs,
> +				       struct ext4_crypto_ctx, free_list);
> +	if (ctx)
> +		list_del(&ctx->free_list);
> +	spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags);
> +	if (!ctx) {
> +		ctx = ext4_alloc_and_init_crypto_ctx(GFP_NOFS);
> +		if (IS_ERR(ctx)) {
> +			res = PTR_ERR(ctx);
> +			goto out;
> +		}
> +		ctx->flags |= EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL;
> +	} else {
> +		ctx->flags &= ~EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL;
> +	}
> +
> +	/* Allocate a new Crypto API context if we don't already have one or if
> +	 * it isn't the right mode. */
> +	BUG_ON(key->mode == EXT4_ENCRYPTION_MODE_INVALID);
> +	if (ctx->tfm && (ctx->mode != key->mode)) {
> +		crypto_free_tfm(ctx->tfm);
> +		ctx->tfm = NULL;
> +		ctx->mode = EXT4_ENCRYPTION_MODE_INVALID;
> +	}
> +	if (!ctx->tfm) {
> +		switch (key->mode) {
> +		case EXT4_ENCRYPTION_MODE_AES_256_XTS:
> +			ctx->tfm = crypto_ablkcipher_tfm(
> +				crypto_alloc_ablkcipher("xts(aes)", 0, 0));
> +			break;
> +		case EXT4_ENCRYPTION_MODE_AES_256_GCM:
> +			/* TODO(mhalcrow): AEAD w/ gcm(aes);
> +			 * crypto_aead_setauthsize() */
> +			ctx->tfm = ERR_PTR(-ENOTSUPP);
> +			break;
> +		default:
> +			BUG();
> +		}
> +		if (IS_ERR_OR_NULL(ctx->tfm)) {
> +			res = PTR_ERR(ctx->tfm);
> +			ctx->tfm = NULL;
> +			goto out;
> +		}
> +		ctx->mode = key->mode;
> +	}
> +	BUG_ON(key->size != ext4_encryption_key_size(key->mode));
> +
> +	/* There shouldn't be a bounce page attached to the crypto
> +	 * context at this point. */
> +	BUG_ON(ctx->bounce_page);
> +
> +out:
> +	if (res) {
> +		if (!IS_ERR_OR_NULL(ctx))
> +			ext4_release_crypto_ctx(ctx);
> +		ctx = ERR_PTR(res);
> +	}
> +	return ctx;
> +}
> +
> +struct workqueue_struct *ext4_read_workqueue;
> +static DEFINE_MUTEX(crypto_init);
> +
> +/**
> + * ext4_exit_crypto() - Shutdown the ext4 encryption system
> + */
> +void ext4_exit_crypto(void)
> +{
> +	struct ext4_crypto_ctx *pos, *n;
> +
> +	list_for_each_entry_safe(pos, n, &ext4_free_crypto_ctxs, free_list) {
> +		if (pos->bounce_page) {
> +			if (pos->flags &
> +			    EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL) {
> +				__free_page(pos->bounce_page);
> +			} else {
> +				mempool_free(pos->bounce_page,
> +					     ext4_bounce_page_pool);
> +			}
> +		}
> +		if (pos->tfm)
> +			crypto_free_tfm(pos->tfm);
> +		kfree(pos);
> +	}
> +	INIT_LIST_HEAD(&ext4_free_crypto_ctxs);
> +	if (ext4_bounce_page_pool)
> +		mempool_destroy(ext4_bounce_page_pool);
> +	ext4_bounce_page_pool = NULL;
> +	if (ext4_read_workqueue)
> +		destroy_workqueue(ext4_read_workqueue);
> +	ext4_read_workqueue = NULL;
> +}
> +
> +/**
> + * ext4_init_crypto() - Set up for ext4 encryption.
> + *
> + * We call this when we mount a file system which has the encryption
> + * feature enabled, since it results in memory getting allocated that
> + * won't be used unless we are using encryption.
> + *
> + * Return: Zero on success, non-zero otherwise.
> + */
> +int ext4_init_crypto(void)
> +{
> +	int i, res = 0;
> +
> +	mutex_lock(&crypto_init);
> +	if (ext4_read_workqueue)
> +		goto already_initialized;
> +	ext4_read_workqueue = alloc_workqueue("ext4_crypto", WQ_HIGHPRI, 0);
> +	if (!ext4_read_workqueue) {
> +		res = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
> +		struct ext4_crypto_ctx *ctx;
> +
> +		ctx = ext4_alloc_and_init_crypto_ctx(GFP_KERNEL);
> +		if (IS_ERR(ctx)) {
> +			res = PTR_ERR(ctx);
> +			goto fail;
> +		}
> +		list_add(&ctx->free_list, &ext4_free_crypto_ctxs);
> +	}
> +
> +	ext4_bounce_page_pool =
> +		mempool_create_page_pool(num_prealloc_crypto_pages, 0);
> +	if (!ext4_bounce_page_pool)
> +		goto fail;
> +already_initialized:
> +	mutex_unlock(&crypto_init);
> +	return 0;
> +fail:
> +	ext4_exit_crypto();
> +	mutex_unlock(&crypto_init);
> +	return res;
> +}
> +
> +/**
> + * ext4_xts_tweak_for_page() - Generates an XTS tweak for a page
> + * @xts_tweak: Buffer into which this writes the XTS tweak.
> + * @page:      The page for which this generates a tweak.
> + *
> + * Generates an XTS tweak value for the given page.
> + */
> +static void ext4_xts_tweak_for_page(u8 xts_tweak[EXT4_XTS_TWEAK_SIZE],
> +				    const struct page *page)
> +{
> +	/* Only do this for XTS tweak values. For other modes (CBC,
> +	 * GCM, etc.), you most likely will need to do something
> +	 * different. */
> +	BUILD_BUG_ON(EXT4_XTS_TWEAK_SIZE < sizeof(page->index));
> +	memcpy(xts_tweak, &page->index, sizeof(page->index));
> +	memset(&xts_tweak[sizeof(page->index)], 0,
> +	       EXT4_XTS_TWEAK_SIZE - sizeof(page->index));
> +}
> +
> +void ext4_restore_control_page(struct page *data_page)
> +{
> +	struct ext4_crypto_ctx *ctx =
> +		(struct ext4_crypto_ctx *)page_private(data_page);
> +
> +	set_page_private(data_page, (unsigned long)NULL);
> +	ClearPagePrivate(data_page);
> +	unlock_page(data_page);
> +	ext4_release_crypto_ctx(ctx);
> +}
> +
> +struct ext4_crypt_result {
> +	struct completion completion;
> +	int res;
> +};
> +
> +/**
> + * ext4_crypt_complete() - The completion callback for page encryption
> + * @req: The asynchronous encryption request context
> + * @res: The result of the encryption operation
> + */
> +static void ext4_crypt_complete(struct crypto_async_request *req, int res)
> +{
> +	struct ext4_crypt_result *ecr = req->data;
> +
> +	if (res == -EINPROGRESS)
> +		return;
> +	ecr->res = res;
> +	complete(&ecr->completion);
> +}
> +
> +/**
> + * ext4_prep_pages_for_write() - Prepares pages for write
> + * @ciphertext_page: Ciphertext page that will actually be written.
> + * @plaintext_page:  Plaintext page that acts as a control page.
> + * @ctx:             Encryption context for the pages.
> + */
> +static void ext4_prep_pages_for_write(struct page *ciphertext_page,
> +				      struct page *plaintext_page,
> +				      struct ext4_crypto_ctx *ctx)
> +{
> +	SetPageDirty(ciphertext_page);
> +	SetPagePrivate(ciphertext_page);
> +	ctx->control_page = plaintext_page;
> +	set_page_private(ciphertext_page, (unsigned long)ctx);
> +	lock_page(ciphertext_page);
> +}
> +
> +/**
> + * ext4_xts_encrypt() - Encrypts a page using AES-256-XTS
> + * @ctx:            The encryption context.
> + * @plaintext_page: The page to encrypt. Must be locked.
> + *
> + * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
> + * encryption context. Uses AES-256-XTS.
> + *
> + * Called on the page write path.
> + *
> + * Return: An allocated page with the encrypted content on success. Else, an
> + * error value or NULL.
> + */
> +static struct page *ext4_xts_encrypt(struct ext4_crypto_ctx *ctx,
> +				     struct page *plaintext_page)
> +{
> +	struct page *ciphertext_page = ctx->bounce_page;
> +	u8 xts_tweak[EXT4_XTS_TWEAK_SIZE];
> +	struct ablkcipher_request *req = NULL;
> +	struct ext4_crypt_result ecr;
> +	struct scatterlist dst, src;
> +	struct ext4_inode_info *ei = EXT4_I(plaintext_page->mapping->host);
> +	struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm);
> +	int res = 0;
> +
> +	BUG_ON(!ciphertext_page);
> +	BUG_ON(!ctx->tfm);
> +	BUG_ON(ei->i_encryption_key.mode != EXT4_ENCRYPTION_MODE_AES_256_XTS);
> +	crypto_ablkcipher_clear_flags(atfm, ~0);
> +	crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
> +
> +	/* Since in AES-256-XTS mode we only perform one cryptographic operation
> +	 * on each block and there are no constraints about how many blocks a
> +	 * single key can encrypt, we directly use the inode master key */
> +	res = crypto_ablkcipher_setkey(atfm, ei->i_encryption_key.raw,
> +				       ei->i_encryption_key.size);
> +	req = ablkcipher_request_alloc(atfm, GFP_NOFS);
> +	if (!req) {
> +		printk_ratelimited(KERN_ERR
> +				   "%s: crypto_request_alloc() failed\n",
> +				   __func__);
> +		ciphertext_page = ERR_PTR(-ENOMEM);
> +		goto out;
> +	}
> +	ablkcipher_request_set_callback(
> +		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
> +		ext4_crypt_complete, &ecr);
> +	ext4_xts_tweak_for_page(xts_tweak, plaintext_page);
> +	sg_init_table(&dst, 1);
> +	sg_set_page(&dst, ciphertext_page, PAGE_CACHE_SIZE, 0);
> +	sg_init_table(&src, 1);
> +	sg_set_page(&src, plaintext_page, PAGE_CACHE_SIZE, 0);
> +	ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
> +				     xts_tweak);
> +	res = crypto_ablkcipher_encrypt(req);
> +	if (res == -EINPROGRESS || res == -EBUSY) {
> +		BUG_ON(req->base.data != &ecr);
> +		wait_for_completion(&ecr.completion);
> +		res = ecr.res;
> +	}
> +	ablkcipher_request_free(req);
> +	if (res) {
> +		printk_ratelimited(
> +			KERN_ERR
> +			"%s: crypto_ablkcipher_encrypt() returned %d\n",
> +			__func__, res);
> +		ciphertext_page = ERR_PTR(res);
> +		goto out;
> +	}
> +out:
> +	return ciphertext_page;
> +}
> +
> +/**
> + * ext4_encrypt() - Encrypts a page
> + * @ctx:            The encryption context.
> + * @plaintext_page: The page to encrypt. Must be locked.
> + *
> + * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
> + * encryption context.
> + *
> + * Called on the page write path.
> + *
> + * Return: An allocated page with the encrypted content on success. Else, an
> + * error value or NULL.
> + */
> +struct page *ext4_encrypt(struct inode *inode,
> +			  struct page *plaintext_page)
> +{
> +	struct ext4_crypto_ctx *ctx;
> +	struct page *ciphertext_page = NULL;
> +
> +	BUG_ON(!PageLocked(plaintext_page));
> +
> +	ctx = ext4_get_crypto_ctx(inode);
> +	if (IS_ERR(ctx))
> +		return (struct page *) ctx;
> +
> +	/* The encryption operation will require a bounce page. */
> +	ctx->bounce_page = alloc_page(GFP_NOFS);
> +	if (!ctx->bounce_page) {
> +		/* This is a potential bottleneck, but at least we'll have
> +		 * forward progress. */
> +		ctx->bounce_page = mempool_alloc(ext4_bounce_page_pool,
> +						 GFP_NOFS);
> +		if (WARN_ON_ONCE(!ctx->bounce_page)) {
> +			ctx->bounce_page = mempool_alloc(ext4_bounce_page_pool,
> +							 GFP_NOFS | __GFP_WAIT);
> +		}
> +		ctx->flags &= ~EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
> +	} else {
> +		ctx->flags |= EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
> +	}
> +
> +	switch (ctx->mode) {
> +	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
> +		ciphertext_page = ext4_xts_encrypt(ctx, plaintext_page);
> +		break;
> +	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
> +		/* TODO(mhalcrow): We'll need buffers for the
> +		 * generated IV and/or auth tag for this mode and the
> +		 * ones below */
> +		ciphertext_page = ERR_PTR(-ENOTSUPP);
> +		break;
> +	default:
> +		BUG();
> +	}
> +	if (IS_ERR_OR_NULL(ciphertext_page))
> +		ext4_release_crypto_ctx(ctx);
> +	else
> +		ext4_prep_pages_for_write(ciphertext_page, plaintext_page, ctx);
> +	return ciphertext_page;
> +}
> +
> +/**
> + * ext4_xts_decrypt() - Decrypts a page using AES-256-XTS
> + * @ctx:  The encryption context.
> + * @page: The page to decrypt. Must be locked.
> + *
> + * Return: Zero on success, non-zero otherwise.
> + */
> +static int ext4_xts_decrypt(struct ext4_crypto_ctx *ctx, struct page *page)
> +{
> +	u8 xts_tweak[EXT4_XTS_TWEAK_SIZE];
> +	struct ablkcipher_request *req = NULL;
> +	struct ext4_crypt_result ecr;
> +	struct scatterlist sg;
> +	struct ext4_inode_info *ei = EXT4_I(page->mapping->host);
> +	struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm);
> +	int res = 0;
> +
> +	BUG_ON(!ctx->tfm);
> +	BUG_ON(ei->i_encryption_key.mode != EXT4_ENCRYPTION_MODE_AES_256_XTS);
> +	crypto_ablkcipher_clear_flags(atfm, ~0);
> +	crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
> +
> +	/* Since in AES-256-XTS mode we only perform one cryptographic operation
> +	 * on each block and there are no constraints about how many blocks a
> +	 * single key can encrypt, we directly use the inode master key */
> +	res = crypto_ablkcipher_setkey(atfm, ei->i_encryption_key.raw,
> +				       ei->i_encryption_key.size);

The return value of crypto_ablkcipher_setkey() is not checked for errors.

> +	req = ablkcipher_request_alloc(atfm, GFP_NOFS);
> +	if (!req) {
> +		res = -ENOMEM;
> +		goto out;
> +	}
> +	ablkcipher_request_set_callback(
> +		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
> +		ext4_crypt_complete, &ecr);
> +	ext4_xts_tweak_for_page(xts_tweak, page);
> +	sg_init_table(&sg, 1);
> +	sg_set_page(&sg, page, PAGE_CACHE_SIZE, 0);
> +	ablkcipher_request_set_crypt(req, &sg, &sg, PAGE_CACHE_SIZE, xts_tweak);
> +	res = crypto_ablkcipher_decrypt(req);
> +	if (res == -EINPROGRESS || res == -EBUSY) {
> +		BUG_ON(req->base.data != &ecr);
> +		wait_for_completion(&ecr.completion);
> +		res = ecr.res;
> +	}
> +	ablkcipher_request_free(req);
> +out:
> +	if (res)
> +		printk_ratelimited(KERN_ERR "%s: res = %d\n", __func__, res);
> +	return res;
> +}
> +
> +/**
> + * ext4_decrypt() - Decrypts a page in-place
> + * @ctx:  The encryption context.
> + * @page: The page to decrypt. Must be locked.
> + *
> + * Decrypts page in-place using the ctx encryption context.
> + *
> + * Called from the read completion callback.
> + *
> + * Return: Zero on success, non-zero otherwise.
> + */
> +int ext4_decrypt(struct ext4_crypto_ctx *ctx, struct page *page)
> +{
> +	int res = 0;
> +
> +	BUG_ON(!PageLocked(page));
> +
> +	switch (ctx->mode) {
> +	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
> +		res = ext4_xts_decrypt(ctx, page);
> +		break;
> +	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
> +		res = -ENOTSUPP;
> +		break;
> +	default:
> +		BUG();
> +	}
> +	return res;
> +}
> +
> +/*
> + * Convenience function which takes care of allocating and
> + * deallocating the encryption context
> + */
> +int ext4_decrypt_one(struct inode *inode, struct page *page)
> +{
> +	int ret;
> +
> +	struct ext4_crypto_ctx *ctx = ext4_get_crypto_ctx(inode);
> +	if (!ctx)
> +		return -ENOMEM;
> +	ret = ext4_decrypt(ctx, page);
> +	ext4_release_crypto_ctx(ctx);
> +	return ret;
> +}
> +
> +/**
> + * ext4_validate_encryption_mode() - Validates the encryption key mode
> + * @mode: The key mode to validate.
> + *
> + * Return: The validated key mode. EXT4_ENCRYPTION_MODE_INVALID if invalid.
> + */
> +uint32_t ext4_validate_encryption_mode(uint32_t mode)
> +{
> +	switch (mode) {
> +	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
> +		return mode;
> +	case EXT4_ENCRYPTION_MODE_AES_256_CBC:
> +		return mode;
> +	default:
> +		break;
> +	}
> +	return EXT4_ENCRYPTION_MODE_INVALID;
> +}
> +
> +/**
> + * ext4_validate_encryption_key_size() - Validate the encryption key size
> + * @mode: The key mode.
> + * @size: The key size to validate.
> + *
> + * Return: The validated key size for @mode. Zero if invalid.
> + */
> +uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size)
> +{
> +	if (size == ext4_encryption_key_size(mode))
> +		return size;
> +	return 0;
> +}
> diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
> index 5cb4e74..3ff4c75 100644
> --- a/fs/ext4/crypto_policy.c
> +++ b/fs/ext4/crypto_policy.c
> @@ -71,14 +71,31 @@ static int ext4_create_encryption_context_from_policy(
>  	ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V0;
>  	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
>  	       EXT4_KEY_DESCRIPTOR_SIZE);
> -	ctx.contents_encryption_mode = policy->contents_encryption_mode;
> -	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
> +	ctx.contents_encryption_mode = ext4_validate_encryption_mode(
> +		policy->contents_encryption_mode);
> +	if (ctx.contents_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) {
> +		printk(KERN_WARNING
> +		       "%s: Invalid contents encryption mode %d\n", __func__,
> +			policy->contents_encryption_mode);
> +		res = -EINVAL;
> +		goto out;
> +	}
> +	ctx.filenames_encryption_mode = ext4_validate_encryption_mode(
> +		policy->filenames_encryption_mode);
> +	if (ctx.filenames_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) {
> +		printk(KERN_WARNING
> +		       "%s: Invalid filenames encryption mode %d\n", __func__,
> +			policy->filenames_encryption_mode);
> +		res = -EINVAL;
> +		goto out;
> +	}
>  	BUILD_BUG_ON(sizeof(ctx.nonce) != EXT4_KEY_DERIVATION_NONCE_SIZE);
>  	get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE);
>  
>  	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
>  			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
>  			     sizeof(ctx), 0);
> +out:
>  	if (!res)
>  		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
>  	return res;
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2d7fcb6..f7ee6c0 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -948,6 +948,11 @@ struct ext4_inode_info {
>  
>  	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
>  	__u32 i_csum_seed;
> +
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	/* Encryption params */
> +	struct ext4_encryption_key i_encryption_key;
> +#endif
>  };
>  
>  /*
> @@ -1349,6 +1354,12 @@ struct ext4_sb_info {
>  	struct ratelimit_state s_err_ratelimit_state;
>  	struct ratelimit_state s_warning_ratelimit_state;
>  	struct ratelimit_state s_msg_ratelimit_state;
> +
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	/* Encryption */
> +	uint32_t s_file_encryption_mode;
> +	uint32_t s_dir_encryption_mode;
> +#endif
>  };
>  
>  static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
> @@ -1998,6 +2009,34 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
>  					      struct ext4_group_desc *gdp);
>  ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
>  
> +/* crypto.c */
> +uint32_t ext4_validate_encryption_mode(uint32_t mode);
> +uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
> +extern struct workqueue_struct *ext4_read_workqueue;
> +struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode);
> +void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx);
> +void ext4_restore_control_page(struct page *data_page);
> +struct page *ext4_encrypt(struct inode *inode,
> +			  struct page *plaintext_page);
> +int ext4_decrypt(struct ext4_crypto_ctx *ctx, struct page *page);
> +int ext4_decrypt_one(struct inode *inode, struct page *page);
> +
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +int ext4_init_crypto(void);
> +void ext4_exit_crypto(void);
> +static inline int ext4_sb_has_crypto(struct super_block *sb)
> +{
> +	return EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT);
> +}
> +#else
> +static inline int ext4_init_crypto(void) { return 0; }
> +static inline void ext4_exit_crypto(void) { }
> +static inline int ext4_sb_has_crypto(struct super_block *sb)
> +{
> +	return 0;
> +}
> +#endif
> +
>  /* dir.c */
>  extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
>  				  struct file *,
> diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
> index 984ff38..fb73935 100644
> --- a/fs/ext4/ext4_crypto.h
> +++ b/fs/ext4/ext4_crypto.h
> @@ -51,4 +51,47 @@ void ext4_to_hex(char *dst, char *src, size_t src_size);
>  int ext4_process_policy(const struct ext4_encryption_policy *policy,
>  			struct inode *inode);
>  
> +/* Encryption parameters */
> +#define EXT4_AES_256_XTS_KEY_SIZE 64
> +#define EXT4_XTS_TWEAK_SIZE 16
> +#define EXT4_AES_128_ECB_KEY_SIZE 16
> +#define EXT4_AES_256_GCM_KEY_SIZE 32
> +#define EXT4_AES_256_CBC_KEY_SIZE 32
> +#define EXT4_MAX_KEY_SIZE 64
> +
> +struct ext4_encryption_key {
> +	uint32_t mode;
> +	char raw[EXT4_MAX_KEY_SIZE];
> +	uint32_t size;
> +};
> +
> +#define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL             0x00000001
> +#define EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL     0x00000002
> +
> +struct ext4_crypto_ctx {
> +	struct crypto_tfm *tfm;         /* Crypto API context */
> +	struct page *bounce_page;       /* Ciphertext page on write path */
> +	struct page *control_page;      /* Original page on write path */
> +	struct bio *bio;                /* The bio for this context */
> +	struct work_struct work;        /* Work queue for read complete path */
> +	struct list_head free_list;     /* Free list */
> +	int flags;                      /* Flags */
> +	int mode;                       /* Encryption mode for tfm */
> +};
> +
> +static inline int ext4_encryption_key_size(int mode)
> +{
> +	switch (mode) {
> +	case EXT4_ENCRYPTION_MODE_AES_256_XTS:
> +		return EXT4_AES_256_XTS_KEY_SIZE;
> +	case EXT4_ENCRYPTION_MODE_AES_256_GCM:
> +		return EXT4_AES_256_GCM_KEY_SIZE;
> +	case EXT4_ENCRYPTION_MODE_AES_256_CBC:
> +		return EXT4_AES_256_CBC_KEY_SIZE;
> +	default:
> +		BUG();
> +	}
> +	return 0;
> +}
> +
>  #endif	/* _EXT4_CRYPTO_H */
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 74c5f53..3dcafe9 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -893,6 +893,9 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
>  	atomic_set(&ei->i_ioend_count, 0);
>  	atomic_set(&ei->i_unwritten, 0);
>  	INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	ei->i_encryption_key.mode = EXT4_ENCRYPTION_MODE_INVALID;
> +#endif
>  
>  	return &ei->vfs_inode;
>  }
> @@ -3439,6 +3442,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	if (sb->s_bdev->bd_part)
>  		sbi->s_sectors_written_start =
>  			part_stat_read(sb->s_bdev->bd_part, sectors[1]);
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	/* Modes of operations for file and directory encryption. */
> +	sbi->s_file_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
> +	sbi->s_dir_encryption_mode = EXT4_ENCRYPTION_MODE_INVALID;
> +#endif
>  
>  	/* Cleanup superblock name */
>  	for (cp = sb->s_id; (cp = strchr(cp, '/'));)
> @@ -4052,6 +4060,9 @@ no_journal:
>  		goto failed_mount4;
>  	}
>  
> +	if (ext4_sb_has_crypto(sb))
> +		ext4_init_crypto();
> +
>  	/*
>  	 * The jbd2_journal_load will have done any necessary log recovery,
>  	 * so we can safely mount the rest of the filesystem now.




^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path
  2015-04-02 22:10 ` [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path Theodore Ts'o
@ 2015-04-09 21:44   ` Andreas Dilger
  2015-04-11 13:17     ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Andreas Dilger @ 2015-04-09 21:44 UTC (permalink / raw)
  To: Theodore Ts'o
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> From: Michael Halcrow <mhalcrow@google.com>
> 
> Pulls block_write_begin() into fs/ext4/inode.c because it might need
> to do a low-level read of the existing data, in which case we need to
> decrypt it.
> 
> Change-Id: I2337918809c43e18454a1d5621024d2699a98666
> Signed-off-by: Michael Halcrow <mhalcrow@google.com>
> Signed-off-by: Ildar Muslukhov <ildarm@google.com>
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/extents.c |   6 +++
> fs/ext4/ialloc.c  |   5 +++
> fs/ext4/inode.c   | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> fs/ext4/page-io.c |  46 +++++++++++++++++++---
> 4 files changed, 164 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index bed4308..1f1c0ea 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -4922,6 +4922,12 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
> 	ext4_lblk_t lblk;
> 	unsigned int blkbits = inode->i_blkbits;
> 
> +	/*
> +	 * TODO: We don't yet support fallocate with encrypted files.
> +	 */
> +	if (ext4_encrypted_inode(inode))
> +		return -EOPNOTSUPP;

Any plans for this?  Would reading from encrypted files with unwritten
extents just generate garbage from urandom if the key wasn't available,
or would it still return zero?

> 	/* Return error if mode is not supported */
> 	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
> 		     FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index ac644c3..e554ca3 100644
> --- a/fs/ext4/ialloc.c
> +++ b/fs/ext4/ialloc.c
> @@ -997,6 +997,11 @@ got:
> 	ei->i_block_group = group;
> 	ei->i_last_alloc_group = ~0;
> 
> +	/* If the directory encrypted, then we should encrypt the inode. */
> +	if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) &&
> +	    ext4_encrypted_inode(dir))
> +		ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
> +
> 	ext4_set_inode_flags(inode);
> 	if (IS_DIRSYNC(inode))
> 		ext4_handle_sync(handle);
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index a68cacc..dcc836c 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -41,6 +41,7 @@
> #include <linux/bitops.h>
> 
> #include "ext4_jbd2.h"
> +#include "ext4_crypto.h"
> #include "xattr.h"
> #include "acl.h"
> #include "truncate.h"
> @@ -871,6 +872,95 @@ int do_journal_get_write_access(handle_t *handle,
> 
> static int ext4_get_block_write_nolock(struct inode *inode, sector_t iblock,
> 		   struct buffer_head *bh_result, int create);
> +
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
> +				  get_block_t *get_block)
> +{
> +	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
> +	unsigned to = from + len;
> +	struct inode *inode = page->mapping->host;
> +	unsigned block_start, block_end;
> +	sector_t block;
> +	int err = 0;
> +	unsigned blocksize = inode->i_sb->s_blocksize;
> +	unsigned bbits;
> +	struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
> +	bool decrypt = false;
> +
> +	BUG_ON(!PageLocked(page));
> +	BUG_ON(from > PAGE_CACHE_SIZE);
> +	BUG_ON(to > PAGE_CACHE_SIZE);
> +	BUG_ON(from > to);
> +
> +	if (!page_has_buffers(page))
> +		create_empty_buffers(page, blocksize, 0);
> +	head = page_buffers(page);
> +	bbits = ilog2(blocksize);
> +	block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits);
> +
> +	for (bh = head, block_start = 0; bh != head || !block_start;
> +	    block++, block_start = block_end, bh = bh->b_this_page) {
> +		block_end = block_start + blocksize;
> +		if (block_end <= from || block_start >= to) {
> +			if (PageUptodate(page)) {
> +				if (!buffer_uptodate(bh))
> +					set_buffer_uptodate(bh);
> +			}
> +			continue;
> +		}
> +		if (buffer_new(bh))
> +			clear_buffer_new(bh);
> +		if (!buffer_mapped(bh)) {
> +			WARN_ON(bh->b_size != blocksize);
> +			err = get_block(inode, block, bh, 1);
> +			if (err)
> +				break;
> +			if (buffer_new(bh)) {
> +				unmap_underlying_metadata(bh->b_bdev,
> +							  bh->b_blocknr);
> +				if (PageUptodate(page)) {
> +					clear_buffer_new(bh);
> +					set_buffer_uptodate(bh);
> +					mark_buffer_dirty(bh);
> +					continue;
> +				}
> +				if (block_end > to || block_start < from)
> +					zero_user_segments(page, to, block_end,
> +							   block_start, from);
> +				continue;
> +			}
> +		}
> +		if (PageUptodate(page)) {
> +			if (!buffer_uptodate(bh))
> +				set_buffer_uptodate(bh);
> +			continue;
> +		}
> +		if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
> +		    !buffer_unwritten(bh) &&
> +		    (block_start < from || block_end > to)) {
> +			ll_rw_block(READ, 1, &bh);
> +			*wait_bh++ = bh;
> +			decrypt = ext4_encrypted_inode(inode) &&
> +				S_ISREG(inode->i_mode);

Surely "decrypt" doesn't need to be decided on a per-buffer basis?

> +		}
> +	}
> +	/*
> +	 * If we issued read requests, let them complete.
> +	 */
> +	while (wait_bh > wait) {
> +		wait_on_buffer(*--wait_bh);
> +		if (!buffer_uptodate(*wait_bh))
> +			err = -EIO;
> +	}
> +	if (unlikely(err))
> +		page_zero_new_buffers(page, from, to);
> +	else if (decrypt)
> +		err = ext4_decrypt_one(inode, page);
> +	return err;
> +}
> +#endif
> +
> static int ext4_write_begin(struct file *file, struct address_space *mapping,
> 			    loff_t pos, unsigned len, unsigned flags,
> 			    struct page **pagep, void **fsdata)
> @@ -933,11 +1023,19 @@ retry_journal:
> 	/* In case writeback began while the page was unlocked */
> 	wait_for_stable_page(page);
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if (ext4_should_dioread_nolock(inode))
> +		ret = ext4_block_write_begin(page, pos, len,
> +					     ext4_get_block_write);
> +	else
> +		ret = ext4_block_write_begin(page, pos, len,
> +					     ext4_get_block);
> +#else
> 	if (ext4_should_dioread_nolock(inode))
> 		ret = __block_write_begin(page, pos, len, ext4_get_block_write);
> 	else
> 		ret = __block_write_begin(page, pos, len, ext4_get_block);
> -
> +#endif
> 	if (!ret && ext4_should_journal_data(inode)) {
> 		ret = ext4_walk_page_buffers(handle, page_buffers(page),
> 					     from, to, NULL,
> @@ -2552,7 +2650,12 @@ retry_journal:
> 	/* In case writeback began while the page was unlocked */
> 	wait_for_stable_page(page);
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	ret = ext4_block_write_begin(page, pos, len,
> +				     ext4_da_get_block_prep);
> +#else
> 	ret = __block_write_begin(page, pos, len, ext4_da_get_block_prep);
> +#endif
> 	if (ret < 0) {
> 		unlock_page(page);
> 		ext4_journal_stop(handle);
> @@ -3010,6 +3113,9 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
> 		get_block_func = ext4_get_block_write;
> 		dio_flags = DIO_LOCKING;
> 	}
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
> +#endif

No #ifdef needed since this is constant false in the non-crypto case.

> 	ret = __blockdev_direct_IO(rw, iocb, inode,
> 				   inode->i_sb->s_bdev, iter,
> 				   offset,
> @@ -3073,6 +3179,11 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
> 	size_t count = iov_iter_count(iter);
> 	ssize_t ret;
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
> +		return 0;
> +#endif

Same.  No #ifdef needed here

> 	/*
> 	 * If we are doing data journalling we don't support O_DIRECT
> 	 */
> diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
> index b24a254..71fb0e6 100644
> --- a/fs/ext4/page-io.c
> +++ b/fs/ext4/page-io.c
> @@ -28,6 +28,7 @@
> #include <linux/ratelimit.h>
> 
> #include "ext4_jbd2.h"
> +#include "ext4_crypto.h"
> #include "xattr.h"
> #include "acl.h"
> 
> @@ -69,6 +70,10 @@ static void ext4_finish_bio(struct bio *bio)
> 
> 	bio_for_each_segment_all(bvec, bio, i) {
> 		struct page *page = bvec->bv_page;
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		struct page *data_page = NULL;
> +		struct ext4_crypto_ctx *ctx = NULL;
> +#endif
> 		struct buffer_head *bh, *head;
> 		unsigned bio_start = bvec->bv_offset;
> 		unsigned bio_end = bio_start + bvec->bv_len;
> @@ -78,6 +83,15 @@ static void ext4_finish_bio(struct bio *bio)
> 		if (!page)
> 			continue;
> 
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +		if (!page->mapping) {
> +			/* The bounce data pages are unmapped. */
> +			data_page = page;
> +			ctx = (struct ext4_crypto_ctx *)page_private(data_page);
> +			page = ctx->control_page;
> +		}
> +#endif
> +
> 		if (error) {
> 			SetPageError(page);
> 			set_bit(AS_EIO, &page->mapping->flags);
> @@ -102,8 +116,13 @@ static void ext4_finish_bio(struct bio *bio)
> 		} while ((bh = bh->b_this_page) != head);
> 		bit_spin_unlock(BH_Uptodate_Lock, &head->b_state);
> 		local_irq_restore(flags);
> -		if (!under_io)
> +		if (!under_io) {
> +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> +			if (ctx)
> +				ext4_restore_control_page(data_page);
> +#endif
> 			end_page_writeback(page);
> +		}
> 	}
> }
> 
> @@ -378,6 +397,7 @@ static int io_submit_init_bio(struct ext4_io_submit *io,
> 
> static int io_submit_add_bh(struct ext4_io_submit *io,
> 			    struct inode *inode,
> +			    struct page *page,
> 			    struct buffer_head *bh)
> {
> 	int ret;
> @@ -391,7 +411,7 @@ submit_and_retry:
> 		if (ret)
> 			return ret;
> 	}
> -	ret = bio_add_page(io->io_bio, bh->b_page, bh->b_size, bh_offset(bh));
> +	ret = bio_add_page(io->io_bio, page, bh->b_size, bh_offset(bh));
> 	if (ret != bh->b_size)
> 		goto submit_and_retry;
> 	io->io_next_block++;
> @@ -404,6 +424,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
> 			struct writeback_control *wbc,
> 			bool keep_towrite)
> {
> +	struct page *data_page = NULL;
> 	struct inode *inode = page->mapping->host;
> 	unsigned block_start, blocksize;
> 	struct buffer_head *bh, *head;
> @@ -463,19 +484,29 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
> 		set_buffer_async_write(bh);
> 	} while ((bh = bh->b_this_page) != head);
> 
> -	/* Now submit buffers to write */
> 	bh = head = page_buffers(page);
> +
> +	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
> +		data_page = ext4_encrypt(inode, page);
> +		if (IS_ERR(data_page)) {
> +			ret = PTR_ERR(data_page);
> +			data_page = NULL;
> +			goto out;
> +		}
> +	}
> +
> +	/* Now submit buffers to write */
> 	do {
> 		if (!buffer_async_write(bh))
> 			continue;
> -		ret = io_submit_add_bh(io, inode, bh);
> +		ret = io_submit_add_bh(io, inode,
> +				       data_page ? data_page : page, bh);
> 		if (ret) {
> 			/*
> 			 * We only get here on ENOMEM.  Not much else
> 			 * we can do but mark the page as dirty, and
> 			 * better luck next time.
> 			 */
> -			redirty_page_for_writepage(wbc, page);
> 			break;
> 		}
> 		nr_submitted++;
> @@ -484,6 +515,11 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
> 
> 	/* Error stopped previous loop? Clean up buffers... */
> 	if (ret) {
> +	out:
> +		if (data_page)
> +			ext4_restore_control_page(data_page);
> +		printk_ratelimited(KERN_ERR "%s: ret = %d\n", __func__, ret);
> +		redirty_page_for_writepage(wbc, page);
> 		do {
> 			clear_buffer_async_write(bh);
> 			bh = bh->b_this_page;
> -- 
> 2.3.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 08/22] ext4 crypto: add ext4 encryption facilities
  2015-04-09 12:54   ` Maurizio Lombardi
@ 2015-04-11 12:50     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 12:50 UTC (permalink / raw)
  To: Maurizio Lombardi
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Thu, Apr 09, 2015 at 02:54:42PM +0200, Maurizio Lombardi wrote:
> 
> The return value of crypto_ablkcipher_setkey() is not checked for errors.

Thanks for pointing this out!  I'll fix this in the next spin of the
patches.

						- Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-06 21:31   ` Andreas Dilger
@ 2015-04-11 13:06     ` Theodore Ts'o
  2015-04-11 13:18       ` Theodore Ts'o
  0 siblings, 1 reply; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 13:06 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Mon, Apr 06, 2015 at 03:31:49PM -0600, Andreas Dilger wrote:
> > +/**
> > + * ext4_to_hex() - Converts to hexadecimal characters
> > + * @dst: Buffer to take hex character representation of contents of
> > + *       src. Must be at least of size (src_size * 2).
> > + * @src: Buffer to be converted to a hex string respresentation.
> > + * @src_size: Number of bytes to convert.
> > + */
> > +void ext4_to_hex(char *dst, char *src, size_t src_size)
> > +{
> > +	int x;
> > +
> > +	for (x = 0; x < src_size; x++)
> > +		sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]);
> > +}
> 
> I think there is already some code in printk() to handle this?  Looking
> at vsnprintf->hex_string() it looks like "%*ph" would print out up to 64
> bytes as hex.

As it turns out we're not even using ext4_to_hex() any more, so we can
even more simply just delete this whole function.  :-)

							- Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-08 18:07   ` Andreas Dilger
@ 2015-04-11 13:10     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 13:10 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Wed, Apr 08, 2015 at 12:07:16PM -0600, Andreas Dilger wrote:
> > +/* Policy provided via an ioctl on the topmost directory */
> > +struct ext4_encryption_policy {
> > +	char version;
> > +	char contents_encryption_mode;
> > +	char filenames_encryption_mode;
> > +	char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
> > +} __attribute__((__packed__));
> 
> It wouldn't be bad to add a padding byte before master_key_descriptor,
> even if this is only passed from the ioctl.  That allows casting the
> key to a numeric value if desired without problems on some arches.

This data structure is only used for the ioctl interface.  The on-disk
format is the struct ext4_encryption_context, where we do have the
padding.

> > +#define EXT4_ENCRYPTION_CONTEXT_FORMAT_V0 0
> 
> Per comments in the call, it is better not to use "0" as a defined
> version, since this is much more likely to be seen accidentally
> (e.g. uninitialized buffer, memory corruption, etc).  Better to
> start with version 1.

I'll make this change.

						- Ted


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path
  2015-04-09 21:44   ` Andreas Dilger
@ 2015-04-11 13:17     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 13:17 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Thu, Apr 09, 2015 at 03:44:35PM -0600, Andreas Dilger wrote:
> > 
> > +	/*
> > +	 * TODO: We don't yet support fallocate with encrypted files.
> > +	 */
> > +	if (ext4_encrypted_inode(inode))
> > +		return -EOPNOTSUPP;
> 
> Any plans for this?  Would reading from encrypted files with unwritten
> extents just generate garbage from urandom if the key wasn't available,
> or would it still return zero?

This is on my todo list to fix.  It actually mostly works today,
except for when we write into the middle of an unwritten extent, under
some circumstances the extent splitting code will simply write zeros
to some of the unwritten pages to avoid needing to split a leaf node.
So what we need to do is implement a function which encrypts the zero
page and writes that out instead of just calling sb_issue_zeroout().

     	 	     	 	    - Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 06/22] ext4 crypto: add encryption policy checking
  2015-04-11 13:06     ` Theodore Ts'o
@ 2015-04-11 13:18       ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 13:18 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Sat, Apr 11, 2015 at 09:06:37AM -0400, Theodore Ts'o wrote:
> As it turns out we're not even using ext4_to_hex() any more, so we can
> even more simply just delete this whole function.  :-)

Whoops, spoke to soon; I'll just add the sprintf call directly into
fs/ext4/crypto_key.c.

						- Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path
  2015-04-08 18:51   ` Andreas Dilger
@ 2015-04-11 13:38     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-11 13:38 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Ildar Muslukhov

On Wed, Apr 08, 2015 at 12:51:04PM -0600, Andreas Dilger wrote:
> > @@ -200,9 +201,15 @@ static const struct vm_operations_struct ext4_file_vm_ops = {
> > 
> > static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
> > {
> > +	int res = 0;
> 
> This function is using "res", the one below "ret".  It would be nice to
> have some consistency.  "rc" is probably the most common and would be
> my preference for new/modified code.

Good point; I've restructured this code to make it simpler (and to
avoid conflicting with the DAX patches).  So it now looks like this:

	if (ext4_encrypted_inode(inode)) {
		int err = ext4_generate_encryption_key(inode);
		if (err)
			return 0;
	}

(I'm not a fan of rc, myself, but this makes the declaration and use
of the function much closer together than what we had before, which
makes it much easier to understand.  I'm guessing Microsoft had a
coding policy which stated that all functions have to only have one
return function at the end of the function, since I've noticed that
both Ildar and Michael seem to code that way, even when it requires
using goto statements and/or making the code less clear.)

> > +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> > +		if (S_ISREG(inode->i_mode) &&
> > +		    ext4_encrypted_inode(inode)) {
> > +			/* We expect the key to be set. */
> > +			BUG_ON(!ext4_has_encryption_key(inode));
> > +			BUG_ON(blocksize != PAGE_CACHE_SIZE);
> > +			WARN_ON_ONCE(ext4_decrypt_one(inode, page));
> > +		}
> > +#endif
> 
> This could avoid the #ifdef since ext4_encrypted_inode() is declared (0)
> in the !CONFIG_EXT4_FS_ENCRYPTION case.  The compiler will optimize out
> the resulting unreachable code in that case and make this code easier
> to read.

Good point, done.

> If the (bio->bi_private != NULL) check was moved to a helper function:
> 
> static inline bool ext4_bio_encrypted(struct bio *bio)
> {
> #ifdef CONFIG_EXT4_FS_ENCRYPTION
> 	return unlikely(bio->bi_private != NULL);
> #else
> 	return false;
> #endif
> }
> 
> Then the inline #ifdefs could be removed here, since the resulting

Sure, that makes the code a bit more readable, thanks.

> > +		if (err)
> > +			ext4_release_crypto_ctx(ctx);
> > +		else {
> 
> The if/else should have matching {} blocks.

Meh, I don't really think it's that important, but sure.

> > @@ -94,9 +137,15 @@ int ext4_mpage_readpages(struct address_space *mapping,
> > 	unsigned page_block;
> > 	struct block_device *bdev = inode->i_sb->s_bdev;
> > 	int length;
> > +	int do_decryption = 0;
> 
> Can be bool instead of int.

Actually, since we only use do_decryption in one place now, I'll just remove this and this:

> > +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> > +	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
> > +		do_decryption = 1;
> > +#endif
> #ifdef can be removed.

And move the condition to the one place where we test for do_decryption.

Thanks,

					- Ted

    

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto
  2015-04-08 17:44   ` Andreas Dilger
@ 2015-04-12  5:06     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-12  5:06 UTC (permalink / raw)
  To: Andreas Dilger
  Cc: Ext4 Developers List, jaegeuk, mhalcrow, Uday Savagaonkar,
	Ildar Muslukhov

On Wed, Apr 08, 2015 at 11:44:05AM -0600, Andreas Dilger wrote:
> > +				if (ctx == NULL) {
> > +					/* Directory is not encrypted */
> > +					while (len--)
> > +						printk("%c", *name++);
> 
> This is a bit strange, why not use "printk("%*.s", len, name)"?

This was copied from grotty debug code that no one has really cleaned
up in a long time.  That's a good idea, thanks.

      	     	    	     	  - Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 20/22] ext4 crypto: Add symlink encryption
  2015-04-08 17:58   ` Andreas Dilger
@ 2015-04-12  5:29     ` Theodore Ts'o
  0 siblings, 0 replies; 44+ messages in thread
From: Theodore Ts'o @ 2015-04-12  5:29 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List, jaegeuk, mhalcrow, Uday Savagaonkar

On Wed, Apr 08, 2015 at 11:58:02AM -0600, Andreas Dilger wrote:
> On Apr 2, 2015, at 4:10 PM, Theodore Ts'o <tytso@mit.edu> wrote:
> > +/**
> > + * For encrypted symlinks, the ciphertext length is stored at the beginning
> > + * of the string in little-endian format.
> > + */
> > +struct ext4_encrypted_symlink_data {
> > +	__le32 len;
> > +	char encrypted_path[1];
> > +} __attribute__((__packed__));
> 
> We can't have a symlink size larger than a block (or possibly PATH_MAX),
> can we?  That would allow using __le16 for the symlink length, and
> checkpatch.pl will complain about __attribute__((__packed__)) and
> request the use of __packed instead.

Yes, although it's a bit pointless since right now we don't support
fast encrypted symlinks.  What we should probably do is to switch to
using CTS, and then add fast encrypted symlinks, and then use __le16
here.

When did checkpatch start complaining about
__attribute__((__packed__))?  It's not complaining for me.

> > +static inline u32 encrypted_symlink_data_len(u32 l)
> > +{
> > +	return ((l + EXT4_CRYPTO_BLOCK_SIZE - 1) / EXT4_CRYPTO_BLOCK_SIZE)
> > +		* EXT4_CRYPTO_BLOCK_SIZE
> > +		+ sizeof(struct ext4_encrypted_symlink_data) - 1;
> 
> Coding style has operators at the end of the line instead of the start.

Thanks, fixed.

> > +		else {
> > +			sd = kmalloc(l2 + 1, GFP_NOFS);
    			   ...
> 
> Can "sd" moved inside this scope?  It doesn't appear to be used outside.

Good point, thanks.

> > diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
> > index ff37119..d788891 100644
> > --- a/fs/ext4/symlink.c
> > +++ b/fs/ext4/symlink.c
> > @@ -22,9 +22,106 @@
> > #include <linux/namei.h>
> > #include "ext4.h"
> > #include "xattr.h"
> > +#include "ext4_crypto.h"
> > 
> > +#ifdef CONFIG_EXT4_FS_ENCRYPTION
> > static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
> > {
> > +	struct page *cpage = NULL;
> > +	char *caddr, *paddr;
> > +	struct ext4_str cstr, pstr;
> > +	struct inode *inode = dentry->d_inode;
> > +	struct ext4_fname_crypto_ctx *ctx = NULL;
> > +	struct ext4_encrypted_symlink_data *sd;
> > +	loff_t size = min(inode->i_size, (loff_t) PAGE_SIZE-1);
> 
> No space after typecast.  Should this use min_t() instead?

I'll change it to use min_t, thanks.

> > +		if ((cstr.len + sizeof(struct ext4_encrypted_symlink_data) - 1)
> > +			> inode->i_sb->s_blocksize) {
> 
> Operator at the end of the previous line.
> Continued line should align after '(' from previous line.

Thanks, will fix.

> > +		plen = (cstr.len < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2)
> > +			? EXT4_FNAME_CRYPTO_DIGEST_SIZE*2
> > +			: cstr.len;
> 
> Operators at the end of the previous line.

Thanks, will fix.

						- Ted

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2015-04-12  5:29 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-02 22:10 [PATCH 00/22] ext4 encryption patches Theodore Ts'o
2015-04-02 22:10 ` [PATCH 01/22] ext4: add ext4_mpage_readpages() Theodore Ts'o
2015-04-06 21:08   ` Andreas Dilger
2015-04-08  3:04     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 02/22] ext4: reserve codepoints used by the ext4 encryption feature Theodore Ts'o
2015-04-02 22:10 ` [PATCH 03/22] ext4 crypto: add ext4 encryption Kconfig Theodore Ts'o
2015-04-02 22:10 ` [PATCH 04/22] ext4 crypto: export ext4_empty_dir() Theodore Ts'o
2015-04-02 22:10 ` [PATCH 05/22] ext4 crypto: add encryption xattr support Theodore Ts'o
2015-04-02 22:10 ` [PATCH 06/22] ext4 crypto: add encryption policy checking Theodore Ts'o
2015-04-06 21:31   ` Andreas Dilger
2015-04-11 13:06     ` Theodore Ts'o
2015-04-11 13:18       ` Theodore Ts'o
2015-04-08 18:07   ` Andreas Dilger
2015-04-11 13:10     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 07/22] ext4 crypto: add ioctl to set encryption policy Theodore Ts'o
2015-04-02 22:10 ` [PATCH 08/22] ext4 crypto: add ext4 encryption facilities Theodore Ts'o
2015-04-09 12:54   ` Maurizio Lombardi
2015-04-11 12:50     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 09/22] ext4 crypto: add encryption key management facilities Theodore Ts'o
2015-04-02 22:10 ` [PATCH 10/22] ext4 crypto: validate context consistency on lookup Theodore Ts'o
2015-04-02 22:10 ` [PATCH 11/22] ext4 crypto: inherit encryption policies on inode and directory create Theodore Ts'o
2015-04-02 22:10 ` [PATCH 12/22] ext4 crypto: implement the ext4 encryption write path Theodore Ts'o
2015-04-09 21:44   ` Andreas Dilger
2015-04-11 13:17     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 13/22] ext4 crypto: implement the ext4 decryption read path Theodore Ts'o
2015-04-08 18:51   ` Andreas Dilger
2015-04-11 13:38     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 14/22] ext4 crypto: filename encryption facilities Theodore Ts'o
2015-04-02 22:10 ` [PATCH 15/22] ext4: teach ext4_htree_store_dirent() to store decrypted filenames Theodore Ts'o
2015-04-02 22:10 ` [PATCH 16/22] ext4 crypto: insert encrypted filenames into a leaf directory block Theodore Ts'o
2015-04-02 22:10 ` [PATCH 17/22] ext4 crypto: partial update to namei.c for fname crypto Theodore Ts'o
2015-04-08 17:44   ` Andreas Dilger
2015-04-12  5:06     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 18/22] ext4 crypto: filename encryption modifications Theodore Ts'o
2015-04-02 22:10 ` [PATCH 19/22] ext4 crypto: enable filename encryption Theodore Ts'o
2015-04-08 18:38   ` Andreas Dilger
2015-04-02 22:10 ` [PATCH 20/22] ext4 crypto: Add symlink encryption Theodore Ts'o
2015-04-08 17:58   ` Andreas Dilger
2015-04-12  5:29     ` Theodore Ts'o
2015-04-02 22:10 ` [PATCH 21/22] ext4 crypto: enable encryption feature flag Theodore Ts'o
2015-04-02 22:10 ` [PATCH 22/22] ext4 crypto: add password salt support Theodore Ts'o
2015-04-03  1:57 ` [PATCH 00/22] ext4 encryption patches Theodore Ts'o
2015-04-06 20:28 ` Jonathan Corbet
2015-04-08  3:07   ` Theodore Ts'o

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.