ANDROID: add dm-default-key target for ICE metadata encryption

Add a device-mapper target "dm-default-key" which assigns an encryption
key to bios that don't already have one.  This will be used on Android's
userdata partition, so that all data not already encrypted with ext4
encryption is still encrypted with a default key (which will not be
derived from a user credential but will still be protected in some way).

Currently this feature depends on inline encryption support in hardware
via Qualcomm ICE; no software fallback is implemented yet.

An alternate approach considered was to introduce a block device ioctl
which would associate an encryption key with a struct block_device,
which would then be used as the default for bios issued to that
block_device.  However, that approach had some issues which made the dm
target seem like the better solution, and perhaps more likely to be
accepted upstream in some form someday (at least, once we have
vendor-independent inline encryption support upstream).  Specifically:

1.) The struct block_device for a partition really only represents a
    part of some underlying disk along with some state associated with
    its current users.  Notably, the block_device for a given partition
    is not guaranteed to stay around while no one has it opened or
    mounted.  This means that any extra state like an encryption key we
    may try to tie a block_device can be lost if there is a period of
    time when no one is using the block device.  Granted, this can't
    happen while the filesystem on the device is mounted, and it also
    can't happen on systems that use tmpfs for their /dev because the
    block_device will be pinned by the device node.  But either way, a
    dm target does not have this problem.

2.) The block_device for a partition doesn't have its own request_queue
    or queue_limits.  One way in which this could cause problems is that
    the disk could support discard requests and have discard_zeroes_data
    set to true, which specifies that data read back following a discard
    is guaranteed to be zeros.  That will not be true for an encrypted
    partition, so any filesystem that issues zeroouts (which the block
    layer may implement with discard) would potentially be broken.  We
    can handle this correctly in a dm target since each dm device has
    its own request_queue and we can disable discard_zeroes_data, just
    as dm-crypt does for example.

3.) Since the block layer remaps bios from partitions to the devices
    containing them, we'd still need to have ->bi_crypt_key and
    initialize it somewhere, e.g. in generic_make_request_checks()
    before it does the partition remapping.  We can't simply read
    ->bi_bdev->bd_default_key from the PFK module.

4.) With a block device ioctl, we'd need to carefully handle the cases
    where the ioctl is executed while someone else has the block device
    open (fail with EBUSY?) or while the block device's mapping already
    has pages cached (sync and invalidate them?).  This would not be too
    difficult, but with a dm target neither of these is a problem.

Change-Id: Ia3884842004cfb84d315ef38e54ab4f35b48cf5f
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Shivaprasad Hongal <shongal@codeaurora.org>
Signed-off-by: Neeraj Soni <neersoni@codeaurora.org>
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 20c70c0..bb1935f 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -292,6 +292,22 @@
 	  To compile this code as a module, choose M here: the module will
 	  be called dm-req-crypt.
 
+config DM_DEFAULT_KEY
+	tristate "Default-key crypt target support"
+	depends on BLK_DEV_DM
+	depends on PFK
+	---help---
+	  This (currently Android-specific) device-mapper target allows you to
+	  create a device that assigns a default encryption key to bios that
+	  don't already have one.  This can sit between inline cryptographic
+	  acceleration hardware and filesystems that use it.  This ensures that
+	  where the filesystem doesn't explicitly specify a key, such as for
+	  filesystem metadata, a default key will be used instead, leaving no
+	  sectors unencrypted.
+
+	  To compile this code as a module, choose M here: the module will be
+	  called dm-default-key.
+
 	  If unsure, say N.
 
 config DM_SNAPSHOT
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index f14e2fc..c3bf33b 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -40,6 +40,7 @@
 obj-$(CONFIG_DM_BUFIO)		+= dm-bufio.o
 obj-$(CONFIG_DM_BIO_PRISON)	+= dm-bio-prison.o
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
+obj-$(CONFIG_DM_DEFAULT_KEY)	+= dm-default-key.o
 obj-$(CONFIG_DM_DELAY)		+= dm-delay.o
 obj-$(CONFIG_DM_FLAKEY)		+= dm-flakey.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
