linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 01/19] btrfs: add encryption structs and constants
@ 2019-01-09  1:26 Mark Harmstone
  2019-01-09  1:26 ` [RFC PATCH 02/19] btrfs: add encryption dependencies to Kconfig Mark Harmstone
                   ` (17 more replies)
  0 siblings, 18 replies; 19+ messages in thread
From: Mark Harmstone @ 2019-01-09  1:26 UTC (permalink / raw)
  Cc: mark, Chris Mason, Josef Bacik, David Sterba, linux-btrfs, linux-kernel

This is a series of patches to add per-extent AES encryption to Btrfs. Unlike
previous attempts, this isn't implemented by adding a new compression type,
meaning that extents that are both encrypted and compressed should work fine.

It's implemented by adding a 8-byte key number and a 16-byte
randomized initialization vector to the end of the BTRFS_EXTENT_DATA_KEY (which
for inline extents means immediately before the encrypted data). The key number
is an index into the new key root, which stores the password hashes, with the
key number doubling as the salt. This means that (unlike, AIUI, ext4), there's
no chance that a file by can be decrypted using the wrong key.

The ioctl BTRFS_IOC_GET_KEY_SALT provides the key number for a given password,
or if not present adds a new key with a randomized number.

We use AES in counter mode, so that we can do random reads without having to
read the whole extent.

Some notes:

* At the moment COW is forced for encrypted extents, meaning that preallocation
and the nocow option don't have any effect.

* Direct IO is disabled for encrypted extents. Direct reading should be
possible, as you'd do the decryption after the bio returns. I'm not sure direct
writing is feasible though, as you'd either have to copy the buffer or change
the process's memory.

* Once a key is added by keyctl, it persists until the FS is unmounted. Ideally
we'd like it to be removed when unlinked or revoked.

* We don't use fscrypt - I understand that the XTS mode that it operates in
means that sectors are keyed to their logical address, which would break
balancing.

* Sending still dumps unencrypted data, and won't work on encrypted
files without the key. We could change this, but it raises the possibility that
the same password could correspond to multiple keys.

* These patches assume that the number of keys in the key tree will be small -
in particular, the ioctl walks the tree testing the hashes. There's no way to
remove keys from the key tree; I think we'd probably want btrfs-check to warn
about unused keys, something like that. We could use reference counting, but
that would mean writing to the key tree whenever an encrypted extent is created
or removed.

* Filenames aren't encrypted at present; I considered this a different enough
problem to save for another patchset, given that this one's already quite
hefty. I'm not sure if we could use fscrypt for this either, as btrfs allows
for multiple inodes with the same number, and it would be a security flaw to
duplicate IVs.

I've attached a quick-and-dirty userspace program demonstrating the ioctl:

#include <linux/btrfs.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <keyutils.h>
#include <sys/xattr.h>

static int getpw(char **pw)
{
	struct termios t, orig_t;
	size_t sz;
	int nread;

	if (tcgetattr(fileno(stdin), &t) != 0)
		return errno;

	orig_t = t;
	t.c_lflag &= ~ECHO;
	if (tcsetattr(fileno(stdin), TCSAFLUSH, &t) != 0)
		return errno;

	nread = getline(pw, &sz, stdin);
	if (nread > 0 && (*pw)[nread - 1] == '\n')
		(*pw)[nread - 1] = 0; /* remove trailing newline */

	tcsetattr(fileno(stdin), TCSAFLUSH, &orig_t);

	return 0;
}

static int add_btrfs_key(__u64 salt, char *pw)
{
	key_serial_t key;
	char name[20];

	sprintf(name, "btrfs:%016llx", salt);

	/*
	 * In pratice, you would want a type of "logon" here, so that
	 * the key can't be read from userspace.
	 */

	key = add_key("user", name, pw, strlen(pw), KEY_SPEC_USER_KEYRING);
	if (key == -1)
		return errno;

	printf("Added key %u to keyring\n", key);

	return 0;
}

