lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20190109012701.26441-12-mark@harmstone.com>
Date:   Wed,  9 Jan 2019 01:26:54 +0000
From:   Mark Harmstone <mark@...mstone.com>
To:     unlisted-recipients:; (no To-header on input)
Cc:     mark@...mstone.com, Chris Mason <clm@...com>,
        Josef Bacik <josef@...icpanda.com>,
        David Sterba <dsterba@...e.com>, linux-btrfs@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents

Signed-off-by: Mark Harmstone <mark@...mstone.com>
---
 fs/btrfs/ctree.h      |  6 +++
 fs/btrfs/encryption.c | 37 +++++++++++++++++
 fs/btrfs/encryption.h |  3 ++
 fs/btrfs/extent_io.c  | 96 ++++++++++++++++++++++++++++++++++++++++---
 fs/btrfs/extent_io.h  |  2 +
 fs/btrfs/extent_map.c | 16 +++++++-
 fs/btrfs/extent_map.h |  5 +++
 fs/btrfs/file-item.c  | 15 ++++++-
 fs/btrfs/inode.c      | 10 ++++-
 fs/btrfs/volumes.h    |  2 +
 10 files changed, 184 insertions(+), 8 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 51fcc24047f8..4c5b8e8a8580 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2420,6 +2420,12 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
 BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
 		   other_encoding, 16);
 