diff --git a/drivers/md/dm-default-key.c b/drivers/md/dm-default-key.c
new file mode 100644
index 0000000..7cc7ea7
--- /dev/null
+++ b/drivers/md/dm-default-key.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device-mapper.h>
+#include <linux/module.h>
+#include <linux/pfk.h>
+
+#define DM_MSG_PREFIX "default-key"
+
+struct default_key_c {
+	struct dm_dev *dev;
+	sector_t start;
+	struct blk_encryption_key key;
+};
+
+static void default_key_dtr(struct dm_target *ti)
+{
+	struct default_key_c *dkc = ti->private;
+
+	if (dkc->dev)
+		dm_put_device(ti, dkc->dev);
+	kzfree(dkc);
+}
+
+/*
+ * Construct a default-key mapping: <mode> <key> <dev_path> <start>
+ */
+static int default_key_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct default_key_c *dkc;
+	size_t key_size;
+	unsigned long long tmp;
+	char dummy;
+	int err;
+
+	if (argc != 4) {
+		ti->error = "Invalid argument count";
+		return -EINVAL;
+	}
+
+	dkc = kzalloc(sizeof(*dkc), GFP_KERNEL);
+	if (!dkc) {
+		ti->error = "Out of memory";
+		return -ENOMEM;
+	}
+	ti->private = dkc;
+
+	if (strcmp(argv[0], "AES-256-XTS") != 0) {
+		ti->error = "Unsupported encryption mode";
+		err = -EINVAL;
+		goto bad;
+	}
+
+	key_size = strlen(argv[1]);
+	if (key_size != 2 * BLK_ENCRYPTION_KEY_SIZE_AES_256_XTS) {
+		ti->error = "Unsupported key size";
+		err = -EINVAL;
+		goto bad;
+	}
+	key_size /= 2;
+
+	if (hex2bin(dkc->key.raw, argv[1], key_size) != 0) {
+		ti->error = "Malformed key string";
+		err = -EINVAL;
+		goto bad;
+	}
+
+	err = dm_get_device(ti, argv[2], dm_table_get_mode(ti->table),
+			    &dkc->dev);
+	if (err) {
+		ti->error = "Device lookup failed";
+		goto bad;
+	}
+
+	if (sscanf(argv[3], "%llu%c", &tmp, &dummy) != 1) {
+		ti->error = "Invalid start sector";
+		err = -EINVAL;
+		goto bad;
+	}
+	dkc->start = tmp;
+
+	if (!blk_queue_inlinecrypt(bdev_get_queue(dkc->dev->bdev))) {
+		ti->error = "Device does not support inline encryption";
+		err = -EINVAL;
+		goto bad;
+	}
+
+	/* Pass flush requests through to the underlying device. */
+	ti->num_flush_bios = 1;
+
+	/*
+	 * We pass discard requests through to the underlying device, although
+	 * the discarded blocks will be zeroed, which leaks information about
+	 * unused blocks.  It's also impossible for dm-default-key to know not
+	 * to decrypt discarded blocks, so they will not be read back as zeroes
+	 * and we must set discard_zeroes_data_unsupported.
+	 */
+	ti->num_discard_bios = 1;
+
+	/*
+	 * It's unclear whether WRITE_SAME would work with inline encryption; it
+	 * would depend on whether the hardware duplicates the data before or
+	 * after encryption.  But since the internal storage in some  devices
+	 * (MSM8998-based) doesn't claim to support WRITE_SAME anyway, we don't
+	 * currently have a way to test it.  Leave it disabled it for now.
+	 */
+	/*ti->num_write_same_bios = 1;*/
+
+	return 0;
+
+bad:
+	default_key_dtr(ti);
+	return err;
+}
+
+static int default_key_map(struct dm_target *ti, struct bio *bio)
+{
+	const struct default_key_c *dkc = ti->private;
+
+	bio->bi_bdev = dkc->dev->bdev;
+	if (bio_sectors(bio)) {
+		bio->bi_iter.bi_sector = dkc->start +
+			dm_target_offset(ti, bio->bi_iter.bi_sector);
+	}
+
+	if (!bio->bi_crypt_key)
+		bio->bi_crypt_key = &dkc->key;
+
+	return DM_MAPIO_REMAPPED;
+}
+
+static void default_key_status(struct dm_target *ti, status_type_t type,
+			       unsigned int status_flags, char *result,
+			       unsigned int maxlen)
+{
+	const struct default_key_c *dkc = ti->private;
+	unsigned int sz = 0;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+
+		/* encryption mode */
+		DMEMIT("AES-256-XTS");
+
+		/* reserved for key; dm-crypt shows it, but we don't for now */
+		DMEMIT(" -");
+
+		/* name of underlying device, and the start sector in it */
+		DMEMIT(" %s %llu", dkc->dev->name,
+		       (unsigned long long)dkc->start);
+		break;
+	}
+}
+
+static int default_key_prepare_ioctl(struct dm_target *ti,
+				     struct block_device **bdev, fmode_t *mode)
+{
+	struct default_key_c *dkc = ti->private;
+	struct dm_dev *dev = dkc->dev;
+
+	*bdev = dev->bdev;
+
+	/*
+	 * Only pass ioctls through if the device sizes match exactly.
+	 */
+	if (dkc->start ||
+	    ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
+		return 1;
+	return 0;
+}
+
+static int default_key_iterate_devices(struct dm_target *ti,
+				       iterate_devices_callout_fn fn,
+				       void *data)
+{
+	struct default_key_c *dkc = ti->private;
+
+	return fn(ti, dkc->dev, dkc->start, ti->len, data);
+}
+
+static struct target_type default_key_target = {
+	.name   = "default-key",
+	.version = {1, 0, 0},
+	.module = THIS_MODULE,
+	.ctr    = default_key_ctr,
+	.dtr    = default_key_dtr,
+	.map    = default_key_map,
+	.status = default_key_status,
+	.prepare_ioctl = default_key_prepare_ioctl,
+	.iterate_devices = default_key_iterate_devices,
+};
+
+static int __init dm_default_key_init(void)
+{
+	return dm_register_target(&default_key_target);
+}
+
+static void __exit dm_default_key_exit(void)
+{
+	dm_unregister_target(&default_key_target);
+}
+
+module_init(dm_default_key_init);
+module_exit(dm_default_key_exit);
+
+MODULE_AUTHOR("Paul Lawrence <paullawrence@google.com>");
+MODULE_AUTHOR("Paul Crowley <paulcrowley@google.com>");
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_DESCRIPTION(DM_NAME " target for encrypting filesystem metadata");
+MODULE_LICENSE("GPL v2");