Messages in this thread Patch in this message | | | From | Mark Harmstone <> | Subject | [RFC PATCH 06/19] btrfs: add ioctl BTRFS_IOC_GET_KEY_SALT | Date | Wed, 9 Jan 2019 01:26:48 +0000 |
| |
Signed-off-by: Mark Harmstone <mark@harmstone.com> --- fs/btrfs/Makefile | 2 +- fs/btrfs/ctree.h | 4 + fs/btrfs/disk-io.c | 6 ++ fs/btrfs/encryption.c | 58 +++++++++++++ fs/btrfs/encryption.h | 3 + fs/btrfs/ioctl.c | 170 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 10 +++ 7 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 fs/btrfs/encryption.c
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index ca693dd554e9..805654060cea 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -10,7 +10,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \ compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ - uuid-tree.o props.o free-space-tree.o tree-checker.o + uuid-tree.o props.o free-space-tree.o tree-checker.o encryption.o btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3f3356a2d145..4bab57e01e21 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2418,6 +2418,10 @@ BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item, #define BTRFS_ENCRYPTION_KEY_ID_LENGTH 64 +struct btrfs_encryption_key_item { + u8 key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH]; +} __attribute__ ((__packed__)); + /* * this returns the number of bytes used by the item on disk, minus the * size of any extent headers. If a file is compressed on disk, this is diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a1964b768750..2ecce2ce51be 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2596,6 +2596,10 @@ int open_ctree(struct super_block *sb, int clear_free_space_tree = 0; int level; + err = crypto_get_default_rng(); + if (err) + goto fail; + tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL); chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL); if (!tree_root || !chunk_root) { @@ -4031,6 +4035,8 @@ void close_ctree(struct btrfs_fs_info *fs_info) list_del_init(&key->key_list); kfree(key); } + + crypto_put_default_rng(); } int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid, diff --git a/fs/btrfs/encryption.c b/fs/btrfs/encryption.c new file mode 100644 index 000000000000..0803642c1ec9 --- /dev/null +++ b/fs/btrfs/encryption.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Mark Harmstone. All rights reserved. + */ + +#include <crypto/hash.h> +#include "ctree.h" +#include "encryption.h" + +int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen, + char *key_id) +{ + int ret; + struct crypto_shash *shash; + struct shash_desc *desc; + + shash = crypto_alloc_shash("sha512", 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; + + 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, password, pwdlen); + if (ret) + goto free_desc; + + ret = crypto_shash_final(desc, key_id); + if (ret) + goto free_desc; + + ret = 0; + +free_desc: + kzfree(desc); + +free_shash: + crypto_free_shash(shash); + + return ret; +} diff --git a/fs/btrfs/encryption.h b/fs/btrfs/encryption.h index 77914d7797c6..adf35696373f 100644 --- a/fs/btrfs/encryption.h +++ b/fs/btrfs/encryption.h @@ -30,4 +30,7 @@ struct btrfs_enc_key { struct mutex lock; }; +int btrfs_get_key_id(u64 salt, char *password, unsigned int pwdlen, + char *key_id); + #endif diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 802a628e9f7d..92fbed90dc4e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -26,6 +26,7 @@ #include <linux/btrfs.h> #include <linux/uaccess.h> #include <linux/iversion.h> +#include <crypto/rng.h> #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -43,6 +44,7 @@ #include "qgroup.h" #include "tree-log.h" #include "compression.h" +#include "encryption.h" #ifdef CONFIG_64BIT /* If we have a 32-bit userspace and 64-bit kernel, then the UAPI @@ -5852,6 +5854,172 @@ static int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat) return ret; } +static int btrfs_ioctl_get_key_salt(struct btrfs_fs_info *fs_info, + void __user *argp) +{ + struct btrfs_ioctl_get_key_salt_args args; + int ret; + u64 salt; + struct btrfs_enc_key *k = NULL, *k2; + char key_id[BTRFS_ENCRYPTION_KEY_ID_LENGTH]; + + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + + down_write(&fs_info->key_sem); + + /* Search loaded keys */ + list_for_each_entry(k2, &fs_info->key_list, key_list) { + ret = btrfs_get_key_id(k2->key_number, args.password, + strlen(args.password), key_id); + if (ret) { + up_write(&fs_info->key_sem); + return ret; + } + + if (!memcmp(key_id, k2->key_id, sizeof(key_id))) { + k = k2; + break; + } + } + + /* Not loaded - search tree */ + if (!k && fs_info->key_root) { + struct btrfs_key key; + struct btrfs_path *path; + + path = btrfs_alloc_path(); + if (!path) { + up_write(&fs_info->key_sem); + return -ENOMEM; + } + + key.objectid = 0; + key.type = BTRFS_ENCRYPTION_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, fs_info->key_root, &key, + path, 0, 0); + if (ret < 0) { + up_write(&fs_info->key_sem); + btrfs_free_path(path); + return ret; + } + + do { + struct extent_buffer *leaf; + int slot; + u32 item_size; + struct btrfs_encryption_key_item *item; + char key_id2[BTRFS_ENCRYPTION_KEY_ID_LENGTH]; + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + + if (key.type != BTRFS_ENCRYPTION_KEY) + goto skip; + + item_size = btrfs_item_size_nr(leaf, slot); + + if (item_size != + sizeof(struct btrfs_encryption_key_item)) + goto skip; + + item = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_encryption_key_item); + + salt = key.objectid; + + ret = btrfs_get_key_id(salt, args.password, + strlen(args.password), key_id); + if (ret) { + up_write(&fs_info->key_sem); + btrfs_free_path(path); + return ret; + } + + read_eb_member(leaf, item, + struct btrfs_encryption_key_item, + key_id, key_id2); + + if (!memcmp(key_id, key_id2, + BTRFS_ENCRYPTION_KEY_ID_LENGTH)) { + k = kmalloc(sizeof(*k), GFP_KERNEL); + if (!k) { + up_write(&fs_info->key_sem); + btrfs_free_path(path); + return -ENOMEM; + } + + memcpy(k->key_id, key_id, + BTRFS_ENCRYPTION_KEY_ID_LENGTH); + k->key_number = salt; + k->loaded = false; + k->added = false; + k->used = true; + mutex_init(&k->lock); + + list_add(&k->key_list, &fs_info->key_list); + break; + } + +skip: + ret = btrfs_next_item(fs_info->key_root, path); + if (ret == 0) + continue; + else if (ret > 0) + ret = 0; + break; + } while (1); + + btrfs_free_path(path); + } + + /* Not found - allocate new key */ + if (!k) { + ret = crypto_rng_get_bytes(crypto_default_rng, (u8 *)&salt, + sizeof(salt)); + + if (ret) { + up_write(&fs_info->key_sem); + return ret; + } + + k = kmalloc(sizeof(*k), GFP_KERNEL); + if (!k) { + up_write(&fs_info->key_sem); + return -ENOMEM; + } + + k->key_number = salt; + + ret = btrfs_get_key_id(k->key_number, args.password, + strlen(args.password), k->key_id); + if (ret) { + up_write(&fs_info->key_sem); + kzfree(k); + return ret; + } + + k->loaded = false; + k->added = true; + k->used = false; + mutex_init(&k->lock); + + list_add(&k->key_list, &fs_info->key_list); + } + + args.salt = k->key_number; + + up_write(&fs_info->key_sem); + + if (copy_to_user(argp, &args, sizeof(args))) + return -EFAULT; + + return 0; +} + long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -5998,6 +6166,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_subvol_rootref(file, argp); case BTRFS_IOC_INO_LOOKUP_USER: return btrfs_ioctl_ino_lookup_user(file, argp); + case BTRFS_IOC_GET_KEY_SALT: + return btrfs_ioctl_get_key_salt(fs_info, argp); } return -ENOTTY; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 6c785d5cfb4b..aef9b695a05c 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -817,6 +817,14 @@ struct btrfs_ioctl_get_subvol_rootref_args { __u8 align[7]; }; +#define BTRFS_ENC_PASSWORD_MAX 255 + +struct btrfs_ioctl_get_key_salt_args { + char password[BTRFS_ENC_PASSWORD_MAX]; /* in */ + + __u64 salt; /* out */ +}; + /* Error codes as returned by the kernel */ enum btrfs_err_code { BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, @@ -941,5 +949,7 @@ enum btrfs_err_code { struct btrfs_ioctl_get_subvol_rootref_args) #define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \ struct btrfs_ioctl_ino_lookup_user_args) +#define BTRFS_IOC_GET_KEY_SALT _IOWR(BTRFS_IOCTL_MAGIC, 63, \ + struct btrfs_ioctl_get_key_salt_args) #endif /* _UAPI_LINUX_BTRFS_H */ -- 2.19.2
| |