+BTRFS_SETGET_FUNCS(file_extent_enc_key_number,
+		   struct btrfs_file_extent_item_enc, key_number, 64);
+
+BTRFS_SETGET_FUNCS(file_extent_inline_enc_key_number,
+		   struct btrfs_file_extent_inline_enc, key_number, 64);
+
 #define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64
 
 struct btrfs_encryption_key_item {
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 81313c4378b4..41c001339cc7 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -312,6 +312,43 @@ int btrfs_decrypt(struct btrfs_fs_info *fs_info,
 	return ret;
 }
 
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+		       u64 key_number, char *iv)
+{
+	struct scatterlist sg;
+	struct skcipher_request *req = NULL;
+	char *kaddr;
+	int ret = -EFAULT;
+	struct btrfs_enc_key *key;
+
+	ret = find_key(fs_info, key_number, &key);
+	if (ret)
+		return ret;
+
+	req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+	if (!req) {
+		pr_info("could not allocate skcipher request\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	kaddr = kmap_atomic(page);
+
+	sg_init_one(&sg, kaddr, PAGE_SIZE);
+	skcipher_request_set_crypt(req, &sg, &sg, PAGE_SIZE, iv);
+
+	ret = crypto_skcipher_decrypt(req);
+
+	kunmap_atomic(kaddr);
+
+	if (ret < 0)
+		goto out;
+
+out:
+	if (req)
+		skcipher_request_free(req);
+	return ret;
+}
 
 int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
 		     char *key_id)
diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h
index dbc035a880a5..0d24dc51793c 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -33,6 +33,9 @@ struct btrfs_enc_key {
 int btrfs_decrypt(struct btrfs_fs_info *fs_info,
 		  unsigned char *data, size_t len);
 
+int btrfs_decrypt_page(struct btrfs_fs_info *fs_info, struct page *page,
+		       u64 key_number, char *iv);
+
 int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
 		     char *key_id);
 
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index d228f706ff3e..73fb0af50da8 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -23,6 +23,7 @@
 #include "rcu-string.h"
 #include "backref.h"
 #include "disk-io.h"
+#include "encryption.h"
 
 static struct kmem_cache *extent_state_cache;
 static struct kmem_cache *extent_buffer_cache;
@@ -2490,6 +2491,26 @@ endio_readpage_release_extent(struct extent_io_tree *tree, u64 start, u64 len,
 	unlock_extent_cached_atomic(tree, start, end, &cached);
 }
 
+void btrfs_increment_iv(char *iv, u32 inc)
+{
+	u64 i1, ni1, i2;
+
+	if (inc == 0)
+		return;
+
+	i1 = be64_to_cpu(*(u64 *)(iv + sizeof(u64)));
+	ni1 = i1 + inc;
+
+	*(u64 *)(iv + sizeof(u64)) = cpu_to_be64(ni1);
+
+	if (ni1 > i1)
+		return;
+
+	i2 = be64_to_cpu(*(u64 *)iv);
+	i2++;
+	*(u64 *)iv = cpu_to_be64(i2);
+}
+
 /*
  * after a readpage IO is done, we need to:
  * clear the uptodate bits on error
@@ -2606,11 +2627,32 @@ static void end_bio_extent_readpage(struct bio *bio)
 			pgoff_t end_index = i_size >> PAGE_SHIFT;
 			unsigned off;
 
+			if (io_bio->key_number != 0) {
+				char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+				memcpy(iv, io_bio->iv,
+				       BTRFS_ENCRYPTION_BLOCK_LENGTH);
+				btrfs_increment_iv(iv,
+				i * PAGE_SIZE / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+				ret = btrfs_decrypt_page(fs_info, page,
+						   io_bio->key_number, iv);
+			} else {
+				ret = 0;
+			}
+
 			/* Zero out the end if this page straddles i_size */
 			off = i_size & (PAGE_SIZE-1);
 			if (page->index == end_index && off)
 				zero_user_segment(page, off, PAGE_SIZE);
-			SetPageUptodate(page);
+
+			if (ret) {
+				uptodate = 0;
+				ClearPageUptodate(page);
+				SetPageError(page);
+			} else {
+				SetPageUptodate(page);
+			}
 		} else {
 			ClearPageUptodate(page);
 			SetPageError(page);
@@ -2752,6 +2794,8 @@ static int __must_check submit_one_bio(struct bio *bio, int mirror_num,
  * @mirror_num:	     desired mirror to read/write
  * @prev_bio_flags:  flags of previous bio to see if we can merge the current one
  * @bio_flags:	flags of the current bio to see if we can merge them
+ * @key_number:	number of the encryption key (0 if not encrypted)
+ * @iv:		encryption initialization vector
  */
 static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
 			      struct writeback_control *wbc,
@@ -2763,7 +2807,8 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
 			      int mirror_num,
 			      unsigned long prev_bio_flags,
 			      unsigned long bio_flags,
-			      bool force_bio_submit)
+			      bool force_bio_submit, u64 key_number,
+			      char *iv)
 {
 	int ret = 0;
 	struct bio *bio;
@@ -2786,6 +2831,25 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
 						      bio, bio_flags))
 			can_merge = false;
 
