>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