From: Mark Harmstone <mark@harmstone.com>
To: unlisted-recipients:; (no To-header on input)
Cc: mark@harmstone.com, Chris Mason <clm@fb.com>,
Josef Bacik <josef@toxicpanda.com>,
David Sterba <dsterba@suse.com>,
linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents
Date: Wed, 9 Jan 2019 01:26:52 +0000 [thread overview]
Message-ID: <20190109012701.26441-10-mark@harmstone.com> (raw)
In-Reply-To: <20190109012701.26441-1-mark@harmstone.com>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
fs/btrfs/encryption.c | 305 ++++++++++++++++++++++++++++++++++++++++++
fs/btrfs/encryption.h | 3 +
fs/btrfs/inode.c | 72 +++++++++-
3 files changed, 379 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c
index 0803642c1ec9..a1c9a982b009 100644
--- a/fs/btrfs/encryption.c
+++ b/fs/btrfs/encryption.c
@@ -4,9 +4,314 @@
*/
#include <crypto/hash.h>
+#include <keys/user-type.h>
#include "ctree.h"
#include "encryption.h"
+#define KEY_SIG_PREFIX "btrfs:"
+#define KEY_SIG_LENGTH (sizeof(KEY_SIG_PREFIX) - 1 + 16)
+
+#define AES256_KEY_LENGTH 32
+
+static int derive_aes_key(u64 salt, char *data, unsigned int datalen,
+ char *aeskey)
+{
+ int ret;
+ struct crypto_shash *shash;
+ struct shash_desc *desc;
+ char tmp[AES256_KEY_LENGTH];
+
+ shash = crypto_alloc_shash("sha256", 0, 0);
+ if (IS_ERR(shash))
+ return PTR_ERR(shash);
+
+ desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(shash),
+ GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto free_shash;
+ }
+
+ desc->tfm = shash;
+ desc->flags = 0;
+
+ /* Get SHA256 of salted password */
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto free_desc;
+
+ salt = cpu_to_le64(salt);
+
+ ret = crypto_shash_update(desc, (u8 *)&salt, sizeof(salt));
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_update(desc, data, datalen);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_final(desc, tmp);
+ if (ret)
+ goto free_desc;
+
+ /* SHA256 again */
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_update(desc, tmp, AES256_KEY_LENGTH);
+ if (ret)
+ goto free_desc;
+
+ ret = crypto_shash_final(desc, aeskey);
+ if (ret)
+ goto free_desc;
+
+ ret = 0;
+
+free_desc:
+ kzfree(desc);
+
+free_shash:
+ crypto_free_shash(shash);
+
+ return ret;
+}
+
+static int btrfs_load_key(struct btrfs_enc_key *k)
+{
+ struct key *key;
+ u64 salt;
+ char sig[KEY_SIG_LENGTH + 1];
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+ char aeskey[AES256_KEY_LENGTH];
+ struct user_key_payload *upayload;
+ int ret;
+
+ if (k->loaded)
+ return 0;
+
+ salt = cpu_to_be64(k->key_number);
+
+ memcpy(sig, KEY_SIG_PREFIX, sizeof(KEY_SIG_PREFIX) - 1);
+ bin2hex(sig + sizeof(KEY_SIG_PREFIX) - 1, &salt, sizeof(u64));
+ sig[KEY_SIG_LENGTH] = 0;
+
+ key = request_key(&key_type_user, sig, NULL);
+
+ if (IS_ERR(key)) {
+ key = request_key(&key_type_logon, sig, NULL);
+
+ if (IS_ERR(key))
+ return -ENOKEY;
+ }
+
+ down_read(&key->sem);
+
+ upayload = user_key_payload_locked(key);
+ if (IS_ERR_OR_NULL(upayload)) {
+ ret = upayload ? PTR_ERR(upayload) : -EINVAL;
+ goto out_key_put;
+ }
+
+ ret = btrfs_get_key_id(k->key_number, upayload->data,
+ upayload->datalen, key_id);
+ if (ret)
+ goto out_key_put;
+
+ if (memcmp(key_id, k->key_id,
+ BTRFS_ENCRYPTION_KEY_ID_LENGTH)) {
+ ret = -EINVAL;
+ goto out_key_put;
+ }
+
+ k->skcipher = crypto_alloc_skcipher("ctr(aes)", 0, 0);
+ if (IS_ERR(k->skcipher)) {
+ ret = PTR_ERR(k->skcipher);
+ goto out_key_put;
+ }
+
+ ret = derive_aes_key(k->key_number, upayload->data,
+ upayload->datalen, aeskey);
+ if (ret) {
+ crypto_free_skcipher(k->skcipher);
+ goto out_key_put;
+ }
+
+ ret = crypto_skcipher_setkey(k->skcipher, aeskey, 32);
+ if (ret) {
+ crypto_free_skcipher(k->skcipher);
+ goto out_key_put;
+ }
+
+ k->loaded = true;
+
+ ret = 0;
+
+out_key_put:
+ up_read(&key->sem);
+
+ key_put(key);
+
+ return ret;
+}
+
+static int find_key(struct btrfs_fs_info *fs_info, u64 key_number,
+ struct btrfs_enc_key **key)
+{
+ int ret;
+ struct btrfs_enc_key *k = NULL, *k2;
+
+ down_write(&fs_info->key_sem);
+
+ list_for_each_entry(k2, &fs_info->key_list, key_list) {
+ if (k2->key_number == key_number) {
+ k = k2;
+ break;
+ }
+ }
+
+ if (!k && fs_info->key_root) {
+ struct btrfs_key key;
+ struct btrfs_path *path;
+ struct extent_buffer *leaf;
+ int slot;
+ u32 item_size;
+ struct btrfs_encryption_key_item *item;
+ char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH];
+
+ path = btrfs_alloc_path();
+ if (!path) {
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ key.objectid = key_number;
+ key.type = BTRFS_ENCRYPTION_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, fs_info->key_root, &key,
+ path, 0, 0);
+ if (ret < 0) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return ret;
+ }
+
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+
+ if (key.objectid != key_number ||
+ key.type != BTRFS_ENCRYPTION_KEY) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOKEY;
+ }
+
+ item_size = btrfs_item_size_nr(leaf, slot);
+
+ if (item_size != sizeof(struct btrfs_encryption_key_item)) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOKEY;
+ }
+
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_encryption_key_item);
+
+ read_eb_member(leaf, item,
+ struct btrfs_encryption_key_item,
+ key_id, key_id);
+
+ k = kmalloc(sizeof(*k), GFP_KERNEL);
+ if (!k) {
+ btrfs_free_path(path);
+ up_write(&fs_info->key_sem);
+ return -ENOMEM;
+ }
+
+ memcpy(k->key_id, key_id, BTRFS_ENCRYPTION_KEY_ID_LENGTH);
+ k->key_number = key.objectid;
+ k->loaded = false;
+ k->added = false;
+ k->used = true;
+ mutex_init(&k->lock);
+
+ list_add(&k->key_list, &fs_info->key_list);
+
+ btrfs_free_path(path);
+ }
+
+ up_write(&fs_info->key_sem);
+
+ if (!k)
+ return -ENOKEY;
+
+ if (!k->loaded) {
+ mutex_lock(&k->lock);
+ ret = btrfs_load_key(k);
+ mutex_unlock(&k->lock);
+
+ if (ret)
+ return ret;
+ }
+
+ *key = k;
+
+ return 0;
+}
+
+int btrfs_decrypt(struct btrfs_fs_info *fs_info,
+ unsigned char *data, size_t len)
+{
+ struct scatterlist sg;
+ struct skcipher_request *req = NULL;
+ int ret = -EFAULT;
+ u64 key_number;
+ struct btrfs_enc_key *key;
+ struct btrfs_file_extent_inline_enc *eienc;
+ char iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+
+ if (len <= sizeof(struct btrfs_file_extent_inline_enc))
+ return -EINVAL;
+
+ eienc = (struct btrfs_file_extent_inline_enc *)data;
+
+ key_number = le64_to_cpu(eienc->key_number);
+
+ ret = find_key(fs_info, key_number, &key);
+ if (ret)
+ return ret;
+
+ memcpy(iv, eienc->iv, BTRFS_ENCRYPTION_BLOCK_LENGTH);
+
+ len -= sizeof(struct btrfs_file_extent_inline_enc);
+
+ sg_init_one(&sg, data + sizeof(struct btrfs_file_extent_inline_enc),
+ len);
+
+ req = skcipher_request_alloc(key->skcipher, GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skcipher_request_set_crypt(req, &sg, &sg, len, iv);
+
+ ret = crypto_skcipher_decrypt(req);
+ 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 adf35696373f..add7ee6d879d 100644
--- a/fs/btrfs/encryption.h
+++ b/fs/btrfs/encryption.h
@@ -30,6 +30,9 @@ struct btrfs_enc_key {
struct mutex lock;
};
+int btrfs_decrypt(struct btrfs_fs_info *fs_info,
+ unsigned char *data, size_t len);
+
int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen,
char *key_id);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 1d1084cf9289..202a7458584f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -45,6 +45,7 @@
#include "props.h"
#include "qgroup.h"
#include "dedupe.h"
+#include "encryption.h"
struct btrfs_iget_args {
struct btrfs_key *location;
@@ -6757,6 +6758,66 @@ static noinline int uncompress_inline(struct btrfs_path *path,
return ret;
}
+static noinline int decrypt_inline(struct btrfs_fs_info *fs_info,
+ struct btrfs_path *path,
+ struct page *page,
+ size_t pg_offset, u64 extent_offset,
+ struct btrfs_file_extent_item *item,
+ int compress_type)
+{
+ int ret;
+ struct extent_buffer *leaf = path->nodes[0];
+ char *tmp;
+ size_t max_size;
+ unsigned long inline_size;
+ unsigned long ptr;
+ int encryption_type;
+
+ WARN_ON(pg_offset != 0);
+ encryption_type = btrfs_file_extent_encryption(leaf, item);
+ max_size = btrfs_file_extent_ram_bytes(leaf, item);
+ inline_size = btrfs_file_extent_inline_item_len(leaf,
+ btrfs_item_nr(path->slots[0]));
+ tmp = kmalloc(inline_size, GFP_NOFS);
+ if (!tmp)
+ return -ENOMEM;
+ ptr = btrfs_file_extent_inline_start(item);
+
+ read_extent_buffer(leaf, tmp, ptr, inline_size);
+
+ max_size = min_t(unsigned long, PAGE_SIZE, max_size);
+ ret = btrfs_decrypt(fs_info, tmp, inline_size);
+
+ if (ret)
+ goto end;
+
+ inline_size -= sizeof(struct btrfs_file_extent_inline_enc);
+
+ if (compress_type != BTRFS_COMPRESS_NONE) {
+
+ ret = btrfs_decompress(compress_type,
+ tmp + sizeof(struct btrfs_file_extent_inline_enc),
+ page, extent_offset, inline_size, max_size);
+ } else {
+ char *kaddr = kmap_atomic(page);
+
+ memcpy(kaddr + pg_offset,
+ tmp + sizeof(struct btrfs_file_extent_inline_enc) +
+ extent_offset,
+ min(max_size, inline_size));
+
+ if (max_size + pg_offset < PAGE_SIZE)
+ memset(kaddr + pg_offset + max_size, 0,
+ PAGE_SIZE - max_size - pg_offset);
+
+ kunmap_atomic(kaddr);
+ }
+
+end:
+ kfree(tmp);
+ return ret;
+}
+
/*
* a bit scary, this does extent mapping from logical file offset to the disk.
* the ugly parts come from merging extents from the disk with the in-ram
@@ -6932,7 +6993,16 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
btrfs_set_path_blocking(path);
if (!PageUptodate(page)) {
- if (btrfs_file_extent_compression(leaf, item) !=
+ if (btrfs_file_extent_encryption(leaf, item) !=
+ BTRFS_ENCRYPTION_NONE) {
+ ret = decrypt_inline(fs_info, path, page,
+ pg_offset, extent_offset, item,
+ btrfs_file_extent_compression(leaf, item));
+ if (ret) {
+ err = ret;
+ goto out;
+ }
+ } else if (btrfs_file_extent_compression(leaf, item) !=
BTRFS_COMPRESS_NONE) {
ret = uncompress_inline(path, page, pg_offset,
extent_offset, item);
--
2.19.2
next prev parent reply other threads:[~2019-01-09 1:28 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-09 1:26 [RFC PATCH 01/19] btrfs: add encryption structs and constants Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 03/19] btrfs: load key tree Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 04/19] btrfs: allow encrypted volumes to be mounted Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 05/19] btrfs: add key list Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 07/19] btrfs: add new keys to key root when flushed Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 08/19] btrfs: change extract in prop_handler to write into string Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 09/19] btrfs: add btrfs.key property Mark Harmstone
2019-01-09 1:26 ` Mark Harmstone [this message]
2019-01-09 1:26 ` [RFC PATCH 11/19] btrfs: allow writing encrypted inline extents Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 12/19] btrfs: allow reading normal encrypted extents Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 13/19] btrfs: allow writing normal and compressed " Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 14/19] btrfs: allow reading " Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 15/19] btrfs: allow writing compressed, encrypted, inline extents Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 16/19] btrfs: add encryption incompat flag to sysfs Mark Harmstone
2019-01-09 1:26 ` [RFC PATCH 17/19] btrfs: don't allow direct IO of encrypted extents Mark Harmstone
2019-01-09 1:27 ` [RFC PATCH 18/19] btrfs: return encrypted flag to statx Mark Harmstone
2019-01-09 1:27 ` [RFC PATCH 19/19] btrfs: translate encryption flag to FS_ENCRYPT_FL Mark Harmstone
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190109012701.26441-10-mark@harmstone.com \
--to=mark@harmstone.com \
--cc=clm@fb.com \
--cc=dsterba@suse.com \
--cc=josef@toxicpanda.com \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.