>From e7f405abb329977b717371b80fddb38ff9063e8a Mon Sep 17 00:00:00 2001
From: Simon Peter
Date: Sat, 26 Oct 2013 18:54:36 +0200
Subject: [PATCH 1/2] Reimport Simon Peter's devmapper
---
grub-core/Makefile.core.def | 5 +
grub-core/disk/devmapper.c | 414 ++++++++++++++++++++++++++++++++++++++++++++
include/grub/disk.h | 1 +
3 files changed, 420 insertions(+)
create mode 100644 grub-core/disk/devmapper.c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index abd54ba..6643b53 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2140,3 +2140,8 @@ module = {
name = progress;
common = lib/progress.c;
};
+
+module = {
+ name = crypto_devmapper;
+ common = disk/devmapper.c;
+};
\ No newline at end of file
diff --git a/grub-core/disk/devmapper.c b/grub-core/disk/devmapper.c
new file mode 100644
index 0000000..1ac8e5d
--- /dev/null
+++ b/grub-core/disk/devmapper.c
@@ -0,0 +1,414 @@
+/*
+ * devmapper.c - Device mapper (w/ crypto support)
+ *
+ * Copyright (C) 2007 Simon Peter
+ * Thanks to Raoul Boenisch for the initial idea.
+ */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2007 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define DEFAULT_HASH "ripemd160"
+#define DEFAULT_CIPHER "aes-cbc"
+#define MAX_KEYSIZE 64
+#define MAX_PASSPHRASE 256
+
+#define MIN(a, b) (a < b ? a : b)
+
+struct grub_crypto
+{
+ char *devname, *source_devname;
+ int has_partitions;
+ grub_crypto_cipher_handle_t cipher;
+ grub_disk_t srcdisk;
+ int keysize;
+
+ struct grub_crypto *next;
+};
+
+typedef struct grub_crypto *grub_crypto_t;
+
+struct crypto_private
+{
+ grub_crypto_t crypto;
+ grub_disk_t srcdisk;
+};
+
+typedef struct crypto_private *crypto_private_t;
+
+static grub_crypto_t crypto_list = NULL;
+
+/* Delete a registered crypto device. */
+static grub_err_t
+delete_crypto (const char *name)
+{
+ grub_crypto_t dev, *prev;
+
+ /* Search for the device */
+ for (dev = crypto_list, prev = &crypto_list; dev;
+ prev = &dev->next, dev = dev->next)
+ if (grub_strcmp (dev->devname, name) == 0)
+ break;
+
+ if (!dev)
+ return grub_error (GRUB_ERR_BAD_DEVICE, "Device not found");
+
+ /* Remove the device from the list */
+ *prev = dev->next;
+ grub_free (dev->devname);
+ grub_free (dev->source_devname);
+ grub_crypto_cipher_close (dev->cipher);
+ grub_free (dev);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Hashes a passphrase into a key and stores it with cipher. */
+static gcry_err_code_t
+set_passphrase (grub_crypto_t dev, const gcry_md_spec_t *hashparams,
+ const char *passphrase)
+{
+ grub_uint8_t hash[MAX_KEYSIZE * 2], *key = hash;
+ char *p;
+ unsigned int round, i, size = dev->keysize;
+ unsigned int len;
+
+ /* Need no passphrase if there's no key */
+ if (size == 0)
+ return GPG_ERR_INV_KEYLEN;
+
+ /* Hack to support the "none" hash */
+ if (hashparams)
+ len = hashparams->mdlen;
+ else
+ len = grub_strlen (passphrase);
+
+ if (size > MAX_KEYSIZE || len > MAX_KEYSIZE)
+ return GPG_ERR_INV_KEYLEN;
+
+ p = grub_malloc (grub_strlen (passphrase) + 2 + size / len);
+ if (!p)
+ return grub_errno;
+
+ for (round = 0; size; round++, key += len, size -= len)
+ {
+ /* hack from hashalot to avoid null bytes in key */
+ for (i = 0; i < round; i++)
+ p[i] = 'A';
+
+ grub_strcpy (p + i, passphrase);
+
+ if (len > size)
+ len = size;
+
+ grub_crypto_hash (hashparams, key, p, grub_strlen (p));
+ }
+
+ return grub_crypto_cipher_set_key (dev->cipher, hash, size);
+}
+
+/***** GRUB command line interface *****************************************/
+
+
+static const struct grub_arg_option options[] = {
+ {"delete", 'd', 0, "delete the crypto device entry", 0, ARG_TYPE_NONE},
+ {"partitions", 'p', 0, "set that the device has partitions", 0,
+ ARG_TYPE_NONE},
+ {"cipher", 'c', 0, "set cipher (default=" DEFAULT_CIPHER ")", 0,
+ ARG_TYPE_STRING},
+ {"hash", 'h', 0, "set hash function (default=" DEFAULT_HASH ")", 0,
+ ARG_TYPE_STRING},
+ {"passphrase", 'P', 0, "set decryption passphrase", 0, ARG_TYPE_STRING},
+ {"keysize", 'k', 0, "set key size (default is cipher specific)", 0,
+ ARG_TYPE_INT},
+ {0, 0, 0, 0, 0, 0}
+};
+
+static grub_err_t
+grub_cmd_devmap (grub_extcmd_t cmd, int argc, char **args)
+{
+ grub_disk_t disk;
+ grub_crypto_t newdev;
+ const char *cipher, *hash;
+ const gcry_md_spec_t *hashparams;
+ grub_err_t err = GRUB_ERR_NONE;
+ char *passphrase = "";
+ /* char cmdphrase[MAX_PASSPHRASE]; */
+ const gcry_cipher_spec_t *ciph;
+ struct grub_arg_list *state = cmd->state;
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Device name required");
+
+ /* Check whether delete is requested */
+ if (state[0].set)
+ return delete_crypto (args[0]);
+
+ if (argc < 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Source device name required");
+
+ /*** Create device is requested ***/
+
+ /* Choke on already existing devices */
+ for (newdev = crypto_list; newdev != NULL; newdev = newdev->next)
+ if (grub_strcmp (newdev->devname, args[0]) == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "Device already exists");
+
+ /* Check whether source device can be opened */
+ disk = grub_disk_open (args[1]);
+ if (!disk)
+ return grub_errno;
+ grub_disk_close (disk);
+
+ /* Parse remaining options */
+ if (state[2].set)
+ cipher = state[2].arg;
+ else
+ cipher = DEFAULT_CIPHER;
+ if (state[3].set)
+ hash = state[3].arg;
+ else
+ hash = DEFAULT_HASH;
+
+ /* Create new device entry */
+ newdev = grub_malloc (sizeof (struct grub_crypto));
+ if (!newdev)
+ return grub_errno;
+ newdev->devname = grub_strdup (args[0]);
+ if (!newdev->devname)
+ {
+ grub_free (newdev);
+ return grub_errno;
+ }
+ newdev->source_devname = grub_strdup (args[1]);
+ if (!newdev->source_devname)
+ {
+ grub_free (newdev->devname);
+ grub_free (newdev);
+ return grub_errno;
+ }
+ newdev->has_partitions = state[1].set;
+ ciph = grub_crypto_lookup_cipher_by_name (cipher);
+ if (!ciph)
+ {
+ grub_free (newdev->source_devname);
+ grub_free (newdev->devname);
+ grub_free (newdev);
+ return grub_error (GRUB_ERR_CIPHER_NOT_FOUND, "Unknown cipher %s", hash);
+ }
+ newdev->cipher = grub_crypto_cipher_open (ciph);
+ if (!newdev->cipher)
+ {
+ grub_free (newdev->source_devname);
+ grub_free (newdev->devname);
+ grub_free (newdev);
+ return grub_errno;
+ }
+ hashparams = grub_crypto_lookup_md_by_name (hash);
+ if (!hashparams)
+ {
+ grub_free (newdev->source_devname);
+ grub_free (newdev->devname);
+ grub_free (newdev);
+ grub_crypto_cipher_close (newdev->cipher);
+ return grub_error (GRUB_ERR_CIPHER_NOT_FOUND, "Unknown digest %s", hash);
+ }
+ newdev->srcdisk = NULL;
+ if (state[5].set)
+ newdev->keysize = grub_strtoul (state[5].arg, NULL, 10);
+ else
+ newdev->keysize = 16;
+
+ /* Get passphrase */
+ if (state[4].set) /* Passphrase supplied on commandline */
+ passphrase = state[4].arg;
+ else
+ {
+#if 1
+ return 0;
+#else
+ if (grub_strcmp (cipher, "none"))
+ {
+ grub_cmdline_get ("Passphrase: ", cmdphrase, MAX_PASSPHRASE, '*',
+ 0);
+ passphrase = cmdphrase;
+ }
+#endif
+ }
+ err = set_passphrase (newdev, hashparams, passphrase);
+ if (err)
+ {
+ grub_crypto_cipher_close (newdev->cipher);
+ grub_free (newdev->source_devname);
+ grub_free (newdev->devname);
+ grub_free (newdev);
+ return err;
+ }
+
+ /* Add new entry to list and return */
+ newdev->next = crypto_list;
+ crypto_list = newdev;
+
+ /* Error conditions */
+ return GRUB_ERR_NONE;
+}
+
+/***** GRUB disk device interface ******************************************/
+
+static int
+grub_crypto_iterate (int (*hook) (const char *name))
+{
+ grub_crypto_t i;
+
+ for (i = crypto_list; i != NULL; i = i->next)
+ if (hook (i->devname))
+ return 1;
+
+ return 0;
+}
+
+static grub_err_t
+grub_crypto_open (const char *name, grub_disk_t disk)
+{
+ grub_crypto_t dev;
+ crypto_private_t private;
+
+ for (dev = crypto_list; dev != NULL; dev = dev->next)
+ if (grub_strcmp (dev->devname, name) == 0)
+ break;
+
+ if (!dev)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device");
+
+ /* Setup crypto private structure */
+ if (!(private = grub_malloc (sizeof (struct crypto_private))))
+ return grub_errno;
+ private->crypto = dev;
+
+ /* Open underlying device */
+ private->srcdisk = grub_disk_open (dev->source_devname);
+ if (!private->srcdisk)
+ {
+ return grub_errno;
+ }
+
+ /* Populate requested disk */
+ disk->total_sectors = grub_disk_get_size (private->srcdisk);
+ disk->id = (int) dev;
+ disk->has_partitions = dev->has_partitions;
+ disk->data = private;
+
+ return 0;
+}
+
+static void
+grub_crypto_close (grub_disk_t disk)
+{
+ crypto_private_t private = (crypto_private_t) disk->data;
+
+ grub_disk_close (private->srcdisk);
+ grub_free (private);
+}
+
+static grub_err_t
+grub_crypto_read (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_size_t size, char *buf)
+{
+ crypto_private_t private = (crypto_private_t) disk->data;
+ grub_err_t err;
+ grub_crypto_cipher_handle_t cipher = private->crypto->cipher;
+ grub_size_t i;
+
+ /* Read sectors from underlying disk */
+ err =
+ grub_disk_read (private->srcdisk, sector, 0,
+ size << GRUB_DISK_SECTOR_BITS, buf);
+ if (err)
+ return err;
+
+ /* Decrypt sectors */
+ for (i = 0; i < size; i++)
+ {
+ grub_disk_addr_t s = grub_cpu_to_le64 (sector + i);
+ grub_uint8_t iv[cipher->cipher->blocksize];
+ gcry_err_code_t gcry_err;
+
+ /* Set IV from raw sector number (plain mode) */
+ grub_memset (iv, 0, cipher->cipher->blocksize);
+ grub_memcpy (iv, &s,
+ MIN (sizeof (grub_disk_addr_t),
+ cipher->cipher->blocksize));
+
+ gcry_err = grub_crypto_cbc_decrypt (cipher,
+ buf + (i << GRUB_DISK_SECTOR_BITS),
+ buf + (i << GRUB_DISK_SECTOR_BITS),
+ GRUB_DISK_SECTOR_SIZE, iv);
+ if (gcry_err)
+ return grub_crypto_gcry_error (gcry_err);
+ }
+
+ return 0;
+}
+
+static grub_err_t
+grub_crypto_write (grub_disk_t disk __attribute ((unused)),
+ grub_disk_addr_t sector __attribute ((unused)),
+ grub_size_t size __attribute ((unused)),
+ const char *buf __attribute ((unused)))
+{
+ return GRUB_ERR_NOT_IMPLEMENTED_YET;
+}
+
+static struct grub_disk_dev grub_crypto_dev = {
+ .name = "crypto",
+ .id = GRUB_DISK_DEVICE_DEVMAP_ID,
+ .iterate = grub_crypto_iterate,
+ .open = grub_crypto_open,
+ .close = grub_crypto_close,
+ .read = grub_crypto_read,
+ .write = grub_crypto_write,
+ .next = 0
+};
+
+/***** GRUB module (de-)initialization *************************************/
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT (devmapper)
+{
+ cmd = grub_register_extcmd ("devmap", grub_cmd_devmap, GRUB_COMMAND_FLAG_BOTH,
+ "devmap [OPTIONS...] [DEVICE] [SRC-DEV]",
+ "Map one device onto another (w/ cryptography support).",
+ options);
+ grub_disk_dev_register (&grub_crypto_dev);
+}
+
+GRUB_MOD_FINI (devmapper)
+{
+ grub_unregister_extcmd (cmd);
+ grub_disk_dev_unregister (&grub_crypto_dev);
+}
diff --git a/include/grub/disk.h b/include/grub/disk.h
index bf21473..003a2a0 100644
--- a/include/grub/disk.h
+++ b/include/grub/disk.h
@@ -48,6 +48,7 @@ enum grub_disk_dev_id
GRUB_DISK_DEVICE_PROCFS_ID,
GRUB_DISK_DEVICE_CBFSDISK_ID,
GRUB_DISK_DEVICE_UBOOTDISK_ID,
+ GRUB_DISK_DEVICE_DEVMAP_ID
};
struct grub_disk;
--
2.1.2