int main(int argc, char *argv[])
{
	int fd, ret;
	char *pw = NULL;
	struct btrfs_ioctl_get_key_salt_args args;
	char keyhex[17];

	if (argc < 2) {
		fprintf(stderr, "Usage: btrfscrypt <directory>\n");
		return 0;
	}

	/* Prompt for password */

	printf("Password: ");

	ret = getpw(&pw);

	printf("\n");

	if (ret) {
		fprintf(stderr, "getpw returned %i\n", ret);
		return ret;
	}

	fd = open(argv[1], 0);
	if (fd < 0) {
		fprintf(stderr, "open returned %i\n", fd);
		goto end;
	}

	strcpy(args.password, pw);

	/* Get salt */

	ret = ioctl(fd, BTRFS_IOC_GET_KEY_SALT, &args);
	if (ret) {
		fprintf(stderr, "ioctl returned %i\n", ret);
		goto end2;
	}

	printf("BTRFS_IOC_GET_KEY_SALT returned salt %llx\n", args.salt);

	ret = add_btrfs_key(args.salt, pw);
	if (ret) {
		fprintf(stderr, "add_btrfs_key returned %i\n", ret);
		goto end2;
	}

	/* Set xattr */

	sprintf(keyhex, "%016llx", args.salt);

	ret = setxattr(argv[1], "btrfs.key", keyhex, strlen(keyhex), 0);
	if (ret) {
		fprintf(stderr, "setxattr returned %i\n", ret);
		goto end2;
	}

	ret = 0;

end2:
	close(fd);

end:
	free(pw);

	return ret;
}


Signed-off-by: Mark Harmstone <mark@harmstone.com>
---
 include/uapi/linux/btrfs.h      |  1 +
 include/uapi/linux/btrfs_tree.h | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 5ca1d21fc4a7..6c785d5cfb4b 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -269,6 +269,7 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_RAID56		(1ULL << 7)
 #define BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA	(1ULL << 8)
 #define BTRFS_FEATURE_INCOMPAT_NO_HOLES		(1ULL << 9)
+#define BTRFS_FEATURE_INCOMPAT_ENCRYPTION	(1ULL << 10)
 
 struct btrfs_ioctl_feature_flags {
 	__u64 compat_flags;
diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h
index aff1356c2bb8..40acca24c69f 100644
--- a/include/uapi/linux/btrfs_tree.h
+++ b/include/uapi/linux/btrfs_tree.h
@@ -48,6 +48,9 @@
 /* tracks free space in block groups. */
 #define BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
 
+/* stores encryption keys */
+#define BTRFS_KEY_TREE_OBJECTID 11ULL
+
 /* device stats in the device tree */
 #define BTRFS_DEV_STATS_OBJECTID 0ULL
 
@@ -141,6 +144,11 @@
  */
 #define BTRFS_ROOT_ITEM_KEY	132
 
+/*
+ * encryption key, consisting of the hashed password
+ */
+#define BTRFS_ENCRYPTION_KEY	133
+
 /*
  * root backrefs tie subvols and snapshots to the directory entries that
  * reference them
@@ -789,6 +797,19 @@ struct btrfs_file_extent_item {
 
 } __attribute__ ((__packed__));
 
+#define BTRFS_ENCRYPTION_BLOCK_LENGTH 16
+
+struct btrfs_file_extent_item_enc {
+	struct btrfs_file_extent_item extent_item;
+	__le64 key_number;
+	__u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+} __attribute__ ((__packed__));
+
+struct btrfs_file_extent_inline_enc {
+	__le64 key_number;
+	u8 iv[BTRFS_ENCRYPTION_BLOCK_LENGTH];
+} __attribute__ ((__packed__));
+
 struct btrfs_csum_item {
 	__u8 csum;
 } __attribute__ ((__packed__));
-- 
2.19.2


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

end of thread, other threads:[~2019-01-09  1:29 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [RFC PATCH 10/19] btrfs: allow reading encrypted inline extents Mark Harmstone
2019-01-09  1:26 ` [RFC PATCH 11/19] btrfs: allow writing " 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).