+		if (prev_bio_flags == bio_flags && contig &&
+			can_merge && !force_bio_submit) {
+			struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+			if (key_number != io_bio->key_number) {
+				can_merge = false;
+			} else if (key_number != 0) {
+				char iv2[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+				memcpy(iv2, io_bio->iv,
+				       BTRFS_ENCRYPTION_BLOCK_LENGTH);
+				btrfs_increment_iv(iv2, bio->bi_iter.bi_size /
+					BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+				can_merge = !memcmp(iv, iv2,
+						 BTRFS_ENCRYPTION_BLOCK_LENGTH);
+			}
+		}
+
 		if (prev_bio_flags != bio_flags || !contig || !can_merge ||
 		    force_bio_submit ||
 		    bio_add_page(bio, page, page_size, pg_offset) < page_size) {
@@ -2813,6 +2877,13 @@ static int submit_extent_page(unsigned int opf, struct extent_io_tree *tree,
 		wbc_account_io(wbc, page, page_size);
 	}
 
+	if (key_number != 0) {
+		struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
+
+		io_bio->key_number = key_number;
+		memcpy(io_bio->iv, iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+	}
+
 	*bio_ret = bio;
 
 	return ret;
@@ -2924,6 +2995,8 @@ static int __do_readpage(struct extent_io_tree *tree,
 	while (cur <= end) {
 		bool force_bio_submit = false;
 		u64 offset;
+		char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+		u64 key_number = 0;
 
 		if (cur >= last_byte) {
 			char *userpage;
@@ -2951,6 +3024,9 @@ static int __do_readpage(struct extent_io_tree *tree,
 		BUG_ON(extent_map_end(em) <= cur);
 		BUG_ON(end < cur);
 
+		if (test_bit(EXTENT_FLAG_ENCRYPTED, &em->flags))
+			this_bio_flag |= EXTENT_BIO_ENCRYPTED;
+
 		if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
 			this_bio_flag |= EXTENT_BIO_COMPRESSED;
 			extent_set_compress_type(&this_bio_flag,
@@ -3014,6 +3090,16 @@ static int __do_readpage(struct extent_io_tree *tree,
 		if (prev_em_start)
 			*prev_em_start = em->orig_start;
 
+		if (this_bio_flag & EXTENT_BIO_ENCRYPTED &&
+			!(this_bio_flag & EXTENT_BIO_COMPRESSED)) {
+			key_number = em->key_number;
+			memcpy(iv, em->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+			if (extent_offset != 0)
+				btrfs_increment_iv(iv,
+				extent_offset / BTRFS_ENCRYPTION_BLOCK_LENGTH);
+		}
+
 		free_extent_map(em);
 		em = NULL;
 
@@ -3061,7 +3147,7 @@ static int __do_readpage(struct extent_io_tree *tree,
 					 end_bio_extent_readpage, mirror_num,
 					 *bio_flags,
 					 this_bio_flag,
-					 force_bio_submit);
+					 force_bio_submit, key_number, iv);
 		if (!ret) {
 			nr++;
 			*bio_flags = this_bio_flag;
@@ -3425,7 +3511,7 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode,
 					 page, offset, iosize, pg_offset,
 					 bdev, &epd->bio,
 					 end_bio_extent_writepage,
-					 0, 0, 0, false);
+					 0, 0, 0, false, 0, NULL);
 		if (ret) {
 			SetPageError(page);
 			if (PageWriteback(page))
@@ -3738,7 +3824,7 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb,
 					 p, offset, PAGE_SIZE, 0, bdev,
 					 &epd->bio,
 					 end_bio_extent_buffer_writepage,
-					 0, 0, 0, false);
+					 0, 0, 0, false, 0, NULL);
 		if (ret) {
 			set_btree_ioerr(p);
 			if (PageWriteback(p))
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 369daa5d4f73..54a7d6864d05 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -35,6 +35,7 @@
  * type for this bio
  */
 #define EXTENT_BIO_COMPRESSED 1
+#define EXTENT_BIO_ENCRYPTED 2
 #define EXTENT_BIO_FLAG_SHIFT 16
 
 /* these are bit numbers for test/set bit */
@@ -553,5 +554,6 @@ u64 btrfs_find_lock_delalloc_range(struct inode *inode,
 #endif
 struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info,
 					       u64 start);
+void btrfs_increment_iv(char *iv, u32 inc);
 
 #endif
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 7eea8b6e2cd3..0cc0ffbe0453 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -6,7 +6,7 @@
 #include "ctree.h"
 #include "extent_map.h"
 #include "compression.h"
-
+#include "extent_io.h"
 
 static struct kmem_cache *extent_map_cache;
 
@@ -210,6 +210,20 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
 	if (!list_empty(&prev->list) || !list_empty(&next->list))
 		return 0;
 
+	if (prev->key_number != next->key_number)
+		return 0;
+
+	if (next->key_number != 0) {
+		char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+		memcpy(iv, prev->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+		btrfs_increment_iv(iv, prev->len /
+				   BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+		if (memcmp(iv, next->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH))
+			return 0;
+	}
+
 	if (extent_map_end(prev) == next->start &&
 	    prev->flags == next->flags &&
 	    prev->bdev == next->bdev &&
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 31977ffd6190..2bc1087564a1 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -5,6 +5,7 @@
 
 #include <linux/rbtree.h>
 #include <linux/refcount.h>
+#include <linux/btrfs_tree.h>
 
 #define EXTENT_MAP_LAST_BYTE ((u64)-4)
 #define EXTENT_MAP_HOLE ((u64)-3)
@@ -14,6 +15,7 @@
 /* bits for the flags field */
 #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */
 #define EXTENT_FLAG_COMPRESSED 1
+#define EXTENT_FLAG_ENCRYPTED 2
 #define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
 #define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
 #define EXTENT_FLAG_FILLING 5 /* Filling in a preallocated extent */
@@ -45,6 +47,9 @@ struct extent_map {
 	};
 	refcount_t refs;
 	unsigned int compress_type;
+	unsigned int encrypt_type;
+	u64 key_number;
+	char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
 	struct list_head list;
 };
 
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ba74827beb32..a903997e67ba 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -13,6 +13,7 @@
 #include "volumes.h"
 #include "print-tree.h"
 #include "compression.h"
+#include "encryption.h"
 
 #define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
 				   sizeof(struct btrfs_item) * 2) / \
@@ -931,6 +932,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 	u64 bytenr;
 	u8 type = btrfs_file_extent_type(leaf, fi);
 	int compress_type = btrfs_file_extent_compression(leaf, fi);
+	int encrypt_type = btrfs_file_extent_encryption(leaf, fi);
 
 	em->bdev = fs_info->fs_devices->latest_bdev;
 	btrfs_item_key_to_cpu(leaf, &key, slot);
@@ -960,7 +962,18 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
 			em->block_start = EXTENT_MAP_HOLE;
 			return;
 		}
-		if (compress_type != BTRFS_COMPRESS_NONE) {
+
+		if (encrypt_type != BTRFS_ENCRYPTION_NONE) {
+			set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
+			em->encrypt_type = encrypt_type;
+			em->block_start = bytenr;
+			em->block_len = em->orig_block_len;
+
+			if (compress_type != BTRFS_COMPRESS_NONE) {
+				set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
+				em->compress_type = compress_type;
+			}
+		} else if (compress_type != BTRFS_COMPRESS_NONE) {
 			set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
 			em->compress_type = compress_type;
 			em->block_start = bytenr;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7018a2169e3e..52ea7d7c880b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1953,7 +1953,7 @@ int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
 	u64 map_length;
 	int ret;
 
-	if (bio_flags & EXTENT_BIO_COMPRESSED)
+	if (bio_flags & (EXTENT_BIO_COMPRESSED | EXTENT_BIO_ENCRYPTED))
 		return 0;
 
 	length = bio->bi_iter.bi_size;
@@ -7039,6 +7039,14 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
 	btrfs_extent_item_to_extent_map(inode, path, item,
 			new_inline, em);
 
+	if (btrfs_file_extent_type(leaf, item) != BTRFS_ENCRYPTION_NONE) {
+		em->key_number = btrfs_file_extent_enc_key_number(leaf,
+				(struct btrfs_file_extent_item_enc *)item);
+
+		read_eb_member(leaf, item, struct btrfs_file_extent_item_enc,
+			       iv, em->iv);
+	}
+
 	if (found_type == BTRFS_FILE_EXTENT_REG ||
 	    found_type == BTRFS_FILE_EXTENT_PREALLOC) {
 		goto insert;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index aefce895e994..fc12c28bdd93 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -271,6 +271,8 @@ struct btrfs_io_bio {
 	u8 *csum_allocated;
 	btrfs_io_bio_end_io_t *end_io;
 	struct bvec_iter iter;
+	u64 key_number;
+	char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
 	/*
 	 * This member must come last, bio_alloc_bioset will allocate enough
 	 * bytes for entire btrfs_io_bio but relies on bio being last.
-- 
2.19.2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