From 7217f315d375c678d4f00a4601923769b9b5a6ad Mon Sep 17 00:00:00 2001 From: Michael Gorven Date: Thu, 21 Apr 2011 11:14:29 +0200 Subject: [PATCH 01/43] LUKS support based on work of Michael Gorven with some code from Clemens Fruhwirth and heavily cleaned up by me (phcoder) Also-By: Clemens Fruhwirth Also-By: Vladimir Serbinenko --- grub-core/Makefile.core.def | 6 + grub-core/disk/AFSplitter.c | 104 ++++++ grub-core/disk/luks.c | 700 ++++++++++++++++++++++++++++++++++++ 3 files changed, 810 insertions(+) create mode 100644 grub-core/disk/AFSplitter.c create mode 100644 grub-core/disk/luks.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index f4d38149d..b8c996ba7 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -767,6 +767,12 @@ module = { common = disk/loopback.c; }; +module = { + name = luks; + common = disk/luks.c; + common = disk/AFSplitter.c; +}; + module = { name = lvm; common = disk/lvm.c; diff --git a/grub-core/disk/AFSplitter.c b/grub-core/disk/AFSplitter.c new file mode 100644 index 000000000..6c3dc488e --- /dev/null +++ b/grub-core/disk/AFSplitter.c @@ -0,0 +1,104 @@ +/* + * AFsplitter - Anti forensic information splitter + * Copyright 2004, Clemens Fruhwirth + * + * AFsplitter diffuses information over a large stripe of data, + * therefor supporting secure data destruction. + * + * This program is grub_free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the grub_free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the grub_free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); +static void diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t size); +gcry_err_code_t AF_split (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); + + +static void +diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t size) +{ + grub_size_t i; + grub_uint32_t IV; /* host byte order independend hash IV */ + + grub_size_t fullblocks = size / hash->mdlen; + int padding = size % hash->mdlen; + grub_uint8_t final[hash->mdlen]; + grub_uint8_t temp[sizeof (IV) + hash->mdlen]; + + /* hash block the whole data set with different IVs to produce + * more than just a single data block + */ + for (i = 0; i < fullblocks; i++) + { + IV = grub_cpu_to_be32 (i); + grub_memcpy (temp, &IV, sizeof (IV)); + grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, hash->mdlen); + grub_crypto_hash (hash, dst + hash->mdlen * i, temp, + sizeof (IV) + hash->mdlen); + } + + if (padding) + { + IV = grub_cpu_to_be32 (i); + grub_memcpy (temp, &IV, sizeof (IV)); + grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, padding); + grub_crypto_hash (hash, final, temp, sizeof (IV) + padding); + grub_memcpy (dst + hash->mdlen * i, final, padding); + } +} + +/** + * Merges the splitted master key stored on disk into the original key + */ +gcry_err_code_t +AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst, + grub_size_t blocksize, grub_size_t blocknumbers) +{ + grub_size_t i; + grub_uint8_t *bufblock; + + bufblock = grub_zalloc (blocksize); + if (bufblock == NULL) + return GPG_ERR_OUT_OF_MEMORY; + + grub_memset (bufblock, 0, blocksize); + for (i = 0; i < blocknumbers - 1; i++) + { + grub_crypto_xor (bufblock, src + (blocksize * i), bufblock, blocksize); + diffuse (hash, bufblock, bufblock, blocksize); + } + grub_crypto_xor (dst, src + (i * blocksize), bufblock, blocksize); + + grub_free (bufblock); + return GPG_ERR_NO_ERROR; +} diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c new file mode 100644 index 000000000..47d10d63c --- /dev/null +++ b/grub-core/disk/luks.c @@ -0,0 +1,700 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011 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 +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define MAX_PASSPHRASE 256 + +#define LUKS_KEY_ENABLED 0x00AC71F3 +#define LUKS_STRIPES 4000 + +/* On disk LUKS header */ +struct grub_luks_phdr +{ + grub_uint8_t magic[6]; +#define LUKS_MAGIC "LUKS\xBA\xBE" + grub_uint16_t version; + char cipherName[32]; + char cipherMode[32]; + char hashSpec[32]; + grub_uint32_t payloadOffset; + grub_uint32_t keyBytes; + grub_uint8_t mkDigest[20]; + grub_uint8_t mkDigestSalt[32]; + grub_uint32_t mkDigestIterations; + grub_uint8_t uuid[40]; + struct + { + grub_uint32_t active; + grub_uint32_t passwordIterations; + grub_uint8_t passwordSalt[32]; + grub_uint32_t keyMaterialOffset; + grub_uint32_t stripes; + } keyblock[8]; +} __attribute__ ((packed)); + +typedef struct grub_luks_phdr *grub_luks_phdr_t; + +typedef enum +{ + GRUB_LUKS_MODE_ECB, + GRUB_LUKS_MODE_CBC_PLAIN, + GRUB_LUKS_MODE_CBC_ESSIV +} +luks_mode_t; + +struct grub_luks +{ + char *devname, *source; + grub_uint32_t offset; + grub_disk_t source_disk; + int ref; + grub_crypto_cipher_handle_t cipher; + grub_crypto_cipher_handle_t essiv_cipher; + luks_mode_t mode; + int id; + struct grub_luks *next; +}; +typedef struct grub_luks *grub_luks_t; + +static grub_luks_t luks_list = NULL; +static grub_uint8_t n = 0; + +gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); + +static const struct grub_arg_option options[] = + { + {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +static gcry_err_code_t +luks_decrypt (grub_crypto_cipher_handle_t cipher, luks_mode_t mode, + grub_uint8_t * data, grub_size_t len, + grub_size_t sector, grub_crypto_cipher_handle_t essiv_cipher) +{ + grub_size_t i; + gcry_err_code_t err; + + switch (mode) + { + case GRUB_LUKS_MODE_ECB: + return grub_crypto_ecb_decrypt (cipher, data, data, len); + + case GRUB_LUKS_MODE_CBC_PLAIN: + for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + { + grub_uint32_t iv[(cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)]; + grub_memset (iv, 0, cipher->cipher->blocksize); + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + err = grub_crypto_cbc_decrypt (cipher, data + i, data + i, + GRUB_DISK_SECTOR_SIZE, iv); + if (err) + return err; + sector++; + } + return GPG_ERR_NO_ERROR; + + case GRUB_LUKS_MODE_CBC_ESSIV: + for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + { + grub_uint32_t iv[(cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)]; + grub_memset (iv, 0, cipher->cipher->blocksize); + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + err = + grub_crypto_ecb_encrypt (essiv_cipher, iv, iv, + cipher->cipher->blocksize); + if (err) + return err; + err = grub_crypto_cbc_decrypt (cipher, data + i, data + i, + GRUB_DISK_SECTOR_SIZE, iv); + if (err) + return err; + sector++; + } + return GPG_ERR_NO_ERROR; + + default: + return GPG_ERR_NOT_IMPLEMENTED; + } +} + +static int check_uuid, have_it; +static char *uuid; + +static grub_err_t +grub_luks_scan_device_real (const char *name) +{ + grub_disk_t source; + grub_err_t err; + grub_luks_t newdev; + struct grub_luks_phdr header; + grub_crypto_cipher_handle_t cipher = NULL, essiv_cipher = NULL; + const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; + grub_size_t keysize; + /* GCC thinks we may use this variable uninitialised. Silence the warning. */ + grub_size_t essiv_keysize = 0; + grub_uint8_t *hashed_key = NULL; + luks_mode_t mode; + grub_uint8_t *split_key = NULL; + unsigned i; + grub_size_t length; + const struct gcry_cipher_spec *ciph; + char passphrase[MAX_PASSPHRASE] = ""; + grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; + + /* Try to open disk. */ + source = grub_disk_open (name); + if (!source) + return grub_errno; + + /* Read the LUKS header. */ + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + { + grub_disk_close (source); + return err; + } + + /* Look for LUKS magic sequence. */ + if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) + || grub_be_to_cpu16 (header.version) != 1) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } + + if (check_uuid && grub_memcmp (header.uuid, uuid, + sizeof (header.uuid)) != 0) + return 0; + + newdev = grub_zalloc (sizeof (struct grub_luks)); + if (!newdev) + { + grub_disk_close (source); + return grub_errno; + } + + newdev->id = n; + newdev->devname = grub_xasprintf ("luks%d", n); + newdev->source = grub_strdup (name); + n++; + + /* Make sure that strings are null terminated. */ + header.cipherName[sizeof (header.cipherName) - 1] = 0; + header.cipherMode[sizeof (header.cipherMode) - 1] = 0; + header.hashSpec[sizeof (header.hashSpec) - 1] = 0; + + ciph = grub_crypto_lookup_cipher_by_name (header.cipherName); + if (!ciph) + { + grub_disk_close (source); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + header.cipherName); + } + + /* Configure the cipher used for the bulk data. */ + cipher = grub_crypto_cipher_open (ciph); + if (!cipher) + { + grub_disk_close (source); + grub_free (newdev); + return grub_errno; + } + + keysize = grub_be_to_cpu32 (header.keyBytes); + if (keysize > 1024) + { + grub_disk_close (source); + grub_free (newdev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", + keysize); + } + + /* Configure the cipher mode. */ + if (grub_strncmp (header.cipherMode, "ecb", 3) == 0) + mode = GRUB_LUKS_MODE_ECB; + else if (grub_strncmp (header.cipherMode, "cbc-plain", 9) == 0 + || grub_strncmp (header.cipherMode, "plain", 5) == 0) + mode = GRUB_LUKS_MODE_CBC_PLAIN; + else if (grub_strncmp (header.cipherMode, "cbc-essiv", 9) == 0) + { + mode = GRUB_LUKS_MODE_CBC_ESSIV; + char *hash_str = header.cipherMode + 10; + + /* Configure the hash and cipher used for ESSIV. */ + essiv_hash = grub_crypto_lookup_md_by_name (hash_str); + if (!essiv_hash) + { + grub_crypto_cipher_close (cipher); + grub_disk_close (source); + grub_free (newdev); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Couldn't load %s hash", hash_str); + } + essiv_cipher = grub_crypto_cipher_open (ciph); + if (!cipher) + { + grub_crypto_cipher_close (cipher); + grub_disk_close (source); + grub_free (newdev); + return grub_errno; + } + + essiv_keysize = essiv_hash->mdlen; + hashed_key = grub_malloc (essiv_hash->mdlen); + if (!hashed_key) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_disk_close (source); + grub_free (newdev); + return grub_errno; + } + } + else + { + grub_crypto_cipher_close (cipher); + grub_disk_close (source); + grub_free (newdev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", + header.cipherMode); + } + + /* Configure the hash used for the AF splitter and HMAC. */ + hash = grub_crypto_lookup_md_by_name (header.hashSpec); + if (!hash) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_disk_close (source); + grub_free (newdev); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + header.hashSpec); + } + + grub_printf ("Attempting to decrypt master key...\n"); + + grub_uint8_t candidate_key[keysize]; + grub_uint8_t digest[keysize]; + + split_key = grub_malloc (keysize * LUKS_STRIPES); + if (!split_key) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_disk_close (source); + grub_free (newdev); + return grub_errno; + } + + /* Get the passphrase from the user. */ + grub_printf ("Enter passphrase: "); + if (!grub_password_get (passphrase, MAX_PASSPHRASE)) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + } + + /* Try to recover master key from each active keyslot. */ + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + { + gcry_err_code_t gcry_err; + + /* Check if keyslot is enabled. */ + if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) + continue; + + grub_dprintf ("luks", "Trying keyslot %d\n", i); + + /* Calculate the PBKDF2 of the user supplied passphrase. */ + gcry_err = grub_crypto_pbkdf2 (hash, (grub_uint8_t *) passphrase, + grub_strlen (passphrase), + header.keyblock[i].passwordSalt, + sizeof (header. + keyblock[i].passwordSalt), + grub_be_to_cpu32 (header.keyblock[i]. + passwordIterations), + digest, keysize); + + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "PBKDF2 done\n"); + + /* Set the PBKDF2 output as the cipher key. */ + gcry_err = grub_crypto_cipher_set_key (cipher, digest, keysize); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + /* Configure ESSIV if necessary. */ + if (mode == GRUB_LUKS_MODE_CBC_ESSIV) + { + grub_crypto_hash (essiv_hash, hashed_key, digest, keysize); + grub_crypto_cipher_set_key (essiv_cipher, hashed_key, + essiv_keysize); + } + + length = + grub_be_to_cpu32 (header.keyBytes) * + grub_be_to_cpu32 (header.keyblock[i].stripes); + + /* Read and decrypt the key material from the disk. */ + err = grub_disk_read (source, + grub_be_to_cpu32 (header.keyblock + [i].keyMaterialOffset), 0, + length, split_key); + if (err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return err; + } + + gcry_err = luks_decrypt (cipher, mode, split_key, + length, 0, essiv_cipher); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + /* Merge the decrypted key material to get the candidate master key. */ + gcry_err = AF_merge (hash, split_key, candidate_key, keysize, + grub_be_to_cpu32 (header.keyblock[i].stripes)); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "candidate key recovered\n"); + + /* Calculate the PBKDF2 of the candidate master key. */ + gcry_err = grub_crypto_pbkdf2 (hash, candidate_key, + grub_be_to_cpu32 (header.keyBytes), + header.mkDigestSalt, + sizeof (header.mkDigestSalt), + grub_be_to_cpu32 + (header.mkDigestIterations), + candidate_digest, + sizeof (candidate_digest)); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_disk_close (source); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + /* Compare the calculated PBKDF2 to the digest stored + in the header to see if it's correct. */ + if (grub_memcmp (candidate_digest, header.mkDigest, + sizeof (header.mkDigest)) != 0) + continue; + + grub_disk_close (source); + + grub_printf ("Slot %d opened\n", i); + + /* Set the master key. */ + gcry_err = grub_crypto_cipher_set_key (cipher, candidate_key, keysize); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + + newdev->cipher = cipher; + + /* Configure ESSIV if necessary. */ + if (mode == GRUB_LUKS_MODE_CBC_ESSIV) + { + grub_crypto_hash (essiv_hash, hashed_key, candidate_key, keysize); + gcry_err = + grub_crypto_cipher_set_key (essiv_cipher, hashed_key, + essiv_keysize); + if (gcry_err) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); + grub_free (hashed_key); + grub_free (split_key); + grub_free (newdev); + return grub_crypto_gcry_error (gcry_err); + } + newdev->essiv_cipher = essiv_cipher; + } + else + { + newdev->essiv_cipher = NULL; + } + + newdev->offset = grub_be_to_cpu32 (header.payloadOffset); + newdev->source_disk = NULL; + newdev->mode = mode; + + grub_free (split_key); + grub_free (hashed_key); + newdev->next = luks_list; + luks_list = newdev; + + have_it = 1; + + return GRUB_ERR_NONE; + } + + return GRUB_ACCESS_DENIED; +} + +static int +grub_luks_scan_device (const char *name) +{ + grub_err_t err; + err = grub_luks_scan_device_real (name); + if (err) + grub_print_error (); + return have_it && check_uuid ? 0 : 1; +} + +static int +grub_luks_iterate (int (*hook) (const char *name)) +{ + grub_luks_t i; + + for (i = luks_list; i != NULL; i = i->next) + if (hook (i->devname)) + return 1; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_luks_open (const char *name, grub_disk_t disk) +{ + grub_luks_t dev; + + /* Search for requested device in the list of LUKS devices. */ + for (dev = luks_list; dev != NULL; dev = dev->next) + if (grub_strcmp (dev->devname, name) == 0) + break; + + if (!dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + + if (!dev->source_disk) + { + grub_dprintf ("luks", "Opening device %s\n", name); + /* Try to open the source disk and populate the requested disk. */ + dev->source_disk = grub_disk_open (dev->source); + if (!dev->source_disk) + return grub_errno; + } + + disk->data = dev; + disk->total_sectors = grub_disk_get_size (dev->source_disk) - dev->offset; + disk->id = dev->id; + dev->ref++; + return GRUB_ERR_NONE; +} + +static void +grub_luks_close (grub_disk_t disk) +{ + grub_luks_t dev = (grub_luks_t) disk->data; + grub_dprintf ("luks", "Closing disk\n"); + + dev->ref--; + + if (dev->ref == 0) + { + grub_disk_close (dev->source_disk); + dev->source_disk = NULL; + } +} + +static grub_err_t +grub_luks_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_luks_t dev = (grub_luks_t) disk->data; + grub_err_t err; + grub_dprintf ("luks", + "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" + PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT32_T "\n", + size, sector, dev->offset); + + err = grub_disk_read (dev->source_disk, sector + dev->offset, 0, + size << GRUB_DISK_SECTOR_BITS, buf); + if (err) + { + grub_dprintf ("luks", "grub_disk_read failed with error %d\n", err); + return err; + } + return grub_crypto_gcry_error (luks_decrypt (dev->cipher, + dev->mode, + (grub_uint8_t *) buf, + size << GRUB_DISK_SECTOR_BITS, + sector, dev->essiv_cipher)); +} + +static grub_err_t +grub_luks_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 void +luks_cleanup (void) +{ + grub_luks_t dev = luks_list; + grub_luks_t tmp; + + while (dev != NULL) + { + grub_free (dev->devname); + grub_free (dev->source); + grub_free (dev->cipher); + grub_free (dev->essiv_cipher); + tmp = dev->next; + grub_free (dev); + dev = tmp; + } +} + +static grub_err_t +grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + have_it = 0; + if (state[0].set) + { + check_uuid = 1; + uuid = args[0]; + grub_device_iterate (&grub_luks_scan_device); + uuid = NULL; + } + else + { + grub_err_t err; + check_uuid = 0; + uuid = NULL; + err = grub_luks_scan_device_real (args[0]); + return err; + } + if (!have_it) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); + return 0; +} + +static struct grub_disk_dev grub_luks_dev = { + .name = "luks", + .id = GRUB_DISK_DEVICE_LUKS_ID, + .iterate = grub_luks_iterate, + .open = grub_luks_open, + .close = grub_luks_close, + .read = grub_luks_read, + .write = grub_luks_write, + .next = 0 +}; + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT (luks) +{ + cmd = grub_register_extcmd ("luksmount", grub_cmd_luksmount, 0, + N_("SOURCE|-u UUID"), + N_("Mount a LUKS device."), options); + grub_disk_dev_register (&grub_luks_dev); +} + +GRUB_MOD_FINI (luks) +{ + grub_unregister_extcmd (cmd); + grub_disk_dev_unregister (&grub_luks_dev); + luks_cleanup (); +} From 5709ed126d9155ae06c284f9c8fceb11227cc73d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 21 Apr 2011 11:17:01 +0200 Subject: [PATCH 02/43] small cleanup --- grub-core/disk/AFSplitter.c | 19 ++----------------- grub-core/disk/luks.c | 2 -- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/grub-core/disk/AFSplitter.c b/grub-core/disk/AFSplitter.c index 6c3dc488e..ebcc35221 100644 --- a/grub-core/disk/AFSplitter.c +++ b/grub-core/disk/AFSplitter.c @@ -20,28 +20,13 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst, grub_size_t blocksize, grub_size_t blocknumbers); -static void diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src, - grub_uint8_t * dst, grub_size_t size); -gcry_err_code_t AF_split (const gcry_md_spec_t * hash, grub_uint8_t * src, - grub_uint8_t * dst, grub_size_t blocksize, - grub_size_t blocknumbers); - static void diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src, diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 47d10d63c..78fcabb03 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -20,11 +20,9 @@ #include #include #include -#include #include #include #include -#include #include #include From a89c3dd3f7f587447b61cdfb4de587951217fca2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 21 Apr 2011 11:38:51 +0200 Subject: [PATCH 03/43] Don't mount the same LUKS volume twice --- grub-core/disk/luks.c | 171 ++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 96 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 78fcabb03..160d1102c 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -47,7 +47,7 @@ struct grub_luks_phdr grub_uint8_t mkDigest[20]; grub_uint8_t mkDigestSalt[32]; grub_uint32_t mkDigestIterations; - grub_uint8_t uuid[40]; + char uuid[40]; struct { grub_uint32_t active; @@ -65,8 +65,7 @@ typedef enum GRUB_LUKS_MODE_ECB, GRUB_LUKS_MODE_CBC_PLAIN, GRUB_LUKS_MODE_CBC_ESSIV -} -luks_mode_t; +} luks_mode_t; struct grub_luks { @@ -77,7 +76,9 @@ struct grub_luks grub_crypto_cipher_handle_t cipher; grub_crypto_cipher_handle_t essiv_cipher; luks_mode_t mode; - int id; + unsigned long id, source_id; + enum grub_disk_dev_id source_dev_id; + char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid)]; struct grub_luks *next; }; typedef struct grub_luks *grub_luks_t; @@ -154,11 +155,9 @@ static int check_uuid, have_it; static char *uuid; static grub_err_t -grub_luks_scan_device_real (const char *name) +grub_luks_scan_device_real (const char *name, grub_disk_t source) { - grub_disk_t source; grub_err_t err; - grub_luks_t newdev; struct grub_luks_phdr header; grub_crypto_cipher_handle_t cipher = NULL, essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; @@ -174,73 +173,38 @@ grub_luks_scan_device_real (const char *name) char passphrase[MAX_PASSPHRASE] = ""; grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; - /* Try to open disk. */ - source = grub_disk_open (name); - if (!source) - return grub_errno; - /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); if (err) - { - grub_disk_close (source); - return err; - } + return err; /* Look for LUKS magic sequence. */ if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) || grub_be_to_cpu16 (header.version) != 1) - { - grub_disk_close (source); - return GRUB_ERR_NONE; - } - - if (check_uuid && grub_memcmp (header.uuid, uuid, - sizeof (header.uuid)) != 0) - return 0; - - newdev = grub_zalloc (sizeof (struct grub_luks)); - if (!newdev) - { - grub_disk_close (source); - return grub_errno; - } - - newdev->id = n; - newdev->devname = grub_xasprintf ("luks%d", n); - newdev->source = grub_strdup (name); - n++; + return GRUB_ERR_NONE; /* Make sure that strings are null terminated. */ header.cipherName[sizeof (header.cipherName) - 1] = 0; header.cipherMode[sizeof (header.cipherMode) - 1] = 0; header.hashSpec[sizeof (header.hashSpec) - 1] = 0; + header.uuid[sizeof (header.uuid) - 1] = 0; + + if (check_uuid && grub_strcmp (header.uuid, uuid) != 0) + return 0; ciph = grub_crypto_lookup_cipher_by_name (header.cipherName); if (!ciph) - { - grub_disk_close (source); - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", - header.cipherName); - } + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + header.cipherName); /* Configure the cipher used for the bulk data. */ cipher = grub_crypto_cipher_open (ciph); if (!cipher) - { - grub_disk_close (source); - grub_free (newdev); - return grub_errno; - } + return grub_errno; keysize = grub_be_to_cpu32 (header.keyBytes); if (keysize > 1024) - { - grub_disk_close (source); - grub_free (newdev); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", - keysize); - } + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", keysize); /* Configure the cipher mode. */ if (grub_strncmp (header.cipherMode, "ecb", 3) == 0) @@ -258,8 +222,6 @@ grub_luks_scan_device_real (const char *name) if (!essiv_hash) { grub_crypto_cipher_close (cipher); - grub_disk_close (source); - grub_free (newdev); return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", hash_str); } @@ -267,8 +229,6 @@ grub_luks_scan_device_real (const char *name) if (!cipher) { grub_crypto_cipher_close (cipher); - grub_disk_close (source); - grub_free (newdev); return grub_errno; } @@ -278,16 +238,12 @@ grub_luks_scan_device_real (const char *name) { grub_crypto_cipher_close (cipher); grub_crypto_cipher_close (essiv_cipher); - grub_disk_close (source); - grub_free (newdev); return grub_errno; } } else { grub_crypto_cipher_close (cipher); - grub_disk_close (source); - grub_free (newdev); return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", header.cipherMode); } @@ -299,8 +255,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (cipher); grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); - grub_disk_close (source); - grub_free (newdev); return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", header.hashSpec); } @@ -316,8 +270,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (cipher); grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); - grub_disk_close (source); - grub_free (newdev); return grub_errno; } @@ -329,8 +281,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); } @@ -361,8 +311,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } @@ -376,8 +324,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } @@ -404,8 +350,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return err; } @@ -417,8 +361,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } @@ -431,8 +373,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } @@ -453,8 +393,6 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_disk_close (source); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } @@ -464,8 +402,6 @@ grub_luks_scan_device_real (const char *name) sizeof (header.mkDigest)) != 0) continue; - grub_disk_close (source); - grub_printf ("Slot %d opened\n", i); /* Set the master key. */ @@ -476,12 +412,9 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } - newdev->cipher = cipher; - /* Configure ESSIV if necessary. */ if (mode == GRUB_LUKS_MODE_CBC_ESSIV) { @@ -495,24 +428,33 @@ grub_luks_scan_device_real (const char *name) grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); - grub_free (newdev); return grub_crypto_gcry_error (gcry_err); } - newdev->essiv_cipher = essiv_cipher; - } - else - { - newdev->essiv_cipher = NULL; } - newdev->offset = grub_be_to_cpu32 (header.payloadOffset); - newdev->source_disk = NULL; - newdev->mode = mode; + { + grub_luks_t newdev; + newdev = grub_zalloc (sizeof (struct grub_luks)); + if (!newdev) + return grub_errno; + newdev->id = n; + newdev->devname = grub_xasprintf ("luks%d", n); + newdev->source = grub_strdup (name); + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->cipher = cipher; + newdev->offset = grub_be_to_cpu32 (header.payloadOffset); + newdev->source_disk = NULL; + newdev->mode = mode; + newdev->essiv_cipher = essiv_cipher; + grub_memcpy (newdev->uuid, header.uuid, sizeof (newdev->uuid)); + newdev->next = luks_list; + luks_list = newdev; + n++; + } grub_free (split_key); grub_free (hashed_key); - newdev->next = luks_list; - luks_list = newdev; have_it = 1; @@ -526,7 +468,17 @@ static int grub_luks_scan_device (const char *name) { grub_err_t err; - err = grub_luks_scan_device_real (name); + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (name); + if (!source) + return grub_errno; + + err = grub_luks_scan_device_real (name, source); + + grub_disk_close (source); + if (err) grub_print_error (); return have_it && check_uuid ? 0 : 1; @@ -651,6 +603,15 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) have_it = 0; if (state[0].set) { + grub_luks_t dev; + + for (dev = luks_list; dev != NULL; dev = dev->next) + if (grub_strcmp (dev->uuid, args[0]) == 0) + { + grub_dprintf ("luks", "already mounted as %s\n", dev->devname); + return GRUB_ERR_NONE; + } + check_uuid = 1; uuid = args[0]; grub_device_iterate (&grub_luks_scan_device); @@ -659,9 +620,27 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) else { grub_err_t err; + grub_disk_t disk; + grub_luks_t dev; + check_uuid = 0; uuid = NULL; - err = grub_luks_scan_device_real (args[0]); + disk = grub_disk_open (args[0]); + if (!disk) + return grub_errno; + + for (dev = luks_list; dev != NULL; dev = dev->next) + if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) + { + grub_dprintf ("luks", "already mounted as %s\n", dev->devname); + grub_disk_close (disk); + return GRUB_ERR_NONE; + } + + err = grub_luks_scan_device_real (args[0], disk); + + grub_disk_close (disk); + return err; } if (!have_it) From 79cde98f5d1c77697e8e3713987136cbf35a13c8 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 21 Apr 2011 11:58:06 +0200 Subject: [PATCH 04/43] Support luksuuid specification --- grub-core/disk/luks.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 160d1102c..3ab6b844d 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -69,7 +69,7 @@ typedef enum struct grub_luks { - char *devname, *source; + char *source; grub_uint32_t offset; grub_disk_t source_disk; int ref; @@ -438,7 +438,6 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) if (!newdev) return grub_errno; newdev->id = n; - newdev->devname = grub_xasprintf ("luks%d", n); newdev->source = grub_strdup (name); newdev->source_id = source->id; newdev->source_dev_id = source->dev->id; @@ -490,8 +489,12 @@ grub_luks_iterate (int (*hook) (const char *name)) grub_luks_t i; for (i = luks_list; i != NULL; i = i->next) - if (hook (i->devname)) - return 1; + { + char buf[30]; + grub_snprintf (buf, sizeof (buf), "luks%lu", i->id); + if (hook (buf)) + return 1; + } return GRUB_ERR_NONE; } @@ -501,11 +504,25 @@ grub_luks_open (const char *name, grub_disk_t disk) { grub_luks_t dev; - /* Search for requested device in the list of LUKS devices. */ - for (dev = luks_list; dev != NULL; dev = dev->next) - if (grub_strcmp (dev->devname, name) == 0) - break; + if (grub_memcmp (name, "luks", sizeof ("luks") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + if (grub_memcmp (name, "luksuuid/", sizeof ("luksuuid/") - 1) == 0) + { + for (dev = luks_list; dev != NULL; dev = dev->next) + if (grub_strcmp (name + sizeof ("luksuuid/") - 1, dev->uuid) == 0) + break; + } + else + { + unsigned long id = grub_strtoul (name + sizeof ("luks") - 1, 0, 0); + if (grub_errno) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + /* Search for requested device in the list of LUKS devices. */ + for (dev = luks_list; dev != NULL; dev = dev->next) + if (dev->id == id) + break; + } if (!dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); @@ -582,7 +599,6 @@ luks_cleanup (void) while (dev != NULL) { - grub_free (dev->devname); grub_free (dev->source); grub_free (dev->cipher); grub_free (dev->essiv_cipher); @@ -608,7 +624,7 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) for (dev = luks_list; dev != NULL; dev = dev->next) if (grub_strcmp (dev->uuid, args[0]) == 0) { - grub_dprintf ("luks", "already mounted as %s\n", dev->devname); + grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); return GRUB_ERR_NONE; } @@ -632,7 +648,7 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) for (dev = luks_list; dev != NULL; dev = dev->next) if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) { - grub_dprintf ("luks", "already mounted as %s\n", dev->devname); + grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); grub_disk_close (disk); return GRUB_ERR_NONE; } From 64516e9df6de88b84e53e7b84a25bff2130c00ae Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 21 Apr 2011 12:39:14 +0200 Subject: [PATCH 05/43] Fix couple of UUID problems --- grub-core/disk/luks.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 3ab6b844d..bc53e0a2b 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -78,7 +78,7 @@ struct grub_luks luks_mode_t mode; unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; - char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid)]; + char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; struct grub_luks *next; }; typedef struct grub_luks *grub_luks_t; @@ -152,7 +152,7 @@ luks_decrypt (grub_crypto_cipher_handle_t cipher, luks_mode_t mode, } static int check_uuid, have_it; -static char *uuid; +static char *search_uuid; static grub_err_t grub_luks_scan_device_real (const char *name, grub_disk_t source) @@ -172,6 +172,8 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) const struct gcry_cipher_spec *ciph; char passphrase[MAX_PASSPHRASE] = ""; grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; + char uuid[sizeof (header.uuid) + 1]; + char *iptr, *optr; /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); @@ -189,8 +191,20 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) header.hashSpec[sizeof (header.hashSpec) - 1] = 0; header.uuid[sizeof (header.uuid) - 1] = 0; - if (check_uuid && grub_strcmp (header.uuid, uuid) != 0) - return 0; + optr = uuid; + for (iptr = header.uuid; iptr < &header.uuid[ARRAY_SIZE (header.uuid)]; + iptr++) + { + if (*iptr != '-') + *optr++ = *iptr; + } + *optr = 0; + + if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) + { + grub_dprintf ("luks", "%s != %s", uuid, search_uuid); + return 0; + } ciph = grub_crypto_lookup_cipher_by_name (header.cipherName); if (!ciph) @@ -274,7 +288,7 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) } /* Get the passphrase from the user. */ - grub_printf ("Enter passphrase: "); + grub_printf ("Enter passphrase for %s (%s): ", name, uuid); if (!grub_password_get (passphrase, MAX_PASSPHRASE)) { grub_crypto_cipher_close (cipher); @@ -446,7 +460,7 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) newdev->source_disk = NULL; newdev->mode = mode; newdev->essiv_cipher = essiv_cipher; - grub_memcpy (newdev->uuid, header.uuid, sizeof (newdev->uuid)); + grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); newdev->next = luks_list; luks_list = newdev; n++; @@ -510,7 +524,7 @@ grub_luks_open (const char *name, grub_disk_t disk) if (grub_memcmp (name, "luksuuid/", sizeof ("luksuuid/") - 1) == 0) { for (dev = luks_list; dev != NULL; dev = dev->next) - if (grub_strcmp (name + sizeof ("luksuuid/") - 1, dev->uuid) == 0) + if (grub_strcasecmp (name + sizeof ("luksuuid/") - 1, dev->uuid) == 0) break; } else @@ -622,16 +636,20 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) grub_luks_t dev; for (dev = luks_list; dev != NULL; dev = dev->next) - if (grub_strcmp (dev->uuid, args[0]) == 0) + if (grub_strcasecmp (dev->uuid, args[0]) == 0) { grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); return GRUB_ERR_NONE; } check_uuid = 1; - uuid = args[0]; + search_uuid = args[0]; grub_device_iterate (&grub_luks_scan_device); - uuid = NULL; + search_uuid = NULL; + + if (!have_it) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); + return GRUB_ERR_NONE; } else { @@ -640,7 +658,7 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) grub_luks_t dev; check_uuid = 0; - uuid = NULL; + search_uuid = NULL; disk = grub_disk_open (args[0]); if (!disk) return grub_errno; @@ -659,9 +677,6 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) return err; } - if (!have_it) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); - return 0; } static struct grub_disk_dev grub_luks_dev = { From a10e7a5a8918bea6e2632055129fa9b516fe965a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 21 Apr 2011 12:39:31 +0200 Subject: [PATCH 06/43] Support grub-probe -t drive --- grub-core/kern/emu/getroot.c | 75 +++++++++++++++++++++++++++--------- include/grub/emu/getroot.h | 1 + 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index f47203cfc..40cbc4998 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -640,11 +640,11 @@ grub_guess_root_device (const char *dir) return os_dev; } -static int -grub_util_is_lvm (const char *os_dev) +static char * +get_dm_uuid (const char *os_dev) { if ((strncmp ("/dev/mapper/", os_dev, 12) != 0)) - return 0; + return NULL; #ifdef HAVE_DEVICE_MAPPER { @@ -652,17 +652,18 @@ grub_util_is_lvm (const char *os_dev) uint32_t maj, min; struct dm_tree_node *node = NULL; const char *node_uuid; + char *ret; struct stat st; if (stat (os_dev, &st) < 0) - return 0; + return NULL; tree = dm_tree_create (); if (! tree) { grub_printf ("Failed to create tree\n"); grub_dprintf ("hostdisk", "dm_tree_create failed\n"); - return 0; + return NULL; } maj = major (st.st_rdev); @@ -672,7 +673,7 @@ grub_util_is_lvm (const char *os_dev) { grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n"); dm_tree_free (tree); - return 0; + return NULL; } node = dm_tree_find_node (tree, maj, min); @@ -680,39 +681,64 @@ grub_util_is_lvm (const char *os_dev) { grub_dprintf ("hostdisk", "dm_tree_find_node failed\n"); dm_tree_free (tree); - return 0; + return NULL; } node_uuid = dm_tree_node_get_uuid (node); if (! node_uuid) { grub_dprintf ("hostdisk", "%s has no DM uuid\n", os_dev); dm_tree_free (tree); - return 0; - } - if (strncmp (node_uuid, "LVM-", 4) != 0) - { - dm_tree_free (tree); - return 0; + return NULL; } + + ret = grub_strdup (node_uuid); dm_tree_free (tree); - return 1; + return ret; } #else - return 1; + return NULL; #endif /* HAVE_DEVICE_MAPPER */ } +static enum grub_dev_abstraction_types +grub_util_get_dm_abstraction (const char *os_dev) +{ + char *uuid; + uuid = get_dm_uuid (os_dev); + + if (uuid == NULL) + return GRUB_DEV_ABSTRACTION_NONE; + + if (strncmp (uuid, "LVM-", 4) == 0) + { + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_LVM; + } + if (strncmp (uuid, "CRYPT-LUKS1-", 4) == 0) + { + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_LUKS; + } + + grub_free (uuid); + return GRUB_DEV_ABSTRACTION_NONE; +} + int grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused))) { #ifdef __linux__ + enum grub_dev_abstraction_types ret; + /* User explicitly claims that this drive is visible by BIOS. */ if (grub_util_biosdisk_is_present (os_dev)) return GRUB_DEV_ABSTRACTION_NONE; - /* Check for LVM. */ - if (grub_util_is_lvm (os_dev)) - return GRUB_DEV_ABSTRACTION_LVM; + /* Check for LVM and LUKS. */ + ret = grub_util_get_dm_abstraction (os_dev); + + if (ret != GRUB_DEV_ABSTRACTION_NONE) + return ret; /* Check for RAID. */ if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev)) @@ -830,6 +856,19 @@ grub_util_get_grub_dev (const char *os_dev) break; + case GRUB_DEV_ABSTRACTION_LUKS: + { + char *uuid, *dash; + uuid = get_dm_uuid (os_dev); + dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-'); + if (dash) + *dash = 0; + grub_dev = grub_xasprintf ("luksuuid/%s", + uuid + sizeof ("CRYPT-LUKS1-") - 1); + grub_free (uuid); + } + break; + case GRUB_DEV_ABSTRACTION_RAID: if (os_dev[7] == '_' && os_dev[8] == 'd') diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 581ea8056..14df583b3 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -25,6 +25,7 @@ enum grub_dev_abstraction_types { GRUB_DEV_ABSTRACTION_NONE, GRUB_DEV_ABSTRACTION_LVM, GRUB_DEV_ABSTRACTION_RAID, + GRUB_DEV_ABSTRACTION_LUKS, }; char *grub_find_device (const char *dir, dev_t dev); From f3470f4eb51026e410c687f10065a1fc797ba384 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 16:32:27 +0200 Subject: [PATCH 07/43] restructure prior to adding cheatmounts --- grub-core/disk/luks.c | 302 +++++++++++++++++++++++------------------- 1 file changed, 166 insertions(+), 136 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 12a37b98b..4d907b4c6 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -75,6 +75,7 @@ struct grub_luks int ref; grub_crypto_cipher_handle_t cipher; grub_crypto_cipher_handle_t essiv_cipher; + const gcry_md_spec_t *essiv_hash, *hash; luks_mode_t mode; unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; @@ -154,45 +155,28 @@ luks_decrypt (grub_crypto_cipher_handle_t cipher, luks_mode_t mode, static int check_uuid, have_it; static char *search_uuid; -static grub_err_t -grub_luks_scan_device_real (const char *name, grub_disk_t source) +static grub_luks_t +configure_ciphers (const struct grub_luks_phdr *header) { - grub_err_t err; - struct grub_luks_phdr header; + grub_luks_t newdev; + const char *iptr; + char *optr; + char uuid[sizeof (header->uuid) + 1]; + char ciphername[sizeof (header->cipherName) + 1]; + char ciphermode[sizeof (header->cipherMode) + 1]; + char hashspec[sizeof (header->hashSpec) + 1]; grub_crypto_cipher_handle_t cipher = NULL, essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; - grub_size_t keysize; - /* GCC thinks we may use this variable uninitialised. Silence the warning. */ - grub_size_t essiv_keysize = 0; - grub_uint8_t *hashed_key = NULL; - luks_mode_t mode; - grub_uint8_t *split_key = NULL; - unsigned i; - grub_size_t length; const struct gcry_cipher_spec *ciph; - char passphrase[MAX_PASSPHRASE] = ""; - grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; - char uuid[sizeof (header.uuid) + 1]; - char *iptr, *optr; - - /* Read the LUKS header. */ - err = grub_disk_read (source, 0, 0, sizeof (header), &header); - if (err) - return err; - + luks_mode_t mode; + /* Look for LUKS magic sequence. */ - if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) - || grub_be_to_cpu16 (header.version) != 1) - return GRUB_ERR_NONE; - - /* Make sure that strings are null terminated. */ - header.cipherName[sizeof (header.cipherName) - 1] = 0; - header.cipherMode[sizeof (header.cipherMode) - 1] = 0; - header.hashSpec[sizeof (header.hashSpec) - 1] = 0; - header.uuid[sizeof (header.uuid) - 1] = 0; + if (grub_memcmp (header->magic, LUKS_MAGIC, sizeof (header->magic)) + || grub_be_to_cpu16 (header->version) != 1) + return NULL; optr = uuid; - for (iptr = header.uuid; iptr < &header.uuid[ARRAY_SIZE (header.uuid)]; + for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)]; iptr++) { if (*iptr != '-') @@ -203,126 +187,163 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) { grub_dprintf ("luks", "%s != %s", uuid, search_uuid); - return 0; + return NULL; } - ciph = grub_crypto_lookup_cipher_by_name (header.cipherName); + /* Make sure that strings are null terminated. */ + grub_memcpy (ciphername, header->cipherName, sizeof (header->cipherName)); + ciphername[sizeof (header->cipherName)] = 0; + grub_memcpy (ciphermode, header->cipherMode, sizeof (header->cipherMode)); + ciphermode[sizeof (header->cipherMode)] = 0; + grub_memcpy (hashspec, header->hashSpec, sizeof (header->hashSpec)); + hashspec[sizeof (header->hashSpec)] = 0; + + ciph = grub_crypto_lookup_cipher_by_name (ciphername); if (!ciph) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", - header.cipherName); + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + ciphername); + return NULL; + } /* Configure the cipher used for the bulk data. */ cipher = grub_crypto_cipher_open (ciph); if (!cipher) - return grub_errno; + return NULL; - keysize = grub_be_to_cpu32 (header.keyBytes); - if (keysize > 1024) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", keysize); + if (grub_be_to_cpu32 (header->keyBytes) > 1024) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", + grub_be_to_cpu32 (header->keyBytes)); + return NULL; + } /* Configure the cipher mode. */ - if (grub_strncmp (header.cipherMode, "ecb", 3) == 0) + if (grub_strncmp (ciphermode, "ecb", 3) == 0) mode = GRUB_LUKS_MODE_ECB; - else if (grub_strncmp (header.cipherMode, "cbc-plain", 9) == 0 - || grub_strncmp (header.cipherMode, "plain", 5) == 0) + else if (grub_strncmp (ciphermode, "cbc-plain", 9) == 0 + || grub_strncmp (ciphermode, "plain", 5) == 0) mode = GRUB_LUKS_MODE_CBC_PLAIN; - else if (grub_strncmp (header.cipherMode, "cbc-essiv", 9) == 0) + else if (grub_strncmp (ciphermode, "cbc-essiv", 9) == 0) { mode = GRUB_LUKS_MODE_CBC_ESSIV; - char *hash_str = header.cipherMode + 10; + char *hash_str = ciphermode + 10; /* Configure the hash and cipher used for ESSIV. */ essiv_hash = grub_crypto_lookup_md_by_name (hash_str); if (!essiv_hash) { grub_crypto_cipher_close (cipher); - return grub_error (GRUB_ERR_FILE_NOT_FOUND, - "Couldn't load %s hash", hash_str); + grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Couldn't load %s hash", hash_str); + return NULL; } essiv_cipher = grub_crypto_cipher_open (ciph); if (!cipher) { grub_crypto_cipher_close (cipher); - return grub_errno; - } - - essiv_keysize = essiv_hash->mdlen; - hashed_key = grub_malloc (essiv_hash->mdlen); - if (!hashed_key) - { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); - return grub_errno; + return NULL; } } else { grub_crypto_cipher_close (cipher); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", - header.cipherMode); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", + ciphermode); + return NULL; } /* Configure the hash used for the AF splitter and HMAC. */ - hash = grub_crypto_lookup_md_by_name (header.hashSpec); + hash = grub_crypto_lookup_md_by_name (hashspec); if (!hash) { grub_crypto_cipher_close (cipher); grub_crypto_cipher_close (essiv_cipher); - grub_free (hashed_key); - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", - header.hashSpec); + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + hashspec); + return NULL; + } + + newdev = grub_zalloc (sizeof (struct grub_luks)); + if (!newdev) + return NULL; + newdev->cipher = cipher; + newdev->offset = grub_be_to_cpu32 (header->payloadOffset); + newdev->source_disk = NULL; + newdev->mode = mode; + newdev->essiv_cipher = essiv_cipher; + newdev->essiv_hash = essiv_hash; + newdev->hash = hash; + newdev->id = n++; + grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); + return newdev; +} + +static grub_err_t +luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, + const char *name, grub_disk_t source) +{ + grub_size_t keysize = grub_be_to_cpu32 (header->keyBytes); + grub_uint8_t candidate_key[keysize]; + grub_uint8_t digest[keysize]; + grub_uint8_t *hashed_key = NULL; + grub_uint8_t *split_key = NULL; + char passphrase[MAX_PASSPHRASE] = ""; + grub_uint8_t candidate_digest[sizeof (header->mkDigest)]; + unsigned i; + grub_size_t essiv_keysize = 0; + grub_size_t length; + grub_err_t err; + + if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) + essiv_keysize = dev->essiv_hash->mdlen; + hashed_key = grub_malloc (dev->essiv_hash->mdlen); + if (!hashed_key) + { + return grub_errno; } grub_printf ("Attempting to decrypt master key...\n"); - grub_uint8_t candidate_key[keysize]; - grub_uint8_t digest[keysize]; - split_key = grub_malloc (keysize * LUKS_STRIPES); if (!split_key) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); return grub_errno; } /* Get the passphrase from the user. */ - grub_printf ("Enter passphrase for %s (%s): ", name, uuid); + grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid); if (!grub_password_get (passphrase, MAX_PASSPHRASE)) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); } /* Try to recover master key from each active keyslot. */ - for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) { gcry_err_code_t gcry_err; /* Check if keyslot is enabled. */ - if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) + if (grub_be_to_cpu32 (header->keyblock[i].active) != LUKS_KEY_ENABLED) continue; grub_dprintf ("luks", "Trying keyslot %d\n", i); /* Calculate the PBKDF2 of the user supplied passphrase. */ - gcry_err = grub_crypto_pbkdf2 (hash, (grub_uint8_t *) passphrase, + gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, grub_strlen (passphrase), - header.keyblock[i].passwordSalt, - sizeof (header. + header->keyblock[i].passwordSalt, + sizeof (header-> keyblock[i].passwordSalt), - grub_be_to_cpu32 (header.keyblock[i]. + grub_be_to_cpu32 (header->keyblock[i]. passwordIterations), digest, keysize); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); @@ -331,60 +352,52 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) grub_dprintf ("luks", "PBKDF2 done\n"); /* Set the PBKDF2 output as the cipher key. */ - gcry_err = grub_crypto_cipher_set_key (cipher, digest, keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, keysize); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } /* Configure ESSIV if necessary. */ - if (mode == GRUB_LUKS_MODE_CBC_ESSIV) + if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) { - grub_crypto_hash (essiv_hash, hashed_key, digest, keysize); - grub_crypto_cipher_set_key (essiv_cipher, hashed_key, + grub_crypto_hash (dev->essiv_hash, hashed_key, digest, keysize); + grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, essiv_keysize); } length = - grub_be_to_cpu32 (header.keyBytes) * - grub_be_to_cpu32 (header.keyblock[i].stripes); + grub_be_to_cpu32 (header->keyBytes) * + grub_be_to_cpu32 (header->keyblock[i].stripes); /* Read and decrypt the key material from the disk. */ err = grub_disk_read (source, - grub_be_to_cpu32 (header.keyblock + grub_be_to_cpu32 (header->keyblock [i].keyMaterialOffset), 0, length, split_key); if (err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return err; } - gcry_err = luks_decrypt (cipher, mode, split_key, - length, 0, essiv_cipher); + gcry_err = luks_decrypt (dev->cipher, dev->mode, split_key, + length, 0, dev->essiv_cipher); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } /* Merge the decrypted key material to get the candidate master key. */ - gcry_err = AF_merge (hash, split_key, candidate_key, keysize, - grub_be_to_cpu32 (header.keyblock[i].stripes)); + gcry_err = AF_merge (dev->hash, split_key, candidate_key, keysize, + grub_be_to_cpu32 (header->keyblock[i].stripes)); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); @@ -393,18 +406,16 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) grub_dprintf ("luks", "candidate key recovered\n"); /* Calculate the PBKDF2 of the candidate master key. */ - gcry_err = grub_crypto_pbkdf2 (hash, candidate_key, - grub_be_to_cpu32 (header.keyBytes), - header.mkDigestSalt, - sizeof (header.mkDigestSalt), + gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key, + grub_be_to_cpu32 (header->keyBytes), + header->mkDigestSalt, + sizeof (header->mkDigestSalt), grub_be_to_cpu32 - (header.mkDigestIterations), + (header->mkDigestIterations), candidate_digest, sizeof (candidate_digest)); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); @@ -412,71 +423,90 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) /* Compare the calculated PBKDF2 to the digest stored in the header to see if it's correct. */ - if (grub_memcmp (candidate_digest, header.mkDigest, - sizeof (header.mkDigest)) != 0) + if (grub_memcmp (candidate_digest, header->mkDigest, + sizeof (header->mkDigest)) != 0) continue; grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = grub_crypto_cipher_set_key (cipher, candidate_key, keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, + candidate_key, keysize); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } /* Configure ESSIV if necessary. */ - if (mode == GRUB_LUKS_MODE_CBC_ESSIV) + if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) { - grub_crypto_hash (essiv_hash, hashed_key, candidate_key, keysize); + grub_crypto_hash (dev->essiv_hash, hashed_key, + candidate_key, keysize); gcry_err = - grub_crypto_cipher_set_key (essiv_cipher, hashed_key, + grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, essiv_keysize); if (gcry_err) { - grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } } - { - grub_luks_t newdev; - newdev = grub_zalloc (sizeof (struct grub_luks)); - if (!newdev) - return grub_errno; - newdev->id = n; - newdev->source = grub_strdup (name); - newdev->source_id = source->id; - newdev->source_dev_id = source->dev->id; - newdev->cipher = cipher; - newdev->offset = grub_be_to_cpu32 (header.payloadOffset); - newdev->source_disk = NULL; - newdev->mode = mode; - newdev->essiv_cipher = essiv_cipher; - grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); - newdev->next = luks_list; - luks_list = newdev; - n++; - } grub_free (split_key); grub_free (hashed_key); - have_it = 1; - return GRUB_ERR_NONE; } return GRUB_ACCESS_DENIED; } +static void +luks_close (grub_luks_t luks) +{ + grub_crypto_cipher_close (luks->cipher); + grub_crypto_cipher_close (luks->essiv_cipher); + grub_free (luks); +} + +static grub_err_t +grub_luks_scan_device_real (const char *name, grub_disk_t source) +{ + grub_err_t err; + struct grub_luks_phdr header; + grub_luks_t newdev; + + /* Read the LUKS header. */ + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + return err; + + newdev = configure_ciphers (&header); + if (!newdev) + return grub_errno; + + err = luks_recover_key (newdev, &header, name, source); + if (err) + { + luks_close (newdev); + return err; + } + + newdev->source = grub_strdup (name); + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->next = luks_list; + luks_list = newdev; + + have_it = 1; + + return GRUB_ERR_NONE; +} + static int grub_luks_scan_device (const char *name) { From dcd73ec05ef7a98f403860fcf7c9d80ef3c6440b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 19:04:21 +0200 Subject: [PATCH 08/43] add gcry to utils --- .bzrignore | 1 + Makefile.util.def | 18 ++++++++++-------- autogen.sh | 2 +- include/grub/crypto.h | 6 ++++++ util/grub-probe.c | 2 ++ util/import_gcry.py | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/.bzrignore b/.bzrignore index 55cbdaeeb..204fc182f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -135,3 +135,4 @@ widthspec.bin widthspec.h docs/stamp-1 docs/version-dev.texi +Makefile.utilgcry.def diff --git a/Makefile.util.def b/Makefile.util.def index 40c67c233..693a7d127 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -21,12 +21,16 @@ library = { common = grub-core/kern/list.c; common = grub-core/kern/misc.c; common = grub-core/kern/partition.c; + common = grub-core/lib/crypto.c; + common = grub-core/disk/luks.c; + common = grub-core/disk/AFSplitter.c; + common = grub-core/lib/pbkdf2.c; + common = grub-core/commands/extcmd.c; + common = grub-core/lib/arg.c; }; library = { name = libgrubmods.a; - cflags = '$(CFLAGS_GCRY)'; - cppflags = '$(CPPFLAGS_GCRY)'; common_nodist = grub_script.tab.c; common_nodist = grub_script.yy.c; @@ -35,7 +39,6 @@ library = { common_nodist = grub_script.tab.h; common = grub-core/commands/blocklist.c; - common = grub-core/commands/extcmd.c; common = grub-core/commands/ls.c; common = grub-core/disk/dmraid_nvidia.c; common = grub-core/disk/loopback.c; @@ -75,15 +78,10 @@ library = { common = grub-core/fs/zfs/zfs_lzjb.c; common = grub-core/fs/zfs/zfs_sha256.c; common = grub-core/fs/zfs/zfs_fletcher.c; - common = grub-core/lib/arg.c; - common = grub-core/lib/crypto.c; common = grub-core/lib/envblk.c; common = grub-core/lib/hexdump.c; - common = grub-core/lib/libgcrypt-grub/cipher/sha512.c; - common = grub-core/lib/libgcrypt-grub/cipher/crc.c; common = grub-core/lib/LzFind.c; common = grub-core/lib/LzmaEnc.c; - common = grub-core/lib/pbkdf2.c; common = grub-core/lib/crc.c; common = grub-core/normal/datetime.c; common = grub-core/normal/misc.c; @@ -123,6 +121,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBLZMA)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -173,6 +172,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; cflags = '$(CFLAGS_GCRY)'; @@ -211,6 +211,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -257,6 +258,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; diff --git a/autogen.sh b/autogen.sh index d14707aad..db719cd1d 100755 --- a/autogen.sh +++ b/autogen.sh @@ -26,7 +26,7 @@ if [ "x${GRUB_CONTRIB}" != x ]; then [ "${GRUB_CONTRIB}" = grub-core/contrib ] || ln -s ../contrib grub-core/contrib fi -UTIL_DEFS=Makefile.util.def +UTIL_DEFS='Makefile.util.def Makefile.utilgcry.def' CORE_DEFS='grub-core/Makefile.core.def grub-core/Makefile.gcry.def' for extra in contrib/*/Makefile.util.def; do diff --git a/include/grub/crypto.h b/include/grub/crypto.h index ebe78e7a1..a8ca2106d 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -274,4 +274,10 @@ grub_password_get (char buf[], unsigned buf_size); extern void (*grub_crypto_autoload_hook) (const char *name); +#ifdef GRUB_UTIL +void grub_gcry_init_all (void); +void grub_gcry_fini_all (void); +#endif + + #endif diff --git a/util/grub-probe.c b/util/grub-probe.c index 0d5dac902..6c6220b8b 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -393,6 +393,7 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); + grub_gcry_init_all (); grub_lvm_fini (); grub_mdraid09_fini (); @@ -410,6 +411,7 @@ main (int argc, char *argv[]) probe (argument, NULL); /* Free resources. */ + grub_gcry_fini_all (); grub_fini_all (); grub_util_biosdisk_fini (); diff --git a/util/import_gcry.py b/util/import_gcry.py index b2a0a5451..3a110f028 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -42,7 +42,15 @@ except: cipher_files = os.listdir (cipher_dir_in) conf = open (os.path.join ("grub-core", "Makefile.gcry.def"), "w") conf.write ("AutoGen definitions Makefile.tpl;\n\n") +confutil = open ("Makefile.utilgcry.def", "w") +confutil.write ("AutoGen definitions Makefile.tpl;\n\n") +confutil.write ("library = {\n"); +confutil.write (" name = libgrubgcry.a;\n"); +confutil.write (" cflags = '$(CFLAGS_GCRY)';\n"); +confutil.write (" cppflags = '$(CPPFLAGS_GCRY)';\n"); +confutil.write ("\n"); chlog = "" +modules = [] # Strictly speaking CRC32/CRC24 work on bytes so this value should be 1 # But libgcrypt uses 64. Let's keep the value for compatibility. Since @@ -249,6 +257,7 @@ for cipher_file in cipher_files: % (cipher_file, cipher_file.replace ("-glue.c", ".c")) else: modfiles = "lib/libgcrypt-grub/cipher/%s" % cipher_file + modules.append (modname) chmsg = "(GRUB_MOD_INIT(%s)): New function\n" % modname if nch: chlognew = "%s\n %s" % (chlognew, chmsg) @@ -283,6 +292,7 @@ for cipher_file in cipher_files: conf.write (" name = %s;\n" % modname) for src in modfiles.split(): conf.write (" common = %s;\n" % src) + confutil.write (" common = grub-core/%s;\n" % src) conf.write (" cflags = '$(CFLAGS_GCRY)';\n"); conf.write (" cppflags = '$(CPPFLAGS_GCRY)';\n"); conf.write ("};\n\n") @@ -329,6 +339,32 @@ fw.close () infile = os.path.join (cipher_dir_in, "ChangeLog") outfile = os.path.join (cipher_dir_out, "ChangeLog") +conf.close (); + +initfile = open (os.path.join (cipher_dir_out, "init.c"), "w") +for module in modules: + initfile.write ("extern void grub_%s_init (void);\n" % module) + initfile.write ("extern void grub_%s_fini (void);\n" % module) +initfile.write ("\n") +initfile.write ("void\n") +initfile.write ("grub_gcry_init_all (void)\n") +initfile.write ("{\n") +for module in modules: + initfile.write (" grub_%s_init ();\n" % module) +initfile.write ("}\n") +initfile.write ("\n") +initfile.write ("void\n") +initfile.write ("grub_gcry_fini_all (void)\n") +initfile.write ("{\n") +for module in modules: + initfile.write (" grub_%s_fini ();\n" % module) +initfile.write ("}\n") +initfile.close () + +confutil.write (" common = grub-core/lib/libgcrypt-grub/cipher/init.c;\n") +confutil.write ("};\n"); +confutil.close (); + f=open (infile, "r") fw=open (outfile, "w") From 24089d19e2dde4e72d8c783a3f1a607f5a8e7729 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 19:20:46 +0200 Subject: [PATCH 09/43] Add cheatmounting --- grub-core/disk/luks.c | 99 +++++++++++++++++++++++++++++++++-- grub-core/kern/emu/getroot.c | 26 +++++++-- grub-core/kern/emu/hostdisk.c | 75 ++++++++++++++------------ include/grub/emu/hostdisk.h | 6 +++ 4 files changed, 166 insertions(+), 40 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 4d907b4c6..99dd3a8e6 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -25,6 +25,15 @@ #include #include #include +#ifdef GRUB_UTIL +#include +#include +#include +#include +#include +#include +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -80,6 +89,10 @@ struct grub_luks unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; +#ifdef GRUB_UTIL + char *cheat; + int cheat_fd; +#endif struct grub_luks *next; }; typedef struct grub_luks *grub_luks_t; @@ -497,6 +510,12 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) } newdev->source = grub_strdup (name); + if (!newdev->source) + { + grub_free (newdev); + return grub_errno; + } + newdev->source_id = source->id; newdev->source_dev_id = source->dev->id; newdev->next = luks_list; @@ -507,6 +526,48 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) return GRUB_ERR_NONE; } +#ifdef GRUB_UTIL +grub_err_t +grub_luks_cheat_mount (const char *sourcedev, const char *cheat) +{ + grub_err_t err; + struct grub_luks_phdr header; + grub_luks_t newdev; + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (sourcedev); + if (!source) + return grub_errno; + + /* Read the LUKS header. */ + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + return err; + + newdev = configure_ciphers (&header); + grub_disk_close (source); + if (!newdev) + return grub_errno; + + newdev->cheat = grub_strdup (cheat); + newdev->source = grub_strdup (sourcedev); + if (!newdev->source || !newdev->cheat) + { + grub_free (newdev->source); + grub_free (newdev->cheat); + grub_free (newdev); + return grub_errno; + } + newdev->cheat_fd = -1; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->next = luks_list; + luks_list = newdev; + return GRUB_ERR_NONE; +} +#endif + static int grub_luks_scan_device (const char *name) { @@ -575,6 +636,17 @@ grub_luks_open (const char *name, grub_disk_t disk, if (!dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); +#ifdef GRUB_UTIL + if (dev->cheat) + { + if (dev->cheat_fd == -1) + dev->cheat_fd = open (dev->cheat, O_RDONLY); + if (dev->cheat_fd == -1) + return grub_error (GRUB_ERR_IO, "couldn't open %s: %s", + dev->cheat, strerror (errno)); + } +#endif + if (!dev->source_disk) { grub_dprintf ("luks", "Opening device %s\n", name); @@ -599,11 +671,17 @@ grub_luks_close (grub_disk_t disk) dev->ref--; - if (dev->ref == 0) + if (dev->ref != 0) + return; +#ifdef GRUB_UTIL + if (dev->cheat) { - grub_disk_close (dev->source_disk); - dev->source_disk = NULL; + close (dev->cheat_fd); + dev->cheat_fd = -1; } +#endif + grub_disk_close (dev->source_disk); + dev->source_disk = NULL; } static grub_err_t @@ -612,6 +690,21 @@ grub_luks_read (grub_disk_t disk, grub_disk_addr_t sector, { grub_luks_t dev = (grub_luks_t) disk->data; grub_err_t err; + +#ifdef GRUB_UTIL + if (dev->cheat) + { + err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector); + if (err) + return err; + if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS) + != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) + return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", + dev->cheat); + return GRUB_ERR_NONE; + } +#endif + grub_dprintf ("luks", "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT32_T "\n", diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 6cc745833..c3a971689 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -865,7 +865,9 @@ out: void grub_util_pull_device (const char *os_dev) { - switch (grub_util_get_dev_abstraction (os_dev)) + int ab; + ab = grub_util_get_dev_abstraction (os_dev); + switch (ab) { case GRUB_DEV_ABSTRACTION_LVM: case GRUB_DEV_ABSTRACTION_LUKS: @@ -875,6 +877,7 @@ grub_util_pull_device (const char *os_dev) struct dm_tree_node *node; struct dm_tree_node *child; void *handle = NULL; + char *lastsubdev = NULL; if (!grub_util_open_dm (os_dev, &tree, &node)) return; @@ -887,9 +890,26 @@ grub_util_pull_device (const char *os_dev) continue; subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor)); if (subdev) - grub_util_pull_device (subdev); + { + lastsubdev = subdev; + grub_util_pull_device (subdev); + } } - dm_tree_free (tree); + if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev) + { + char *grdev = grub_util_get_grub_dev (lastsubdev); + dm_tree_free (tree); + if (grdev) + { + grub_err_t err; + err = grub_luks_cheat_mount (grdev, os_dev); + if (err) + grub_util_error ("Can't mount LUKS: %s", grub_errmsg); + } + grub_free (grdev); + } + else + dm_tree_free (tree); } #endif return; diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index 9b65d417e..110d3e291 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -631,6 +631,37 @@ linux_find_partition (char *dev, unsigned long sector) } #endif /* __linux__ */ +#if defined(__linux__) && (!defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) + /* Maybe libc doesn't have large file support. */ +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) +{ + loff_t offset, result; + static int _llseek (uint filedes, ulong hi, ulong lo, + loff_t *res, uint wh); + _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); + + offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; + if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) + { + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); + } + return GRUB_ERR_NONE; +} +#else +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector) +{ + off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; + + if (lseek (fd, offset, SEEK_SET) != offset) + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name); + return 0; +} +#endif + static int open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) { @@ -781,44 +812,19 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags) configure_device_driver (fd); #endif /* defined(__NetBSD__) */ -#if defined(__linux__) && (!defined(__GLIBC__) || \ - ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) - /* Maybe libc doesn't have large file support. */ - { - loff_t offset, result; - static int _llseek (uint filedes, ulong hi, ulong lo, - loff_t *res, uint wh); - _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, - loff_t *, res, uint, wh); - - offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS; - if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) - { - grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device); - close (fd); - return -1; - } - } -#else - { - off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS; - - if (lseek (fd, offset, SEEK_SET) != offset) - { - grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device); - close (fd); - return -1; - } - } -#endif + if (grub_util_fd_sector_seek (fd, map[disk->id].device, sector)) + { + close (fd); + return -1; + } return fd; } /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an error occurs, otherwise return LEN. */ -static ssize_t -nread (int fd, char *buf, size_t len) +ssize_t +grub_util_fd_read (int fd, char *buf, size_t len) { ssize_t size = len; @@ -901,7 +907,8 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, sectors that are read together with the MBR in one read. It should only remap the MBR, so we split the read in two parts. -jochen */ - if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE) + if (grub_util_fd_read (fd, buf, GRUB_DISK_SECTOR_SIZE) + != GRUB_DISK_SECTOR_SIZE) { grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device); return grub_errno; @@ -912,7 +919,7 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, } #endif /* __linux__ */ - if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS) + if (grub_util_fd_read (fd, buf, size << GRUB_DISK_SECTOR_BITS) != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device); diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index 803c0f755..c7a794d68 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -21,6 +21,7 @@ #define GRUB_BIOSDISK_MACHINE_UTIL_HEADER 1 #include +#include void grub_util_biosdisk_init (const char *dev_map); void grub_util_biosdisk_fini (void); @@ -30,5 +31,10 @@ int grub_util_biosdisk_is_present (const char *name); int grub_util_biosdisk_is_floppy (grub_disk_t disk); grub_err_t grub_util_biosdisk_flush (struct grub_disk *disk); void grub_util_pull_device (const char *osname); +grub_err_t +grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector); +ssize_t grub_util_fd_read (int fd, char *buf, size_t len); +grub_err_t +grub_luks_cheat_mount (const char *sourcedev, const char *cheat); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ From 4169260830d30fca23af54441bfd3ea749b7c006 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 19:44:41 +0200 Subject: [PATCH 10/43] grub-fstest luks support --- grub-core/disk/luks.c | 28 ++++++++++++++++++++++++---- util/grub-fstest.c | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 99dd3a8e6..eb9dd050e 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -107,6 +107,7 @@ gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, static const struct grub_arg_option options[] = { {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, + {"all", 'a', 0, N_("Mount all."), 0, 0}, {0, 0, 0, 0, 0, 0} }; @@ -491,7 +492,11 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) { grub_err_t err; struct grub_luks_phdr header; - grub_luks_t newdev; + grub_luks_t newdev, dev; + + for (dev = luks_list; dev != NULL; dev = dev->next) + if (dev->source_id == source->id && dev->source_dev_id == source->dev->id) + return GRUB_ERR_NONE; /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); @@ -532,7 +537,7 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) { grub_err_t err; struct grub_luks_phdr header; - grub_luks_t newdev; + grub_luks_t newdev, dev; grub_disk_t source; /* Try to open disk. */ @@ -540,6 +545,13 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) if (!source) return grub_errno; + for (dev = luks_list; dev != NULL; dev = dev->next) + if (dev->source_id == source->id && dev->source_dev_id == source->dev->id) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } + /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); if (err) @@ -755,7 +767,7 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) { struct grub_arg_list *state = ctxt->state; - if (argc < 1) + if (argc < 1 && !state[1].set) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); have_it = 0; @@ -779,6 +791,14 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); return GRUB_ERR_NONE; } + else if (state[1].set) + { + check_uuid = 0; + search_uuid = NULL; + grub_device_iterate (&grub_luks_scan_device); + search_uuid = NULL; + return GRUB_ERR_NONE; + } else { grub_err_t err; @@ -823,7 +843,7 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT (luks) { cmd = grub_register_extcmd ("luksmount", grub_cmd_luksmount, 0, - N_("SOURCE|-u UUID"), + N_("SOURCE|-u UUID|-a"), N_("Mount a LUKS device."), options); grub_disk_dev_register (&grub_luks_dev); } diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 293bdf74a..2adb2331d 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -275,6 +275,7 @@ static char **images = NULL; static int cmd = 0; static char *debug_str = NULL; static char **args = NULL; +static int mount_crypt = 0; static void fstest (int n, char **args) @@ -304,6 +305,13 @@ fstest (int n, char **args) grub_free (host_file); } + { + char *argv[2] = { "-a", NULL}; + if (mount_crypt) + if (execute_command ("luksmount", 1, argv)) + grub_util_error (_("luksmount command fails: %s"), grub_errmsg); + } + grub_lvm_fini (); grub_mdraid09_fini (); grub_mdraid1x_fini (); @@ -366,6 +374,7 @@ static struct argp_option options[] = { {"length", 'n', "N", 0, N_("Handle N bytes in output file."), 2}, {"diskcount", 'c', "N", 0, N_("N input files."), 2}, {"debug", 'd', "S", 0, N_("Set debug environment variable."), 2}, + {"crypto", 'C', NULL, OPTION_ARG_OPTIONAL, N_("Mount crypto devices."), 2}, {"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, N_("Print verbose messages."), 2}, {0, 0, 0, 0, 0, 0} }; @@ -389,6 +398,10 @@ argp_parser (int key, char *arg, struct argp_state *state) root = arg; return 0; + case 'C': + mount_crypt = 1; + return 0; + case 's': skip = grub_strtoul (arg, &p, 0); if (*p == 's') @@ -523,6 +536,7 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); + grub_gcry_init_all (); if (debug_str) grub_env_set ("debug", debug_str); @@ -551,6 +565,7 @@ main (int argc, char *argv[]) fstest (args_count - 1 - num_disks, args); /* Free resources. */ + grub_gcry_fini_all (); grub_fini_all (); return 0; From 84a411c0c3b17ba1a67ef30a94134bb8c8a9266a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 19:51:06 +0200 Subject: [PATCH 11/43] small argument revamp --- grub-core/disk/luks.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index eb9dd050e..8bf07903c 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -112,27 +112,26 @@ static const struct grub_arg_option options[] = }; static gcry_err_code_t -luks_decrypt (grub_crypto_cipher_handle_t cipher, luks_mode_t mode, - grub_uint8_t * data, grub_size_t len, - grub_size_t sector, grub_crypto_cipher_handle_t essiv_cipher) +luks_decrypt (const struct grub_luks *dev, + grub_uint8_t * data, grub_size_t len, grub_size_t sector) { grub_size_t i; gcry_err_code_t err; - switch (mode) + switch (dev->mode) { case GRUB_LUKS_MODE_ECB: - return grub_crypto_ecb_decrypt (cipher, data, data, len); + return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); case GRUB_LUKS_MODE_CBC_PLAIN: for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) { - grub_uint32_t iv[(cipher->cipher->blocksize + grub_uint32_t iv[(dev->cipher->cipher->blocksize + sizeof (grub_uint32_t) - 1) / sizeof (grub_uint32_t)]; - grub_memset (iv, 0, cipher->cipher->blocksize); + grub_memset (iv, 0, dev->cipher->cipher->blocksize); iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); - err = grub_crypto_cbc_decrypt (cipher, data + i, data + i, + err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, GRUB_DISK_SECTOR_SIZE, iv); if (err) return err; @@ -143,17 +142,17 @@ luks_decrypt (grub_crypto_cipher_handle_t cipher, luks_mode_t mode, case GRUB_LUKS_MODE_CBC_ESSIV: for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) { - grub_uint32_t iv[(cipher->cipher->blocksize + grub_uint32_t iv[(dev->cipher->cipher->blocksize + sizeof (grub_uint32_t) - 1) / sizeof (grub_uint32_t)]; - grub_memset (iv, 0, cipher->cipher->blocksize); + grub_memset (iv, 0, dev->cipher->cipher->blocksize); iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); err = - grub_crypto_ecb_encrypt (essiv_cipher, iv, iv, - cipher->cipher->blocksize); + grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, + dev->cipher->cipher->blocksize); if (err) return err; - err = grub_crypto_cbc_decrypt (cipher, data + i, data + i, + err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, GRUB_DISK_SECTOR_SIZE, iv); if (err) return err; @@ -398,8 +397,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, return err; } - gcry_err = luks_decrypt (dev->cipher, dev->mode, split_key, - length, 0, dev->essiv_cipher); + gcry_err = luks_decrypt (dev, split_key, length, 0); if (gcry_err) { grub_free (hashed_key); @@ -729,11 +727,9 @@ grub_luks_read (grub_disk_t disk, grub_disk_addr_t sector, grub_dprintf ("luks", "grub_disk_read failed with error %d\n", err); return err; } - return grub_crypto_gcry_error (luks_decrypt (dev->cipher, - dev->mode, - (grub_uint8_t *) buf, + return grub_crypto_gcry_error (luks_decrypt (dev, (grub_uint8_t *) buf, size << GRUB_DISK_SECTOR_BITS, - sector, dev->essiv_cipher)); + sector)); } static grub_err_t From 2cb55e6f7356d82f826e65577373167a46b73f8d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 21:46:00 +0200 Subject: [PATCH 12/43] make grub_password_get work in userland --- grub-core/lib/crypto.c | 44 ++++++++++++++++++++- util/grub-mkpasswd-pbkdf2.c | 76 ++++++------------------------------- 2 files changed, 53 insertions(+), 67 deletions(-) diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index ad1bfc4d3..2f172ebf8 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -23,6 +23,13 @@ #include #include +#ifdef GRUB_UTIL +#include +#include +#include +#include +#endif + GRUB_MOD_LICENSE ("GPLv3+"); struct grub_crypto_hmac_handle @@ -414,10 +421,43 @@ grub_crypto_memcmp (const void *a, const void *b, grub_size_t n) return !!counter; } -#ifndef GRUB_MKPASSWD int grub_password_get (char buf[], unsigned buf_size) { +#ifdef GRUB_UTIL + FILE *in; + struct termios s, t; + int tty_changed = 0; + char *ptr; + + /* Disable echoing. Based on glibc. */ + in = fopen ("/dev/tty", "w+c"); + if (in == NULL) + in = stdin; + + if (tcgetattr (fileno (in), &t) == 0) + { + /* Save the old one. */ + s = t; + /* Tricky, tricky. */ + t.c_lflag &= ~(ECHO|ISIG); + tty_changed = (tcsetattr (fileno (in), TCSAFLUSH, &t) == 0); + } + else + tty_changed = 0; + fgets (buf, buf_size, stdin); + ptr = buf + strlen (buf) - 1; + while (buf <= ptr && (*ptr == '\n' || *ptr == '\r')) + *ptr-- = 0; + /* Restore the original setting. */ + if (tty_changed) + (void) tcsetattr (fileno (in), TCSAFLUSH, &s); + + grub_xputs ("\n"); + grub_refresh (); + + return 1; +#else unsigned cur_len = 0; int key; @@ -452,5 +492,5 @@ grub_password_get (char buf[], unsigned buf_size) grub_refresh (); return (key != '\e'); -} #endif +} diff --git a/util/grub-mkpasswd-pbkdf2.c b/util/grub-mkpasswd-pbkdf2.c index fe1887f8f..032a8b586 100644 --- a/util/grub-mkpasswd-pbkdf2.c +++ b/util/grub-mkpasswd-pbkdf2.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include "progname.h" @@ -85,14 +85,12 @@ int main (int argc, char *argv[]) { unsigned int count = 10000, buflen = 64, saltlen = 64; - char *pass1, *pass2; char *bufhex, *salthex; gcry_err_code_t gcry_err; grub_uint8_t *buf, *salt; ssize_t nr; - FILE *in, *out; - struct termios s, t; - int tty_changed; + char pass1[GRUB_AUTH_MAX_PASSLEN]; + char pass2[GRUB_AUTH_MAX_PASSLEN]; set_program_name (argv[0]); @@ -160,86 +158,37 @@ main (int argc, char *argv[]) free (buf); grub_util_error ("out of memory"); } - - /* Disable echoing. Based on glibc. */ - in = fopen ("/dev/tty", "w+c"); - if (in == NULL) - { - in = stdin; - out = stderr; - } - else - out = in; - - if (tcgetattr (fileno (in), &t) == 0) - { - /* Save the old one. */ - s = t; - /* Tricky, tricky. */ - t.c_lflag &= ~(ECHO|ISIG); - tty_changed = (tcsetattr (fileno (in), TCSAFLUSH, &t) == 0); - } - else - tty_changed = 0; printf ("Enter password: "); - pass1 = NULL; - { - grub_size_t n; - nr = getline (&pass1, &n, stdin); - } - if (nr < 0 || !pass1) + if (!grub_password_get (pass1, GRUB_AUTH_MAX_PASSLEN)) { free (buf); free (bufhex); free (salthex); free (salt); - /* Restore the original setting. */ - if (tty_changed) - (void) tcsetattr (fileno (in), TCSAFLUSH, &s); grub_util_error ("failure to read password"); } - if (nr >= 1 && pass1[nr-1] == '\n') - pass1[nr-1] = 0; - printf ("\nReenter password: "); - pass2 = NULL; - { - grub_size_t n; - nr = getline (&pass2, &n, stdin); - } - /* Restore the original setting. */ - if (tty_changed) - (void) tcsetattr (fileno (in), TCSAFLUSH, &s); - printf ("\n"); - - if (nr < 0 || !pass2) + if (!grub_password_get (pass2, GRUB_AUTH_MAX_PASSLEN)) { - memset (pass1, 0, strlen (pass1)); - free (pass1); free (buf); free (bufhex); free (salthex); free (salt); grub_util_error ("failure to read password"); } - if (nr >= 1 && pass2[nr-1] == '\n') - pass2[nr-1] = 0; if (strcmp (pass1, pass2) != 0) { - memset (pass1, 0, strlen (pass1)); - memset (pass2, 0, strlen (pass2)); - free (pass1); - free (pass2); + memset (pass1, 0, sizeof (pass1)); + memset (pass2, 0, sizeof (pass2)); free (buf); free (bufhex); free (salthex); free (salt); grub_util_error ("passwords don't match"); } - memset (pass2, 0, strlen (pass2)); - free (pass2); + memset (pass2, 0, sizeof (pass2)); #if ! defined (__linux__) && ! defined (__FreeBSD__) printf ("WARNING: your random generator isn't known to be secure\n"); @@ -251,8 +200,7 @@ main (int argc, char *argv[]) f = fopen ("/dev/random", "rb"); if (!f) { - memset (pass1, 0, strlen (pass1)); - free (pass1); + memset (pass1, 0, sizeof (pass1)); free (buf); free (bufhex); free (salthex); @@ -264,8 +212,7 @@ main (int argc, char *argv[]) if (rd != saltlen) { fclose (f); - memset (pass1, 0, strlen (pass1)); - free (pass1); + memset (pass1, 0, sizeof (pass1)); free (buf); free (bufhex); free (salthex); @@ -280,8 +227,7 @@ main (int argc, char *argv[]) (grub_uint8_t *) pass1, strlen (pass1), salt, saltlen, count, buf, buflen); - memset (pass1, 0, strlen (pass1)); - free (pass1); + memset (pass1, 0, sizeof (pass1)); if (gcry_err) { From fe32915a5ebc39f21524547c6a7ec6d123534828 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 21:48:32 +0200 Subject: [PATCH 13/43] XTS support --- grub-core/disk/luks.c | 192 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 35 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 8bf07903c..0098c9d49 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -73,7 +73,8 @@ typedef enum { GRUB_LUKS_MODE_ECB, GRUB_LUKS_MODE_CBC_PLAIN, - GRUB_LUKS_MODE_CBC_ESSIV + GRUB_LUKS_MODE_CBC_ESSIV, + GRUB_LUKS_MODE_XTS } luks_mode_t; struct grub_luks @@ -83,7 +84,7 @@ struct grub_luks grub_disk_t source_disk; int ref; grub_crypto_cipher_handle_t cipher; - grub_crypto_cipher_handle_t essiv_cipher; + grub_crypto_cipher_handle_t secondary_cipher; const gcry_md_spec_t *essiv_hash, *hash; luks_mode_t mode; unsigned long id, source_id; @@ -111,6 +112,33 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; +static inline void +make_iv (grub_uint32_t *iv, grub_size_t sz, grub_disk_addr_t sector) +{ + grub_memset (iv, 0, sz * sizeof (iv[0])); + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); +} + +/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ +#define POLYNOM 0x87 + +static void +gf_mul_x (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = 0; j < 16; j++) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[0] ^= POLYNOM; +} + static gcry_err_code_t luks_decrypt (const struct grub_luks *dev, grub_uint8_t * data, grub_size_t len, grub_size_t sector) @@ -126,11 +154,11 @@ luks_decrypt (const struct grub_luks *dev, case GRUB_LUKS_MODE_CBC_PLAIN: for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) { - grub_uint32_t iv[(dev->cipher->cipher->blocksize + grub_size_t sz = ((dev->cipher->cipher->blocksize + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)]; - grub_memset (iv, 0, dev->cipher->cipher->blocksize); - iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + / sizeof (grub_uint32_t)); + grub_uint32_t iv[sz]; + make_iv (iv, sz, sector); err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, GRUB_DISK_SECTOR_SIZE, iv); if (err) @@ -142,14 +170,14 @@ luks_decrypt (const struct grub_luks *dev, case GRUB_LUKS_MODE_CBC_ESSIV: for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) { - grub_uint32_t iv[(dev->cipher->cipher->blocksize + grub_size_t sz = ((dev->cipher->cipher->blocksize + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)]; - grub_memset (iv, 0, dev->cipher->cipher->blocksize); - iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); - err = - grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, - dev->cipher->cipher->blocksize); + / sizeof (grub_uint32_t)); + grub_uint32_t iv[sz]; + make_iv (iv, sz, sector); + + err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, + dev->cipher->cipher->blocksize); if (err) return err; err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, @@ -160,6 +188,39 @@ luks_decrypt (const struct grub_luks *dev, } return GPG_ERR_NO_ERROR; + case GRUB_LUKS_MODE_XTS: + for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + { + grub_size_t sz = ((dev->cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)); + grub_uint32_t iv[sz]; + int j; + make_iv (iv, sz, sector); + + err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + + for (j = 0; j < GRUB_DISK_SECTOR_SIZE; + j += dev->cipher->cipher->blocksize) + { + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + gf_mul_x ((grub_uint8_t *) iv); + } + sector++; + } + return GPG_ERR_NO_ERROR; + default: return GPG_ERR_NOT_IMPLEMENTED; } @@ -178,7 +239,7 @@ configure_ciphers (const struct grub_luks_phdr *header) char ciphername[sizeof (header->cipherName) + 1]; char ciphermode[sizeof (header->cipherMode) + 1]; char hashspec[sizeof (header->hashSpec) + 1]; - grub_crypto_cipher_handle_t cipher = NULL, essiv_cipher = NULL; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; const struct gcry_cipher_spec *ciph; luks_mode_t mode; @@ -232,12 +293,13 @@ configure_ciphers (const struct grub_luks_phdr *header) } /* Configure the cipher mode. */ - if (grub_strncmp (ciphermode, "ecb", 3) == 0) + if (grub_strcmp (ciphermode, "ecb") == 0) mode = GRUB_LUKS_MODE_ECB; - else if (grub_strncmp (ciphermode, "cbc-plain", 9) == 0 - || grub_strncmp (ciphermode, "plain", 5) == 0) + else if (grub_strcmp (ciphermode, "cbc-plain") == 0 + || grub_strcmp (ciphermode, "plain") == 0) mode = GRUB_LUKS_MODE_CBC_PLAIN; - else if (grub_strncmp (ciphermode, "cbc-essiv", 9) == 0) + else if (grub_memcmp (ciphermode, "cbc-essiv:", sizeof ("cbc-essiv:") - 1) + == 0) { mode = GRUB_LUKS_MODE_CBC_ESSIV; char *hash_str = ciphermode + 10; @@ -251,13 +313,35 @@ configure_ciphers (const struct grub_luks_phdr *header) "Couldn't load %s hash", hash_str); return NULL; } - essiv_cipher = grub_crypto_cipher_open (ciph); - if (!cipher) + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) { grub_crypto_cipher_close (cipher); return NULL; } } + else if (grub_strcmp (ciphermode, "xts-plain") == 0) + { + mode = GRUB_LUKS_MODE_XTS; + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) + { + grub_crypto_cipher_close (cipher); + return NULL; + } + if (cipher->cipher->blocksize != 16) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", + cipher->cipher->blocksize); + return NULL; + } + if (secondary_cipher->cipher->blocksize != 16) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", + secondary_cipher->cipher->blocksize); + return NULL; + } + } else { grub_crypto_cipher_close (cipher); @@ -271,7 +355,7 @@ configure_ciphers (const struct grub_luks_phdr *header) if (!hash) { grub_crypto_cipher_close (cipher); - grub_crypto_cipher_close (essiv_cipher); + grub_crypto_cipher_close (secondary_cipher); grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", hashspec); return NULL; @@ -284,7 +368,7 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->offset = grub_be_to_cpu32 (header->payloadOffset); newdev->source_disk = NULL; newdev->mode = mode; - newdev->essiv_cipher = essiv_cipher; + newdev->secondary_cipher = secondary_cipher; newdev->essiv_hash = essiv_hash; newdev->hash = hash; newdev->id = n++; @@ -309,11 +393,11 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_err_t err; if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) - essiv_keysize = dev->essiv_hash->mdlen; - hashed_key = grub_malloc (dev->essiv_hash->mdlen); - if (!hashed_key) { - return grub_errno; + essiv_keysize = dev->essiv_hash->mdlen; + hashed_key = grub_malloc (dev->essiv_hash->mdlen); + if (!hashed_key) + return grub_errno; } grub_printf ("Attempting to decrypt master key...\n"); @@ -365,7 +449,9 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_dprintf ("luks", "PBKDF2 done\n"); /* Set the PBKDF2 output as the cipher key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, + (dev->mode == GRUB_LUKS_MODE_XTS) + ? (keysize / 2) : keysize); if (gcry_err) { grub_free (hashed_key); @@ -377,8 +463,28 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) { grub_crypto_hash (dev->essiv_hash, hashed_key, digest, keysize); - grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, - essiv_keysize); + gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, + hashed_key, + essiv_keysize); + if (gcry_err) + { + grub_free (hashed_key); + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + } + + if (dev->mode == GRUB_LUKS_MODE_XTS) + { + gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, + digest + (keysize / 2), + keysize / 2); + if (gcry_err) + { + grub_free (hashed_key); + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } } length = @@ -437,13 +543,17 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, in the header to see if it's correct. */ if (grub_memcmp (candidate_digest, header->mkDigest, sizeof (header->mkDigest)) != 0) - continue; + { + grub_dprintf ("luks", "bad digest\n"); + continue; + } grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, - candidate_key, keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, candidate_key, + (dev->mode == GRUB_LUKS_MODE_XTS) + ? (keysize / 2) : keysize); if (gcry_err) { grub_free (hashed_key); @@ -457,7 +567,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_crypto_hash (dev->essiv_hash, hashed_key, candidate_key, keysize); gcry_err = - grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, + grub_crypto_cipher_set_key (dev->secondary_cipher, hashed_key, essiv_keysize); if (gcry_err) { @@ -467,6 +577,18 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } } + if (dev->mode == GRUB_LUKS_MODE_XTS) + { + gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, + candidate_key + (keysize / 2), + keysize / 2); + if (gcry_err) + { + grub_free (hashed_key); + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + } grub_free (split_key); grub_free (hashed_key); @@ -481,7 +603,7 @@ static void luks_close (grub_luks_t luks) { grub_crypto_cipher_close (luks->cipher); - grub_crypto_cipher_close (luks->essiv_cipher); + grub_crypto_cipher_close (luks->secondary_cipher); grub_free (luks); } @@ -751,7 +873,7 @@ luks_cleanup (void) { grub_free (dev->source); grub_free (dev->cipher); - grub_free (dev->essiv_cipher); + grub_free (dev->secondary_cipher); tmp = dev->next; grub_free (dev); dev = tmp; From 50ad7d9cae1c31bbcd2ec776083f5ca59f01c06a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 23:39:36 +0200 Subject: [PATCH 14/43] luks grub-probe support --- grub-core/disk/luks.c | 35 ++++++++++ include/grub/crypto.h | 6 ++ include/grub/emu/hostdisk.h | 1 + util/grub-probe.c | 136 +++++++++++++++--------------------- util/import_gcry.py | 22 ++++-- 5 files changed, 116 insertions(+), 84 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 0098c9d49..ce1f1bfc6 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -863,6 +863,38 @@ grub_luks_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } +#ifdef GRUB_UTIL +static grub_disk_memberlist_t +grub_luks_memberlist (grub_disk_t disk) +{ + grub_luks_t dev = (grub_luks_t) disk->data; + grub_disk_memberlist_t list = NULL; + + list = grub_malloc (sizeof (*list)); + if (list) + { + list->disk = dev->source_disk; + list->next = NULL; + } + + return list; +} + +void +grub_util_luks_print_ciphers (grub_disk_t disk) +{ + grub_luks_t dev = (grub_luks_t) disk->data; + if (dev->cipher) + grub_printf ("%s ", dev->cipher->cipher->modname); + if (dev->secondary_cipher) + grub_printf ("%s ", dev->secondary_cipher->cipher->modname); + if (dev->hash) + grub_printf ("%s ", dev->hash->modname); + if (dev->essiv_hash) + grub_printf ("%s ", dev->essiv_hash->modname); +} +#endif + static void luks_cleanup (void) { @@ -953,6 +985,9 @@ static struct grub_disk_dev grub_luks_dev = { .close = grub_luks_close, .read = grub_luks_read, .write = grub_luks_write, +#ifdef GRUB_UTIL + .memberlist = grub_luks_memberlist, +#endif .next = 0 }; diff --git a/include/grub/crypto.h b/include/grub/crypto.h index a8ca2106d..62ed2015c 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -126,6 +126,9 @@ typedef struct gcry_cipher_spec gcry_cipher_decrypt_t decrypt; gcry_cipher_stencrypt_t stencrypt; gcry_cipher_stdecrypt_t stdecrypt; +#ifdef GRUB_UTIL + const char *modname; +#endif struct gcry_cipher_spec *next; } gcry_cipher_spec_t; @@ -161,6 +164,9 @@ typedef struct gcry_md_spec grub_size_t contextsize; /* allocate this amount of context */ /* Block size, needed for HMAC. */ grub_size_t blocksize; +#ifdef GRUB_UTIL + const char *modname; +#endif struct gcry_md_spec *next; } gcry_md_spec_t; diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index c7a794d68..2be24cc3f 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -36,5 +36,6 @@ grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector); ssize_t grub_util_fd_read (int fd, char *buf, size_t len); grub_err_t grub_luks_cheat_mount (const char *sourcedev, const char *cheat); +void grub_util_luks_print_ciphers (grub_disk_t disk); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/util/grub-probe.c b/util/grub-probe.c index 6c6220b8b..68d9b06f1 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -63,15 +64,28 @@ static void probe_partmap (grub_disk_t disk) { grub_partition_t part; + grub_disk_memberlist_t list = NULL, tmp; if (disk->partition == NULL) { grub_util_info ("no partition map found for %s", disk->name); - return; } for (part = disk->partition; part; part = part->parent) - printf ("%s\n", part->partmap->name); + printf ("%s ", part->partmap->name); + + /* In case of LVM/RAID, check the member devices as well. */ + if (disk->dev->memberlist) + { + list = disk->dev->memberlist (disk); + } + while (list) + { + probe_partmap (list->disk); + tmp = list->next; + free (list); + list = tmp; + } } static int @@ -88,6 +102,45 @@ probe_raid_level (grub_disk_t disk) return ((struct grub_raid_array *) disk->data)->level; } +static void +probe_abstraction (grub_disk_t disk) +{ + grub_disk_memberlist_t list = NULL, tmp; + int raid_level; + + if (disk->dev->memberlist) + list = disk->dev->memberlist (disk); + while (list) + { + probe_abstraction (list->disk); + + tmp = list->next; + free (list); + list = tmp; + } + + if (disk->dev->id == GRUB_DISK_DEVICE_LVM_ID) + printf ("lvm "); + + if (disk->dev->id == GRUB_DISK_DEVICE_LUKS_ID) + { + printf ("luks "); + grub_util_luks_print_ciphers (disk); + } + + raid_level = probe_raid_level (disk); + if (raid_level >= 0) + { + printf ("raid "); + if (disk->dev->raidname) + printf ("%s ", disk->dev->raidname (disk)); + } + if (raid_level == 5) + printf ("raid5rec "); + if (raid_level == 6) + printf ("raid6rec "); +} + static void probe (const char *path, char *device_name) { @@ -136,91 +189,16 @@ probe (const char *path, char *device_name) if (print == PRINT_ABSTRACTION) { - grub_disk_memberlist_t list = NULL, tmp; - const int is_lvm = (dev->disk->dev->id == GRUB_DISK_DEVICE_LVM_ID); - int is_raid = 0; - int is_raid5 = 0; - int is_raid6 = 0; - int raid_level; - grub_disk_t raid_disk; - - raid_level = probe_raid_level (dev->disk); - if (raid_level >= 0) - { - is_raid = 1; - is_raid5 |= (raid_level == 5); - is_raid6 |= (raid_level == 6); - raid_disk = dev->disk; - } - - if ((is_lvm) && (dev->disk->dev->memberlist)) - list = dev->disk->dev->memberlist (dev->disk); - while (list) - { - raid_level = probe_raid_level (list->disk); - if (raid_level >= 0) - { - is_raid = 1; - is_raid5 |= (raid_level == 5); - is_raid6 |= (raid_level == 6); - raid_disk = list->disk; - } - - tmp = list->next; - free (list); - list = tmp; - } - - if (is_raid) - { - printf ("raid "); - if (is_raid5) - printf ("raid5rec "); - if (is_raid6) - printf ("raid6rec "); - if (raid_disk->dev->raidname) - printf ("%s ", raid_disk->dev->raidname (raid_disk)); - } - - if (is_lvm) - printf ("lvm "); - + probe_abstraction (dev->disk); printf ("\n"); - goto end; } if (print == PRINT_PARTMAP) { - grub_disk_memberlist_t list = NULL, tmp; - /* Check if dev->disk itself is contained in a partmap. */ probe_partmap (dev->disk); - - /* In case of LVM/RAID, check the member devices as well. */ - if (dev->disk->dev->memberlist) - list = dev->disk->dev->memberlist (dev->disk); - while (list) - { - probe_partmap (list->disk); - /* LVM on RAID */ - if (list->disk->dev->memberlist) - { - grub_disk_memberlist_t sub_list; - - sub_list = list->disk->dev->memberlist (list->disk); - while (sub_list) - { - probe_partmap (sub_list->disk); - tmp = sub_list->next; - free (sub_list); - sub_list = tmp; - } - } - tmp = list->next; - free (list); - list = tmp; - } + printf ("\n"); goto end; } diff --git a/util/import_gcry.py b/util/import_gcry.py index 3a110f028..0ebfd1f56 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -111,6 +111,7 @@ for cipher_file in cipher_files: skip = False skip2 = False ismd = False + iscipher = False iscryptostart = False iscomma = False isglue = False @@ -140,15 +141,22 @@ for cipher_file in cipher_files: sg = s.groups()[0] cryptolist.write (("%s: %s\n") % (sg, modname)) iscryptostart = False - if ismd: + if ismd or iscipher: if not re.search (" *};", line) is None: - if not mdblocksizes.has_key (mdname): - print ("ERROR: Unknown digest blocksize: %s\n" % mdname) - exit (1) if not iscomma: fw.write (" ,\n") - fw.write (" .blocksize = %s\n" % mdblocksizes [mdname]) + fw.write ("#ifdef GRUB_UTIL\n"); + fw.write (" .modname = \"%s\",\n" % modname); + fw.write ("#endif\n"); + if ismd: + if not mdblocksizes.has_key (mdname): + print ("ERROR: Unknown digest blocksize: %s\n" + % mdname) + exit (1) + fw.write (" .blocksize = %s\n" + % mdblocksizes [mdname]) ismd = False + iscipher = False iscomma = not re.search (",$", line) is None # Used only for selftests. m = re.match ("(static byte|static unsigned char) (weak_keys_chksum)\[[0-9]*\] =", line) @@ -189,14 +197,18 @@ for cipher_file in cipher_files: continue m = re.match ("gcry_cipher_spec_t", line) if isc and not m is None: + assert (not iscryptostart) + assert (not iscipher) assert (not iscryptostart) ciphername = line [len ("gcry_cipher_spec_t"):].strip () ciphername = re.match("[a-zA-Z0-9_]*",ciphername).group () ciphernames.append (ciphername) + iscipher = True iscryptostart = True m = re.match ("gcry_md_spec_t", line) if isc and not m is None: assert (not ismd) + assert (not iscipher) assert (not iscryptostart) mdname = line [len ("gcry_md_spec_t"):].strip () mdname = re.match("[a-zA-Z0-9_]*",mdname).group () From 9d647e4e181b0f88b5107b3f00e4f7d3bb9a1686 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Fri, 22 Apr 2011 23:51:16 +0200 Subject: [PATCH 15/43] New -t luks_uuid --- grub-core/disk/luks.c | 7 +++++++ include/grub/emu/hostdisk.h | 1 + util/grub-probe.c | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index ce1f1bfc6..997e2eb30 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -893,6 +893,13 @@ grub_util_luks_print_ciphers (grub_disk_t disk) if (dev->essiv_hash) grub_printf ("%s ", dev->essiv_hash->modname); } + +void +grub_util_luks_print_uuid (grub_disk_t disk) +{ + grub_luks_t dev = (grub_luks_t) disk->data; + grub_printf ("%s ", dev->uuid); +} #endif static void diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index 2be24cc3f..7541308e6 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -37,5 +37,6 @@ ssize_t grub_util_fd_read (int fd, char *buf, size_t len); grub_err_t grub_luks_cheat_mount (const char *sourcedev, const char *cheat); void grub_util_luks_print_ciphers (grub_disk_t disk); +void grub_util_luks_print_uuid (grub_disk_t disk); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/util/grub-probe.c b/util/grub-probe.c index 68d9b06f1..21fc21b02 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -55,6 +55,7 @@ enum { PRINT_DEVICE, PRINT_PARTMAP, PRINT_ABSTRACTION, + PRINT_LUKS_UUID }; int print = PRINT_FS; @@ -88,6 +89,27 @@ probe_partmap (grub_disk_t disk) } } +static void +probe_luks_uuid (grub_disk_t disk) +{ + grub_disk_memberlist_t list = NULL, tmp; + + /* In case of LVM/RAID, check the member devices as well. */ + if (disk->dev->memberlist) + { + list = disk->dev->memberlist (disk); + } + while (list) + { + probe_luks_uuid (list->disk); + tmp = list->next; + free (list); + list = tmp; + } + if (disk->dev->id == GRUB_DISK_DEVICE_LUKS_ID) + grub_util_luks_print_uuid (disk); +} + static int probe_raid_level (grub_disk_t disk) { @@ -194,6 +216,13 @@ probe (const char *path, char *device_name) goto end; } + if (print == PRINT_LUKS_UUID) + { + probe_luks_uuid (dev->disk); + printf ("\n"); + goto end; + } + if (print == PRINT_PARTMAP) { /* Check if dev->disk itself is contained in a partmap. */ @@ -267,8 +296,8 @@ Probe device information for a given path (or device, if the -d option is given) \n\ -d, --device given argument is a system device, not a path\n\ -m, --device-map=FILE use FILE as the device map [default=%s]\n\ - -t, --target=(fs|fs_uuid|fs_label|drive|device|partmap|abstraction)\n\ - print filesystem module, GRUB drive, system device, partition map module or abstraction module [default=fs]\n\ + -t, --target=(fs|fs_uuid|fs_label|drive|device|partmap|abstraction|luks_uuid)\n\ + print filesystem module, GRUB drive, system device, partition map module, abstraction module or LUKS UUID [default=fs]\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -326,6 +355,8 @@ main (int argc, char *argv[]) print = PRINT_PARTMAP; else if (!strcmp (optarg, "abstraction")) print = PRINT_ABSTRACTION; + else if (!strcmp (optarg, "luks_uuid")) + print = PRINT_LUKS_UUID; else usage (1); break; From 8306591ff30a94c5f9dfcaa4ee02a06a8c0021a5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 00:13:56 +0200 Subject: [PATCH 16/43] support LUKS in shell libraries --- util/grub-install.in | 8 ++++++++ util/grub-mkconfig.in | 1 + util/grub-mkconfig_lib.in | 20 +++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/util/grub-install.in b/util/grub-install.in index ff8bea87c..3b3383ab4 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -456,6 +456,8 @@ for dir in "${localedir}"/*; do fi done +is_path_readable_by_grub "${grubdir}" || (echo "${grubdir}" not readable 1>&2 ; exit 1) + # Write device to a variable so we don't have to traverse /dev every time. grub_device="`"$grub_probe" --device-map="${device_map}" --target=device "${grubdir}"`" || exit 1 @@ -536,6 +538,12 @@ if [ "x${devabstraction_module}" = "x" ] ; then exit 1 fi + if [ x$GRUB_LUKS_ENABLE = xy ]; then + for uuid in "`"${grub_probe}" --device "${device}" --target=luks_uuid`"; do + echo "luksmount -u $uuid" + done + fi + echo "search.fs_uuid ${uuid} root " >> "${grubdir}/load.cfg" echo 'set prefix=($root)'"${relative_grubdir}" >> "${grubdir}/load.cfg" config_opt="-c ${grubdir}/load.cfg " diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index afc66f886..41e68bc1b 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -254,6 +254,7 @@ export GRUB_DEFAULT \ GRUB_DISABLE_OS_PROBER \ GRUB_INIT_TUNE \ GRUB_SAVEDEFAULT \ + GRUB_ENABLE_LUKS \ GRUB_BADRAM if test "x${grub_cfg}" != "x"; then diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 2c5fd8c6f..1664b6bbe 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -63,10 +63,22 @@ is_path_readable_by_grub () # ... or if we can't figure out the abstraction module, for example if # memberlist fails on an LVM volume group. - if ${grub_probe} -t abstraction $path > /dev/null 2>&1 ; then : ; else + if abstractions="`"${grub_probe}" -t abstraction "$path"`" 2>&1 ; then + : + else return 1 fi + if [ x$GRUB_LUKS_ENABLE = xy ]; then + return 0 + fi + + for abstraction in $abstractions; do + if [ "x$abstraction" = xluks ]; then + return 1 + fi + done + return 0 } @@ -126,6 +138,12 @@ prepare_grub_to_access_device () echo "insmod ${module}" done + if [ x$GRUB_LUKS_ENABLE = xy ]; then + for uuid in "`"${grub_probe}" --device "${device}" --target=luks_uuid`"; do + echo "luksmount -u $uuid" + done + fi + # If there's a filesystem UUID that GRUB is capable of identifying, use it; # otherwise set root as per value in device.map. echo "set root='`"${grub_probe}" --device "${device}" --target=drive`'" From 4879d87871dceba99558c7911cc15b9ddee4b4bf Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 02:01:39 +0200 Subject: [PATCH 17/43] fix bug caused by import_gcry modifications --- util/import_gcry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/import_gcry.py b/util/import_gcry.py index 0ebfd1f56..727492f10 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -153,8 +153,8 @@ for cipher_file in cipher_files: print ("ERROR: Unknown digest blocksize: %s\n" % mdname) exit (1) - fw.write (" .blocksize = %s\n" - % mdblocksizes [mdname]) + fw.write (" .blocksize = %s\n" + % mdblocksizes [mdname]) ismd = False iscipher = False iscomma = not re.search (",$", line) is None From 95172af9fa30d41b6f37e32ee9e0e79a2e10930d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 02:04:40 +0200 Subject: [PATCH 18/43] Better IV modes support --- grub-core/disk/luks.c | 266 ++++++++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 115 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 997e2eb30..8f8b8dbe4 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -70,12 +70,20 @@ struct grub_luks_phdr typedef struct grub_luks_phdr *grub_luks_phdr_t; typedef enum -{ - GRUB_LUKS_MODE_ECB, - GRUB_LUKS_MODE_CBC_PLAIN, - GRUB_LUKS_MODE_CBC_ESSIV, - GRUB_LUKS_MODE_XTS -} luks_mode_t; + { + GRUB_LUKS_MODE_ECB, + GRUB_LUKS_MODE_CBC, + GRUB_LUKS_MODE_XTS + } luks_mode_t; + +typedef enum + { + GRUB_LUKS_MODE_IV_NULL, + GRUB_LUKS_MODE_IV_PLAIN, + GRUB_LUKS_MODE_IV_PLAIN64, + GRUB_LUKS_MODE_IV_ESSIV, + GRUB_LUKS_MODE_IV_BENBI, + } luks_mode_iv_t; struct grub_luks { @@ -85,8 +93,10 @@ struct grub_luks int ref; grub_crypto_cipher_handle_t cipher; grub_crypto_cipher_handle_t secondary_cipher; + grub_crypto_cipher_handle_t essiv_cipher; const gcry_md_spec_t *essiv_hash, *hash; luks_mode_t mode; + luks_mode_iv_t mode_iv; unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; @@ -112,13 +122,6 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; -static inline void -make_iv (grub_uint32_t *iv, grub_size_t sz, grub_disk_addr_t sector) -{ - grub_memset (iv, 0, sz * sizeof (iv[0])); - iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); -} - /* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ #define POLYNOM 0x87 @@ -141,89 +144,83 @@ gf_mul_x (grub_uint8_t *g) static gcry_err_code_t luks_decrypt (const struct grub_luks *dev, - grub_uint8_t * data, grub_size_t len, grub_size_t sector) + grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) { grub_size_t i; gcry_err_code_t err; - switch (dev->mode) + /* The only mode without IV. */ + if (dev->mode == GRUB_LUKS_MODE_ECB) + return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); + + for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) { - case GRUB_LUKS_MODE_ECB: - return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); + grub_size_t sz = ((dev->cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)); + grub_uint32_t iv[sz]; - case GRUB_LUKS_MODE_CBC_PLAIN: - for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + grub_memset (iv, 0, sz * sizeof (iv[0])); + switch (dev->mode_iv) { - grub_size_t sz = ((dev->cipher->cipher->blocksize - + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)); - grub_uint32_t iv[sz]; - make_iv (iv, sz, sector); + case GRUB_LUKS_MODE_IV_NULL: + break; + case GRUB_LUKS_MODE_IV_PLAIN64: + iv[1] = grub_cpu_to_le32 (sector >> 32); + case GRUB_LUKS_MODE_IV_PLAIN: + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + break; + case GRUB_LUKS_MODE_IV_BENBI: + iv[sz - 2] = grub_cpu_to_be32 (sector >> 32); + iv[sz - 1] = grub_cpu_to_be32 (sector & 0xFFFFFFFF); + break; + case GRUB_LUKS_MODE_IV_ESSIV: + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + } + + switch (dev->mode) + { + case GRUB_LUKS_MODE_CBC: err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, GRUB_DISK_SECTOR_SIZE, iv); if (err) return err; - sector++; + break; + + case GRUB_LUKS_MODE_XTS: + { + int j; + err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + + for (j = 0; j < GRUB_DISK_SECTOR_SIZE; + j += dev->cipher->cipher->blocksize) + { + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + gf_mul_x ((grub_uint8_t *) iv); + } + } + break; + default: + return GPG_ERR_NOT_IMPLEMENTED; } - return GPG_ERR_NO_ERROR; - - case GRUB_LUKS_MODE_CBC_ESSIV: - for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) - { - grub_size_t sz = ((dev->cipher->cipher->blocksize - + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)); - grub_uint32_t iv[sz]; - make_iv (iv, sz, sector); - - err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, - dev->cipher->cipher->blocksize); - if (err) - return err; - err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, - GRUB_DISK_SECTOR_SIZE, iv); - if (err) - return err; - sector++; - } - return GPG_ERR_NO_ERROR; - - case GRUB_LUKS_MODE_XTS: - for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) - { - grub_size_t sz = ((dev->cipher->cipher->blocksize - + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)); - grub_uint32_t iv[sz]; - int j; - make_iv (iv, sz, sector); - - err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, - dev->cipher->cipher->blocksize); - if (err) - return err; - - for (j = 0; j < GRUB_DISK_SECTOR_SIZE; - j += dev->cipher->cipher->blocksize) - { - grub_crypto_xor (data + i + j, data + i + j, iv, - dev->cipher->cipher->blocksize); - err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, - data + i + j, - dev->cipher->cipher->blocksize); - if (err) - return err; - grub_crypto_xor (data + i + j, data + i + j, iv, - dev->cipher->cipher->blocksize); - gf_mul_x ((grub_uint8_t *) iv); - } - sector++; - } - return GPG_ERR_NO_ERROR; - - default: - return GPG_ERR_NOT_IMPLEMENTED; + sector++; } + return GPG_ERR_NO_ERROR; } static int check_uuid, have_it; @@ -238,11 +235,14 @@ configure_ciphers (const struct grub_luks_phdr *header) char uuid[sizeof (header->uuid) + 1]; char ciphername[sizeof (header->cipherName) + 1]; char ciphermode[sizeof (header->cipherMode) + 1]; + char *cipheriv = NULL; char hashspec[sizeof (header->hashSpec) + 1]; grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; + grub_crypto_cipher_handle_t essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; const struct gcry_cipher_spec *ciph; luks_mode_t mode; + luks_mode_iv_t mode_iv; /* Look for LUKS magic sequence. */ if (grub_memcmp (header->magic, LUKS_MAGIC, sizeof (header->magic)) @@ -294,35 +294,26 @@ configure_ciphers (const struct grub_luks_phdr *header) /* Configure the cipher mode. */ if (grub_strcmp (ciphermode, "ecb") == 0) - mode = GRUB_LUKS_MODE_ECB; - else if (grub_strcmp (ciphermode, "cbc-plain") == 0 - || grub_strcmp (ciphermode, "plain") == 0) - mode = GRUB_LUKS_MODE_CBC_PLAIN; - else if (grub_memcmp (ciphermode, "cbc-essiv:", sizeof ("cbc-essiv:") - 1) - == 0) { - mode = GRUB_LUKS_MODE_CBC_ESSIV; - char *hash_str = ciphermode + 10; - - /* Configure the hash and cipher used for ESSIV. */ - essiv_hash = grub_crypto_lookup_md_by_name (hash_str); - if (!essiv_hash) - { - grub_crypto_cipher_close (cipher); - grub_error (GRUB_ERR_FILE_NOT_FOUND, - "Couldn't load %s hash", hash_str); - return NULL; - } - secondary_cipher = grub_crypto_cipher_open (ciph); - if (!secondary_cipher) - { - grub_crypto_cipher_close (cipher); - return NULL; - } + mode = GRUB_LUKS_MODE_ECB; + mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + cipheriv = NULL; } - else if (grub_strcmp (ciphermode, "xts-plain") == 0) + else if (grub_strcmp (ciphermode, "plain") == 0) + { + mode = GRUB_LUKS_MODE_CBC; + mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + cipheriv = NULL; + } + else if (grub_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0) + { + mode = GRUB_LUKS_MODE_CBC; + cipheriv = ciphermode + sizeof ("cbc-") - 1; + } + else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) { mode = GRUB_LUKS_MODE_XTS; + cipheriv = ciphermode + sizeof ("xts-") - 1; secondary_cipher = grub_crypto_cipher_open (ciph); if (!secondary_cipher) { @@ -350,11 +341,51 @@ configure_ciphers (const struct grub_luks_phdr *header) return NULL; } + if (cipheriv == NULL); + else if (grub_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0) + mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) + mode_iv = GRUB_LUKS_MODE_IV_PLAIN64; + else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) + mode_iv = GRUB_LUKS_MODE_IV_BENBI; + else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) + mode_iv = GRUB_LUKS_MODE_IV_NULL; + else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) + { + char *hash_str = cipheriv + 6; + + mode_iv = GRUB_LUKS_MODE_IV_ESSIV; + + /* Configure the hash and cipher used for ESSIV. */ + essiv_hash = grub_crypto_lookup_md_by_name (hash_str); + if (!essiv_hash) + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Couldn't load %s hash", hash_str); + return NULL; + } + essiv_cipher = grub_crypto_cipher_open (ciph); + if (!essiv_cipher) + { + grub_crypto_cipher_close (cipher); + return NULL; + } + } + else + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown IV mode: %s", + cipheriv); + return NULL; + } + /* Configure the hash used for the AF splitter and HMAC. */ hash = grub_crypto_lookup_md_by_name (hashspec); if (!hash) { grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (essiv_cipher); grub_crypto_cipher_close (secondary_cipher); grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", hashspec); @@ -368,7 +399,9 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->offset = grub_be_to_cpu32 (header->payloadOffset); newdev->source_disk = NULL; newdev->mode = mode; + newdev->mode_iv = mode_iv; newdev->secondary_cipher = secondary_cipher; + newdev->essiv_cipher = essiv_cipher; newdev->essiv_hash = essiv_hash; newdev->hash = hash; newdev->id = n++; @@ -392,7 +425,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_size_t length; grub_err_t err; - if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) + if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) { essiv_keysize = dev->essiv_hash->mdlen; hashed_key = grub_malloc (dev->essiv_hash->mdlen); @@ -433,8 +466,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, grub_strlen (passphrase), header->keyblock[i].passwordSalt, - sizeof (header-> - keyblock[i].passwordSalt), + sizeof (header->keyblock[i].passwordSalt), grub_be_to_cpu32 (header->keyblock[i]. passwordIterations), digest, keysize); @@ -460,10 +492,10 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } /* Configure ESSIV if necessary. */ - if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) + if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) { grub_crypto_hash (dev->essiv_hash, hashed_key, digest, keysize); - gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, + gcry_err = grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, essiv_keysize); if (gcry_err) @@ -562,12 +594,12 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } /* Configure ESSIV if necessary. */ - if (dev->mode == GRUB_LUKS_MODE_CBC_ESSIV) + if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) { grub_crypto_hash (dev->essiv_hash, hashed_key, candidate_key, keysize); gcry_err = - grub_crypto_cipher_set_key (dev->secondary_cipher, hashed_key, + grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, essiv_keysize); if (gcry_err) { @@ -604,6 +636,7 @@ luks_close (grub_luks_t luks) { grub_crypto_cipher_close (luks->cipher); grub_crypto_cipher_close (luks->secondary_cipher); + grub_crypto_cipher_close (luks->essiv_cipher); grub_free (luks); } @@ -888,6 +921,8 @@ grub_util_luks_print_ciphers (grub_disk_t disk) grub_printf ("%s ", dev->cipher->cipher->modname); if (dev->secondary_cipher) grub_printf ("%s ", dev->secondary_cipher->cipher->modname); + if (dev->essiv_cipher) + grub_printf ("%s ", dev->essiv_cipher->cipher->modname); if (dev->hash) grub_printf ("%s ", dev->hash->modname); if (dev->essiv_hash) @@ -913,6 +948,7 @@ luks_cleanup (void) grub_free (dev->source); grub_free (dev->cipher); grub_free (dev->secondary_cipher); + grub_free (dev->essiv_cipher); tmp = dev->next; grub_free (dev); dev = tmp; From ed38c849f4bc6b193431360dd21424c013b594fd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 02:27:45 +0200 Subject: [PATCH 19/43] pcbc support --- grub-core/disk/luks.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 8f8b8dbe4..ac3b81e99 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -73,6 +73,7 @@ typedef enum { GRUB_LUKS_MODE_ECB, GRUB_LUKS_MODE_CBC, + GRUB_LUKS_MODE_PCBC, GRUB_LUKS_MODE_XTS } luks_mode_t; @@ -142,6 +143,29 @@ gf_mul_x (grub_uint8_t *g) g[0] ^= POLYNOM; } +static gcry_err_code_t +grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv) +{ + grub_uint8_t *inptr, *outptr, *end; + grub_uint8_t ivt[cipher->cipher->blocksize]; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + { + grub_memcpy (ivt, inptr, cipher->cipher->blocksize); + cipher->cipher->decrypt (cipher->ctx, outptr, inptr); + grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize); + grub_crypto_xor (iv, ivt, outptr, cipher->cipher->blocksize); + } + return GPG_ERR_NO_ERROR; +} + static gcry_err_code_t luks_decrypt (const struct grub_luks *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) @@ -191,6 +215,12 @@ luks_decrypt (const struct grub_luks *dev, return err; break; + case GRUB_LUKS_MODE_PCBC: + err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, + GRUB_DISK_SECTOR_SIZE, iv); + if (err) + return err; + break; case GRUB_LUKS_MODE_XTS: { int j; @@ -310,6 +340,11 @@ configure_ciphers (const struct grub_luks_phdr *header) mode = GRUB_LUKS_MODE_CBC; cipheriv = ciphermode + sizeof ("cbc-") - 1; } + else if (grub_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0) + { + mode = GRUB_LUKS_MODE_PCBC; + cipheriv = ciphermode + sizeof ("pcbc-") - 1; + } else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) { mode = GRUB_LUKS_MODE_XTS; From 4b35060f6fd84a6fc5fb19974098d66e16a44964 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 03:18:07 +0200 Subject: [PATCH 20/43] Fix benbi --- grub-core/disk/luks.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index ac3b81e99..8af087eea 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -98,6 +98,7 @@ struct grub_luks const gcry_md_spec_t *essiv_hash, *hash; luks_mode_t mode; luks_mode_iv_t mode_iv; + int benbi_log; unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; @@ -195,8 +196,11 @@ luks_decrypt (const struct grub_luks *dev, iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); break; case GRUB_LUKS_MODE_IV_BENBI: - iv[sz - 2] = grub_cpu_to_be32 (sector >> 32); - iv[sz - 1] = grub_cpu_to_be32 (sector & 0xFFFFFFFF); + { + grub_uint64_t num = (sector << dev->benbi_log) + 1; + iv[sz - 2] = grub_cpu_to_be32 (num >> 32); + iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF); + } break; case GRUB_LUKS_MODE_IV_ESSIV: iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); @@ -273,7 +277,8 @@ configure_ciphers (const struct grub_luks_phdr *header) const struct gcry_cipher_spec *ciph; luks_mode_t mode; luks_mode_iv_t mode_iv; - + int benbi_log = 0; + /* Look for LUKS magic sequence. */ if (grub_memcmp (header->magic, LUKS_MAGIC, sizeof (header->magic)) || grub_be_to_cpu16 (header->version) != 1) @@ -382,7 +387,16 @@ configure_ciphers (const struct grub_luks_phdr *header) else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) mode_iv = GRUB_LUKS_MODE_IV_PLAIN64; else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) + { + if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1) + || cipher->cipher->blocksize == 0) + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported benbi blocksize: %d", + cipher->cipher->blocksize); + for (benbi_log = 0; + (cipher->cipher->blocksize << benbi_log) < GRUB_DISK_SECTOR_SIZE; + benbi_log++); mode_iv = GRUB_LUKS_MODE_IV_BENBI; + } else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) mode_iv = GRUB_LUKS_MODE_IV_NULL; else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) @@ -433,6 +447,7 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->cipher = cipher; newdev->offset = grub_be_to_cpu32 (header->payloadOffset); newdev->source_disk = NULL; + newdev->benbi_log = benbi_log; newdev->mode = mode; newdev->mode_iv = mode_iv; newdev->secondary_cipher = secondary_cipher; From 2f179c3236573a4aca5816aac4b3bb5ed16f8268 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 04:51:53 +0200 Subject: [PATCH 21/43] LRW support --- grub-core/disk/luks.c | 120 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 8af087eea..775a10f20 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -74,7 +74,8 @@ typedef enum GRUB_LUKS_MODE_ECB, GRUB_LUKS_MODE_CBC, GRUB_LUKS_MODE_PCBC, - GRUB_LUKS_MODE_XTS + GRUB_LUKS_MODE_XTS, + GRUB_LUKS_MODE_LRW } luks_mode_t; typedef enum @@ -86,6 +87,10 @@ typedef enum GRUB_LUKS_MODE_IV_BENBI, } luks_mode_iv_t; +/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ +#define GF_POLYNOM 0x87 +#define GF_SIZE 128 + struct grub_luks { char *source; @@ -102,6 +107,7 @@ struct grub_luks unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; + grub_uint8_t lrw_key[GF_SIZE / 8]; #ifdef GRUB_UTIL char *cheat; int cheat_fd; @@ -124,16 +130,13 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; -/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ -#define POLYNOM 0x87 - static void gf_mul_x (grub_uint8_t *g) { int over = 0, over2 = 0; int j; - for (j = 0; j < 16; j++) + for (j = 0; j < GF_SIZE / 8; j++) { over2 = !!(g[j] & 0x80); g[j] <<= 1; @@ -141,7 +144,40 @@ gf_mul_x (grub_uint8_t *g) over = over2; } if (over) - g[0] ^= POLYNOM; + g[0] ^= GF_POLYNOM; +} + + +static void +gf_mul_x_be (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = GF_SIZE / 8 - 1; j >= 0; j--) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[GF_SIZE / 8 - 1] ^= GF_POLYNOM; +} + +static void +gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) +{ + int i; + grub_uint8_t t[GF_SIZE / 8]; + grub_memset (o, 0, GF_SIZE / 8); + grub_memcpy (t, b, GF_SIZE / 8); + for (i = 0; i < GF_SIZE; i++) + { + if (((a[GF_SIZE / 8 - i / 8 - 1] >> (i % 8))) & 1) + grub_crypto_xor (o, o, t, GF_SIZE / 8); + gf_mul_x_be (t); + } } static gcry_err_code_t @@ -249,6 +285,34 @@ luks_decrypt (const struct grub_luks *dev, } } break; + case GRUB_LUKS_MODE_LRW: + { + int j, k; + for (j = 0; + j < GRUB_DISK_SECTOR_SIZE; + j += dev->cipher->cipher->blocksize) + { + grub_uint8_t x[sz * sizeof (grub_uint32_t)]; + + gf_mul_be (x, dev->lrw_key, (grub_uint8_t *) iv); + grub_crypto_xor (data + i + j, data + i + j, x, + dev->cipher->cipher->blocksize); + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, x, + dev->cipher->cipher->blocksize); + for (k = sz - 1; k >= 0; k++) + { + iv[k] = grub_cpu_to_be32 (grub_be_to_cpu32 (iv[k]) + 1); + if (iv[k] != 0) + break; + } + } + } + break; default: return GPG_ERR_NOT_IMPLEMENTED; } @@ -360,19 +424,33 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_crypto_cipher_close (cipher); return NULL; } - if (cipher->cipher->blocksize != 16) + if (cipher->cipher->blocksize != GF_SIZE / 8) { + grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", cipher->cipher->blocksize); return NULL; } - if (secondary_cipher->cipher->blocksize != 16) + if (secondary_cipher->cipher->blocksize != GF_SIZE / 8) { + grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", secondary_cipher->cipher->blocksize); return NULL; } } + else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) + { + mode = GRUB_LUKS_MODE_LRW; + cipheriv = ciphermode + sizeof ("lrw-") - 1; + if (cipher->cipher->blocksize != GF_SIZE / 8) + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", + cipher->cipher->blocksize); + return NULL; + } + } else { grub_crypto_cipher_close (cipher); @@ -505,6 +583,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) { gcry_err_code_t gcry_err; + int real_keysize; /* Check if keyslot is enabled. */ if (grub_be_to_cpu32 (header->keyblock[i].active) != LUKS_KEY_ENABLED) @@ -530,10 +609,14 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_dprintf ("luks", "PBKDF2 done\n"); + real_keysize = keysize; + if (dev->mode == GRUB_LUKS_MODE_XTS) + real_keysize /= 2; + if (dev->mode == GRUB_LUKS_MODE_LRW) + real_keysize -= dev->cipher->cipher->blocksize; + /* Set the PBKDF2 output as the cipher key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, - (dev->mode == GRUB_LUKS_MODE_XTS) - ? (keysize / 2) : keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, real_keysize); if (gcry_err) { grub_free (hashed_key); @@ -559,7 +642,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (dev->mode == GRUB_LUKS_MODE_XTS) { gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - digest + (keysize / 2), + digest + real_keysize, keysize / 2); if (gcry_err) { @@ -569,6 +652,10 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } } + if (dev->mode == GRUB_LUKS_MODE_LRW) + grub_memcpy (dev->lrw_key, digest + real_keysize, + dev->cipher->cipher->blocksize); + length = grub_be_to_cpu32 (header->keyBytes) * grub_be_to_cpu32 (header->keyblock[i].stripes); @@ -634,8 +721,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, /* Set the master key. */ gcry_err = grub_crypto_cipher_set_key (dev->cipher, candidate_key, - (dev->mode == GRUB_LUKS_MODE_XTS) - ? (keysize / 2) : keysize); + real_keysize); if (gcry_err) { grub_free (hashed_key); @@ -662,7 +748,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (dev->mode == GRUB_LUKS_MODE_XTS) { gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - candidate_key + (keysize / 2), + candidate_key + real_keysize, keysize / 2); if (gcry_err) { @@ -672,6 +758,10 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } } + if (dev->mode == GRUB_LUKS_MODE_LRW) + grub_memcpy (dev->lrw_key, candidate_key + real_keysize, + dev->cipher->cipher->blocksize); + grub_free (split_key); grub_free (hashed_key); From 6fd80b9ac45df8a5b417f1a215e8cd428ebe8b82 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 12:40:43 +0200 Subject: [PATCH 22/43] factor luks_set_key out --- grub-core/disk/luks.c | 151 ++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 102 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 775a10f20..b1c738f2a 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -537,6 +537,50 @@ configure_ciphers (const struct grub_luks_phdr *header) return newdev; } +static gcry_err_code_t +luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) +{ + gcry_err_code_t err; + int real_keysize; + + real_keysize = keysize; + if (dev->mode == GRUB_LUKS_MODE_XTS) + real_keysize /= 2; + if (dev->mode == GRUB_LUKS_MODE_LRW) + real_keysize -= dev->cipher->cipher->blocksize; + + /* Set the PBKDF2 output as the cipher key. */ + err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize); + if (err) + return err; + + /* Configure ESSIV if necessary. */ + if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) + { + grub_size_t essiv_keysize = dev->essiv_hash->mdlen; + grub_uint8_t hashed_key[essiv_keysize]; + + grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize); + err = grub_crypto_cipher_set_key (dev->essiv_cipher, + hashed_key, essiv_keysize); + if (err) + return err; + } + if (dev->mode == GRUB_LUKS_MODE_XTS) + { + err = grub_crypto_cipher_set_key (dev->secondary_cipher, + key + real_keysize, + keysize / 2); + if (err) + return err; + } + + if (dev->mode == GRUB_LUKS_MODE_LRW) + grub_memcpy (dev->lrw_key, key + real_keysize, + dev->cipher->cipher->blocksize); + return GPG_ERR_NO_ERROR; +} + static grub_err_t luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, const char *name, grub_disk_t source) @@ -544,37 +588,23 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_size_t keysize = grub_be_to_cpu32 (header->keyBytes); grub_uint8_t candidate_key[keysize]; grub_uint8_t digest[keysize]; - grub_uint8_t *hashed_key = NULL; grub_uint8_t *split_key = NULL; char passphrase[MAX_PASSPHRASE] = ""; grub_uint8_t candidate_digest[sizeof (header->mkDigest)]; unsigned i; - grub_size_t essiv_keysize = 0; grub_size_t length; grub_err_t err; - if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) - { - essiv_keysize = dev->essiv_hash->mdlen; - hashed_key = grub_malloc (dev->essiv_hash->mdlen); - if (!hashed_key) - return grub_errno; - } - grub_printf ("Attempting to decrypt master key...\n"); split_key = grub_malloc (keysize * LUKS_STRIPES); if (!split_key) - { - grub_free (hashed_key); - return grub_errno; - } + return grub_errno; /* Get the passphrase from the user. */ grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid); if (!grub_password_get (passphrase, MAX_PASSPHRASE)) { - grub_free (hashed_key); grub_free (split_key); return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); } @@ -583,7 +613,6 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) { gcry_err_code_t gcry_err; - int real_keysize; /* Check if keyslot is enabled. */ if (grub_be_to_cpu32 (header->keyblock[i].active) != LUKS_KEY_ENABLED) @@ -602,63 +631,21 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } grub_dprintf ("luks", "PBKDF2 done\n"); - real_keysize = keysize; - if (dev->mode == GRUB_LUKS_MODE_XTS) - real_keysize /= 2; - if (dev->mode == GRUB_LUKS_MODE_LRW) - real_keysize -= dev->cipher->cipher->blocksize; - - /* Set the PBKDF2 output as the cipher key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, real_keysize); + gcry_err = luks_setkey (dev, digest, keysize); if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } - /* Configure ESSIV if necessary. */ - if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) - { - grub_crypto_hash (dev->essiv_hash, hashed_key, digest, keysize); - gcry_err = grub_crypto_cipher_set_key (dev->essiv_cipher, - hashed_key, - essiv_keysize); - if (gcry_err) - { - grub_free (hashed_key); - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - } - - if (dev->mode == GRUB_LUKS_MODE_XTS) - { - gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - digest + real_keysize, - keysize / 2); - if (gcry_err) - { - grub_free (hashed_key); - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - } - - if (dev->mode == GRUB_LUKS_MODE_LRW) - grub_memcpy (dev->lrw_key, digest + real_keysize, - dev->cipher->cipher->blocksize); - - length = - grub_be_to_cpu32 (header->keyBytes) * - grub_be_to_cpu32 (header->keyblock[i].stripes); + length = (grub_be_to_cpu32 (header->keyBytes) + * grub_be_to_cpu32 (header->keyblock[i].stripes)); /* Read and decrypt the key material from the disk. */ err = grub_disk_read (source, @@ -667,7 +654,6 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, length, split_key); if (err) { - grub_free (hashed_key); grub_free (split_key); return err; } @@ -675,7 +661,6 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, gcry_err = luks_decrypt (dev, split_key, length, 0); if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } @@ -685,7 +670,6 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_be_to_cpu32 (header->keyblock[i].stripes)); if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } @@ -703,7 +687,6 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, sizeof (candidate_digest)); if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } @@ -720,50 +703,14 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, candidate_key, - real_keysize); + gcry_err = luks_setkey (dev, candidate_key, keysize); if (gcry_err) { - grub_free (hashed_key); grub_free (split_key); return grub_crypto_gcry_error (gcry_err); } - /* Configure ESSIV if necessary. */ - if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) - { - grub_crypto_hash (dev->essiv_hash, hashed_key, - candidate_key, keysize); - gcry_err = - grub_crypto_cipher_set_key (dev->essiv_cipher, hashed_key, - essiv_keysize); - if (gcry_err) - { - grub_free (hashed_key); - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - } - - if (dev->mode == GRUB_LUKS_MODE_XTS) - { - gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - candidate_key + real_keysize, - keysize / 2); - if (gcry_err) - { - grub_free (hashed_key); - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - } - - if (dev->mode == GRUB_LUKS_MODE_LRW) - grub_memcpy (dev->lrw_key, candidate_key + real_keysize, - dev->cipher->cipher->blocksize); - grub_free (split_key); - grub_free (hashed_key); return GRUB_ERR_NONE; } From 6f33a8eebc99d6b3cb22f17a94779f6171aca17b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 16:41:43 +0200 Subject: [PATCH 23/43] optimize LRW --- grub-core/disk/luks.c | 114 +++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 25 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index b1c738f2a..542ad4c9c 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -108,6 +108,7 @@ struct grub_luks enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; grub_uint8_t lrw_key[GF_SIZE / 8]; + grub_uint8_t *lrw_precalc; #ifdef GRUB_UTIL char *cheat; int cheat_fd; @@ -203,6 +204,68 @@ grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, return GPG_ERR_NO_ERROR; } +#define GF_BYTES (GF_SIZE / 8) +#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GF_BYTES) + +struct lrw_sector +{ + grub_uint8_t low[GF_BYTES]; + grub_uint8_t high[GF_BYTES]; + grub_uint8_t low_byte, low_byte_c; +}; + +static void +generate_lrw_sector (struct lrw_sector *sec, + const struct grub_luks *dev, + const grub_uint8_t *iv) +{ + grub_uint8_t idx[GF_BYTES]; + grub_uint16_t c; + int j; + grub_memcpy (idx, iv, GF_BYTES); + sec->low_byte = (idx[GF_BYTES - 1] & (GF_PER_SECTOR - 1)); + sec->low_byte_c = (((GF_PER_SECTOR - 1) & ~sec->low_byte) + 1); + idx[GF_BYTES - 1] &= ~(GF_PER_SECTOR - 1); + gf_mul_be (sec->low, dev->lrw_key, idx); + if (!sec->low_byte) + return; + + c = idx[GF_BYTES - 1] + GF_PER_SECTOR; + if (c & 0x100) + { + for (j = GF_BYTES - 2; j >= 0; j--) + { + idx[j]++; + if (idx[j] != 0) + break; + } + } + idx[GF_BYTES - 1] = c; + gf_mul_be (sec->high, dev->lrw_key, idx); +} + +static void __attribute__ ((unused)) +lrw_xor (const struct lrw_sector *sec, + const struct grub_luks *dev, + grub_uint8_t *b) +{ + int i; + + for (i = 0; i < sec->low_byte_c * GF_BYTES; i += GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->low, GF_BYTES); + grub_crypto_xor (b, b, dev->lrw_precalc + GF_BYTES * sec->low_byte, + sec->low_byte_c * GF_BYTES); + if (!sec->low_byte) + return; + + for (i = sec->low_byte_c * GF_BYTES; + i < GRUB_DISK_SECTOR_SIZE; i += GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->high, GF_BYTES); + grub_crypto_xor (b + sec->low_byte_c * GF_BYTES, + b + sec->low_byte_c * GF_BYTES, + dev->lrw_precalc, sec->low_byte * GF_BYTES); +} + static gcry_err_code_t luks_decrypt (const struct grub_luks *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) @@ -287,30 +350,16 @@ luks_decrypt (const struct grub_luks *dev, break; case GRUB_LUKS_MODE_LRW: { - int j, k; - for (j = 0; - j < GRUB_DISK_SECTOR_SIZE; - j += dev->cipher->cipher->blocksize) - { - grub_uint8_t x[sz * sizeof (grub_uint32_t)]; + struct lrw_sector sec; - gf_mul_be (x, dev->lrw_key, (grub_uint8_t *) iv); - grub_crypto_xor (data + i + j, data + i + j, x, - dev->cipher->cipher->blocksize); - err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, - data + i + j, - dev->cipher->cipher->blocksize); - if (err) - return err; - grub_crypto_xor (data + i + j, data + i + j, x, - dev->cipher->cipher->blocksize); - for (k = sz - 1; k >= 0; k++) - { - iv[k] = grub_cpu_to_be32 (grub_be_to_cpu32 (iv[k]) + 1); - if (iv[k] != 0) - break; - } - } + generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv); + lrw_xor (&sec, dev, data + i); + + err = grub_crypto_ecb_decrypt (dev->cipher, data + i, + data + i, GRUB_DISK_SECTOR_SIZE); + if (err) + return err; + lrw_xor (&sec, dev, data + i); } break; default: @@ -576,8 +625,23 @@ luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) } if (dev->mode == GRUB_LUKS_MODE_LRW) - grub_memcpy (dev->lrw_key, key + real_keysize, - dev->cipher->cipher->blocksize); + { + int i; + grub_uint8_t idx[GF_SIZE / 8]; + + grub_free (dev->lrw_precalc); + grub_memcpy (dev->lrw_key, key + real_keysize, + dev->cipher->cipher->blocksize); + dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (!dev->lrw_precalc) + return GPG_ERR_OUT_OF_MEMORY; + grub_memset (idx, 0, GF_SIZE / 8); + for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i += GF_SIZE / 8) + { + idx[15] = i / (GF_SIZE / 8); + gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); + } + } return GPG_ERR_NO_ERROR; } From b896ae82db4584b29ebbfabac99c02a6abd070d7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 16:43:55 +0200 Subject: [PATCH 24/43] small readability improvement --- grub-core/disk/luks.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 542ad4c9c..4a35ef011 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -90,6 +90,8 @@ typedef enum /* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ #define GF_POLYNOM 0x87 #define GF_SIZE 128 +#define GF_BYTES (GF_SIZE / 8) +#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GF_BYTES) struct grub_luks { @@ -107,7 +109,7 @@ struct grub_luks unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; - grub_uint8_t lrw_key[GF_SIZE / 8]; + grub_uint8_t lrw_key[GF_BYTES]; grub_uint8_t *lrw_precalc; #ifdef GRUB_UTIL char *cheat; @@ -137,7 +139,7 @@ gf_mul_x (grub_uint8_t *g) int over = 0, over2 = 0; int j; - for (j = 0; j < GF_SIZE / 8; j++) + for (j = 0; j < GF_BYTES; j++) { over2 = !!(g[j] & 0x80); g[j] <<= 1; @@ -155,7 +157,7 @@ gf_mul_x_be (grub_uint8_t *g) int over = 0, over2 = 0; int j; - for (j = GF_SIZE / 8 - 1; j >= 0; j--) + for (j = GF_BYTES - 1; j >= 0; j--) { over2 = !!(g[j] & 0x80); g[j] <<= 1; @@ -163,20 +165,20 @@ gf_mul_x_be (grub_uint8_t *g) over = over2; } if (over) - g[GF_SIZE / 8 - 1] ^= GF_POLYNOM; + g[GF_BYTES - 1] ^= GF_POLYNOM; } static void gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) { int i; - grub_uint8_t t[GF_SIZE / 8]; - grub_memset (o, 0, GF_SIZE / 8); - grub_memcpy (t, b, GF_SIZE / 8); + grub_uint8_t t[GF_BYTES]; + grub_memset (o, 0, GF_BYTES); + grub_memcpy (t, b, GF_BYTES); for (i = 0; i < GF_SIZE; i++) { - if (((a[GF_SIZE / 8 - i / 8 - 1] >> (i % 8))) & 1) - grub_crypto_xor (o, o, t, GF_SIZE / 8); + if (((a[GF_BYTES - i / 8 - 1] >> (i % 8))) & 1) + grub_crypto_xor (o, o, t, GF_BYTES); gf_mul_x_be (t); } } @@ -204,9 +206,6 @@ grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, return GPG_ERR_NO_ERROR; } -#define GF_BYTES (GF_SIZE / 8) -#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GF_BYTES) - struct lrw_sector { grub_uint8_t low[GF_BYTES]; @@ -473,14 +472,14 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_crypto_cipher_close (cipher); return NULL; } - if (cipher->cipher->blocksize != GF_SIZE / 8) + if (cipher->cipher->blocksize != GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", cipher->cipher->blocksize); return NULL; } - if (secondary_cipher->cipher->blocksize != GF_SIZE / 8) + if (secondary_cipher->cipher->blocksize != GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", @@ -492,7 +491,7 @@ configure_ciphers (const struct grub_luks_phdr *header) { mode = GRUB_LUKS_MODE_LRW; cipheriv = ciphermode + sizeof ("lrw-") - 1; - if (cipher->cipher->blocksize != GF_SIZE / 8) + if (cipher->cipher->blocksize != GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", @@ -627,7 +626,7 @@ luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) if (dev->mode == GRUB_LUKS_MODE_LRW) { int i; - grub_uint8_t idx[GF_SIZE / 8]; + grub_uint8_t idx[GF_BYTES]; grub_free (dev->lrw_precalc); grub_memcpy (dev->lrw_key, key + real_keysize, @@ -635,10 +634,10 @@ luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE); if (!dev->lrw_precalc) return GPG_ERR_OUT_OF_MEMORY; - grub_memset (idx, 0, GF_SIZE / 8); - for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i += GF_SIZE / 8) + grub_memset (idx, 0, GF_BYTES); + for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i += GF_BYTES) { - idx[15] = i / (GF_SIZE / 8); + idx[15] = i / GF_BYTES; gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); } } From fcf3bfb6ffcd03e2b134f6b1c09571351793d50b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 16:45:00 +0200 Subject: [PATCH 25/43] small readability improvement --- grub-core/disk/luks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 4a35ef011..a9590cab5 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -637,7 +637,7 @@ luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) grub_memset (idx, 0, GF_BYTES); for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i += GF_BYTES) { - idx[15] = i / GF_BYTES; + idx[GF_BYTES - 1] = i / GF_BYTES; gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); } } From 8585e54becc32be8cb8e45b85fd14f5235a5c2dd Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Apr 2011 18:00:42 +0200 Subject: [PATCH 26/43] factor cryptodisk part out --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 + grub-core/disk/luks.c | 713 ++++------------------------------- grub-core/kern/emu/getroot.c | 2 +- include/grub/disk.h | 2 +- include/grub/emu/hostdisk.h | 1 - util/grub-probe.c | 11 +- 7 files changed, 80 insertions(+), 655 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index 693a7d127..ffa7a794f 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -23,6 +23,7 @@ library = { common = grub-core/kern/partition.c; common = grub-core/lib/crypto.c; common = grub-core/disk/luks.c; + common = grub-core/disk/cryptodisk.c; common = grub-core/disk/AFSplitter.c; common = grub-core/lib/pbkdf2.c; common = grub-core/commands/extcmd.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 8dc810200..c55fff13a 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -768,6 +768,11 @@ module = { common = disk/loopback.c; }; +module = { + name = cryptodisk; + common = disk/cryptodisk.c; +}; + module = { name = luks; common = disk/luks.c; diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index a9590cab5..361beb756 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -16,6 +16,7 @@ * along with GRUB. If not, see . */ +#include #include #include #include @@ -25,15 +26,6 @@ #include #include #include -#ifdef GRUB_UTIL -#include -#include -#include -#include -#include -#include -#include -#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -69,59 +61,6 @@ struct grub_luks_phdr typedef struct grub_luks_phdr *grub_luks_phdr_t; -typedef enum - { - GRUB_LUKS_MODE_ECB, - GRUB_LUKS_MODE_CBC, - GRUB_LUKS_MODE_PCBC, - GRUB_LUKS_MODE_XTS, - GRUB_LUKS_MODE_LRW - } luks_mode_t; - -typedef enum - { - GRUB_LUKS_MODE_IV_NULL, - GRUB_LUKS_MODE_IV_PLAIN, - GRUB_LUKS_MODE_IV_PLAIN64, - GRUB_LUKS_MODE_IV_ESSIV, - GRUB_LUKS_MODE_IV_BENBI, - } luks_mode_iv_t; - -/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ -#define GF_POLYNOM 0x87 -#define GF_SIZE 128 -#define GF_BYTES (GF_SIZE / 8) -#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GF_BYTES) - -struct grub_luks -{ - char *source; - grub_uint32_t offset; - grub_disk_t source_disk; - int ref; - grub_crypto_cipher_handle_t cipher; - grub_crypto_cipher_handle_t secondary_cipher; - grub_crypto_cipher_handle_t essiv_cipher; - const gcry_md_spec_t *essiv_hash, *hash; - luks_mode_t mode; - luks_mode_iv_t mode_iv; - int benbi_log; - unsigned long id, source_id; - enum grub_disk_dev_id source_dev_id; - char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; - grub_uint8_t lrw_key[GF_BYTES]; - grub_uint8_t *lrw_precalc; -#ifdef GRUB_UTIL - char *cheat; - int cheat_fd; -#endif - struct grub_luks *next; -}; -typedef struct grub_luks *grub_luks_t; - -static grub_luks_t luks_list = NULL; -static grub_uint8_t n = 0; - gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst, grub_size_t blocksize, grub_size_t blocknumbers); @@ -133,249 +72,13 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; -static void -gf_mul_x (grub_uint8_t *g) -{ - int over = 0, over2 = 0; - int j; - - for (j = 0; j < GF_BYTES; j++) - { - over2 = !!(g[j] & 0x80); - g[j] <<= 1; - g[j] |= over; - over = over2; - } - if (over) - g[0] ^= GF_POLYNOM; -} - - -static void -gf_mul_x_be (grub_uint8_t *g) -{ - int over = 0, over2 = 0; - int j; - - for (j = GF_BYTES - 1; j >= 0; j--) - { - over2 = !!(g[j] & 0x80); - g[j] <<= 1; - g[j] |= over; - over = over2; - } - if (over) - g[GF_BYTES - 1] ^= GF_POLYNOM; -} - -static void -gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) -{ - int i; - grub_uint8_t t[GF_BYTES]; - grub_memset (o, 0, GF_BYTES); - grub_memcpy (t, b, GF_BYTES); - for (i = 0; i < GF_SIZE; i++) - { - if (((a[GF_BYTES - i / 8 - 1] >> (i % 8))) & 1) - grub_crypto_xor (o, o, t, GF_BYTES); - gf_mul_x_be (t); - } -} - -static gcry_err_code_t -grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, - void *out, void *in, grub_size_t size, - void *iv) -{ - grub_uint8_t *inptr, *outptr, *end; - grub_uint8_t ivt[cipher->cipher->blocksize]; - if (!cipher->cipher->decrypt) - return GPG_ERR_NOT_SUPPORTED; - if (size % cipher->cipher->blocksize != 0) - return GPG_ERR_INV_ARG; - end = (grub_uint8_t *) in + size; - for (inptr = in, outptr = out; inptr < end; - inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) - { - grub_memcpy (ivt, inptr, cipher->cipher->blocksize); - cipher->cipher->decrypt (cipher->ctx, outptr, inptr); - grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize); - grub_crypto_xor (iv, ivt, outptr, cipher->cipher->blocksize); - } - return GPG_ERR_NO_ERROR; -} - -struct lrw_sector -{ - grub_uint8_t low[GF_BYTES]; - grub_uint8_t high[GF_BYTES]; - grub_uint8_t low_byte, low_byte_c; -}; - -static void -generate_lrw_sector (struct lrw_sector *sec, - const struct grub_luks *dev, - const grub_uint8_t *iv) -{ - grub_uint8_t idx[GF_BYTES]; - grub_uint16_t c; - int j; - grub_memcpy (idx, iv, GF_BYTES); - sec->low_byte = (idx[GF_BYTES - 1] & (GF_PER_SECTOR - 1)); - sec->low_byte_c = (((GF_PER_SECTOR - 1) & ~sec->low_byte) + 1); - idx[GF_BYTES - 1] &= ~(GF_PER_SECTOR - 1); - gf_mul_be (sec->low, dev->lrw_key, idx); - if (!sec->low_byte) - return; - - c = idx[GF_BYTES - 1] + GF_PER_SECTOR; - if (c & 0x100) - { - for (j = GF_BYTES - 2; j >= 0; j--) - { - idx[j]++; - if (idx[j] != 0) - break; - } - } - idx[GF_BYTES - 1] = c; - gf_mul_be (sec->high, dev->lrw_key, idx); -} - -static void __attribute__ ((unused)) -lrw_xor (const struct lrw_sector *sec, - const struct grub_luks *dev, - grub_uint8_t *b) -{ - int i; - - for (i = 0; i < sec->low_byte_c * GF_BYTES; i += GF_BYTES) - grub_crypto_xor (b + i, b + i, sec->low, GF_BYTES); - grub_crypto_xor (b, b, dev->lrw_precalc + GF_BYTES * sec->low_byte, - sec->low_byte_c * GF_BYTES); - if (!sec->low_byte) - return; - - for (i = sec->low_byte_c * GF_BYTES; - i < GRUB_DISK_SECTOR_SIZE; i += GF_BYTES) - grub_crypto_xor (b + i, b + i, sec->high, GF_BYTES); - grub_crypto_xor (b + sec->low_byte_c * GF_BYTES, - b + sec->low_byte_c * GF_BYTES, - dev->lrw_precalc, sec->low_byte * GF_BYTES); -} - -static gcry_err_code_t -luks_decrypt (const struct grub_luks *dev, - grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) -{ - grub_size_t i; - gcry_err_code_t err; - - /* The only mode without IV. */ - if (dev->mode == GRUB_LUKS_MODE_ECB) - return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); - - for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) - { - grub_size_t sz = ((dev->cipher->cipher->blocksize - + sizeof (grub_uint32_t) - 1) - / sizeof (grub_uint32_t)); - grub_uint32_t iv[sz]; - - grub_memset (iv, 0, sz * sizeof (iv[0])); - switch (dev->mode_iv) - { - case GRUB_LUKS_MODE_IV_NULL: - break; - case GRUB_LUKS_MODE_IV_PLAIN64: - iv[1] = grub_cpu_to_le32 (sector >> 32); - case GRUB_LUKS_MODE_IV_PLAIN: - iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); - break; - case GRUB_LUKS_MODE_IV_BENBI: - { - grub_uint64_t num = (sector << dev->benbi_log) + 1; - iv[sz - 2] = grub_cpu_to_be32 (num >> 32); - iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF); - } - break; - case GRUB_LUKS_MODE_IV_ESSIV: - iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); - err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, - dev->cipher->cipher->blocksize); - if (err) - return err; - } - - switch (dev->mode) - { - case GRUB_LUKS_MODE_CBC: - err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, - GRUB_DISK_SECTOR_SIZE, iv); - if (err) - return err; - break; - - case GRUB_LUKS_MODE_PCBC: - err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, - GRUB_DISK_SECTOR_SIZE, iv); - if (err) - return err; - break; - case GRUB_LUKS_MODE_XTS: - { - int j; - err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, - dev->cipher->cipher->blocksize); - if (err) - return err; - - for (j = 0; j < GRUB_DISK_SECTOR_SIZE; - j += dev->cipher->cipher->blocksize) - { - grub_crypto_xor (data + i + j, data + i + j, iv, - dev->cipher->cipher->blocksize); - err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, - data + i + j, - dev->cipher->cipher->blocksize); - if (err) - return err; - grub_crypto_xor (data + i + j, data + i + j, iv, - dev->cipher->cipher->blocksize); - gf_mul_x ((grub_uint8_t *) iv); - } - } - break; - case GRUB_LUKS_MODE_LRW: - { - struct lrw_sector sec; - - generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv); - lrw_xor (&sec, dev, data + i); - - err = grub_crypto_ecb_decrypt (dev->cipher, data + i, - data + i, GRUB_DISK_SECTOR_SIZE); - if (err) - return err; - lrw_xor (&sec, dev, data + i); - } - break; - default: - return GPG_ERR_NOT_IMPLEMENTED; - } - sector++; - } - return GPG_ERR_NO_ERROR; -} - static int check_uuid, have_it; static char *search_uuid; -static grub_luks_t +static grub_cryptodisk_t configure_ciphers (const struct grub_luks_phdr *header) { - grub_luks_t newdev; + grub_cryptodisk_t newdev; const char *iptr; char *optr; char uuid[sizeof (header->uuid) + 1]; @@ -387,8 +90,8 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_crypto_cipher_handle_t essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; const struct gcry_cipher_spec *ciph; - luks_mode_t mode; - luks_mode_iv_t mode_iv; + grub_cryptodisk_mode_t mode; + grub_cryptodisk_mode_iv_t mode_iv; int benbi_log = 0; /* Look for LUKS magic sequence. */ @@ -442,29 +145,29 @@ configure_ciphers (const struct grub_luks_phdr *header) /* Configure the cipher mode. */ if (grub_strcmp (ciphermode, "ecb") == 0) { - mode = GRUB_LUKS_MODE_ECB; - mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + mode = GRUB_CRYPTODISK_MODE_ECB; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; cipheriv = NULL; } else if (grub_strcmp (ciphermode, "plain") == 0) { - mode = GRUB_LUKS_MODE_CBC; - mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + mode = GRUB_CRYPTODISK_MODE_CBC; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; cipheriv = NULL; } else if (grub_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0) { - mode = GRUB_LUKS_MODE_CBC; + mode = GRUB_CRYPTODISK_MODE_CBC; cipheriv = ciphermode + sizeof ("cbc-") - 1; } else if (grub_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0) { - mode = GRUB_LUKS_MODE_PCBC; + mode = GRUB_CRYPTODISK_MODE_PCBC; cipheriv = ciphermode + sizeof ("pcbc-") - 1; } else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) { - mode = GRUB_LUKS_MODE_XTS; + mode = GRUB_CRYPTODISK_MODE_XTS; cipheriv = ciphermode + sizeof ("xts-") - 1; secondary_cipher = grub_crypto_cipher_open (ciph); if (!secondary_cipher) @@ -472,14 +175,14 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_crypto_cipher_close (cipher); return NULL; } - if (cipher->cipher->blocksize != GF_BYTES) + if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", cipher->cipher->blocksize); return NULL; } - if (secondary_cipher->cipher->blocksize != GF_BYTES) + if (secondary_cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", @@ -489,9 +192,9 @@ configure_ciphers (const struct grub_luks_phdr *header) } else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) { - mode = GRUB_LUKS_MODE_LRW; + mode = GRUB_CRYPTODISK_MODE_LRW; cipheriv = ciphermode + sizeof ("lrw-") - 1; - if (cipher->cipher->blocksize != GF_BYTES) + if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) { grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", @@ -509,9 +212,9 @@ configure_ciphers (const struct grub_luks_phdr *header) if (cipheriv == NULL); else if (grub_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0) - mode_iv = GRUB_LUKS_MODE_IV_PLAIN; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) - mode_iv = GRUB_LUKS_MODE_IV_PLAIN64; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64; else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) { if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1) @@ -521,15 +224,15 @@ configure_ciphers (const struct grub_luks_phdr *header) for (benbi_log = 0; (cipher->cipher->blocksize << benbi_log) < GRUB_DISK_SECTOR_SIZE; benbi_log++); - mode_iv = GRUB_LUKS_MODE_IV_BENBI; + mode_iv = GRUB_CRYPTODISK_MODE_IV_BENBI; } else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) - mode_iv = GRUB_LUKS_MODE_IV_NULL; + mode_iv = GRUB_CRYPTODISK_MODE_IV_NULL; else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) { char *hash_str = cipheriv + 6; - mode_iv = GRUB_LUKS_MODE_IV_ESSIV; + mode_iv = GRUB_CRYPTODISK_MODE_IV_ESSIV; /* Configure the hash and cipher used for ESSIV. */ essiv_hash = grub_crypto_lookup_md_by_name (hash_str); @@ -567,7 +270,7 @@ configure_ciphers (const struct grub_luks_phdr *header) return NULL; } - newdev = grub_zalloc (sizeof (struct grub_luks)); + newdev = grub_zalloc (sizeof (struct grub_cryptodisk)); if (!newdev) return NULL; newdev->cipher = cipher; @@ -580,72 +283,12 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->essiv_cipher = essiv_cipher; newdev->essiv_hash = essiv_hash; newdev->hash = hash; - newdev->id = n++; grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); return newdev; } -static gcry_err_code_t -luks_setkey (grub_luks_t dev, grub_uint8_t *key, grub_size_t keysize) -{ - gcry_err_code_t err; - int real_keysize; - - real_keysize = keysize; - if (dev->mode == GRUB_LUKS_MODE_XTS) - real_keysize /= 2; - if (dev->mode == GRUB_LUKS_MODE_LRW) - real_keysize -= dev->cipher->cipher->blocksize; - - /* Set the PBKDF2 output as the cipher key. */ - err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize); - if (err) - return err; - - /* Configure ESSIV if necessary. */ - if (dev->mode_iv == GRUB_LUKS_MODE_IV_ESSIV) - { - grub_size_t essiv_keysize = dev->essiv_hash->mdlen; - grub_uint8_t hashed_key[essiv_keysize]; - - grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize); - err = grub_crypto_cipher_set_key (dev->essiv_cipher, - hashed_key, essiv_keysize); - if (err) - return err; - } - if (dev->mode == GRUB_LUKS_MODE_XTS) - { - err = grub_crypto_cipher_set_key (dev->secondary_cipher, - key + real_keysize, - keysize / 2); - if (err) - return err; - } - - if (dev->mode == GRUB_LUKS_MODE_LRW) - { - int i; - grub_uint8_t idx[GF_BYTES]; - - grub_free (dev->lrw_precalc); - grub_memcpy (dev->lrw_key, key + real_keysize, - dev->cipher->cipher->blocksize); - dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE); - if (!dev->lrw_precalc) - return GPG_ERR_OUT_OF_MEMORY; - grub_memset (idx, 0, GF_BYTES); - for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i += GF_BYTES) - { - idx[GF_BYTES - 1] = i / GF_BYTES; - gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); - } - } - return GPG_ERR_NO_ERROR; -} - static grub_err_t -luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, +luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, const char *name, grub_disk_t source) { grub_size_t keysize = grub_be_to_cpu32 (header->keyBytes); @@ -700,7 +343,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_dprintf ("luks", "PBKDF2 done\n"); - gcry_err = luks_setkey (dev, digest, keysize); + gcry_err = grub_cryptodisk_setkey (dev, digest, keysize); if (gcry_err) { grub_free (split_key); @@ -721,7 +364,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, return err; } - gcry_err = luks_decrypt (dev, split_key, length, 0); + gcry_err = grub_cryptodisk_decrypt (dev, split_key, length, 0); if (gcry_err) { grub_free (split_key); @@ -766,7 +409,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = luks_setkey (dev, candidate_key, keysize); + gcry_err = grub_cryptodisk_setkey (dev, candidate_key, keysize); if (gcry_err) { grub_free (split_key); @@ -782,7 +425,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } static void -luks_close (grub_luks_t luks) +luks_close (grub_cryptodisk_t luks) { grub_crypto_cipher_close (luks->cipher); grub_crypto_cipher_close (luks->secondary_cipher); @@ -795,11 +438,12 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) { grub_err_t err; struct grub_luks_phdr header; - grub_luks_t newdev, dev; + grub_cryptodisk_t newdev, dev; - for (dev = luks_list; dev != NULL; dev = dev->next) - if (dev->source_id == source->id && dev->source_dev_id == source->dev->id) - return GRUB_ERR_NONE; + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + return GRUB_ERR_NONE; /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); @@ -817,17 +461,7 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) return err; } - newdev->source = grub_strdup (name); - if (!newdev->source) - { - grub_free (newdev); - return grub_errno; - } - - newdev->source_id = source->id; - newdev->source_dev_id = source->dev->id; - newdev->next = luks_list; - luks_list = newdev; + grub_cryptodisk_insert (newdev, name, source); have_it = 1; @@ -840,7 +474,7 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) { grub_err_t err; struct grub_luks_phdr header; - grub_luks_t newdev, dev; + grub_cryptodisk_t newdev, dev; grub_disk_t source; /* Try to open disk. */ @@ -848,12 +482,13 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) if (!source) return grub_errno; - for (dev = luks_list; dev != NULL; dev = dev->next) - if (dev->source_id == source->id && dev->source_dev_id == source->dev->id) - { - grub_disk_close (source); - return GRUB_ERR_NONE; - } + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } /* Read the LUKS header. */ err = grub_disk_read (source, 0, 0, sizeof (header), &header); @@ -861,25 +496,18 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) return err; newdev = configure_ciphers (&header); - grub_disk_close (source); if (!newdev) - return grub_errno; - - newdev->cheat = grub_strdup (cheat); - newdev->source = grub_strdup (sourcedev); - if (!newdev->source || !newdev->cheat) { - grub_free (newdev->source); - grub_free (newdev->cheat); - grub_free (newdev); + grub_disk_close (source); return grub_errno; } - newdev->cheat_fd = -1; - newdev->source_id = source->id; - newdev->source_dev_id = source->dev->id; - newdev->next = luks_list; - luks_list = newdev; - return GRUB_ERR_NONE; + + err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat); + grub_disk_close (source); + if (err) + grub_free (newdev); + + return err; } #endif @@ -903,208 +531,16 @@ grub_luks_scan_device (const char *name) return have_it && check_uuid ? 0 : 1; } -static int -grub_luks_iterate (int (*hook) (const char *name), - grub_disk_pull_t pull) -{ - grub_luks_t i; - - if (pull != GRUB_DISK_PULL_NONE) - return 0; - - for (i = luks_list; i != NULL; i = i->next) - { - char buf[30]; - grub_snprintf (buf, sizeof (buf), "luks%lu", i->id); - if (hook (buf)) - return 1; - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_luks_open (const char *name, grub_disk_t disk, - grub_disk_pull_t pull __attribute__ ((unused))) -{ - grub_luks_t dev; - - if (grub_memcmp (name, "luks", sizeof ("luks") - 1) != 0) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); - - if (grub_memcmp (name, "luksuuid/", sizeof ("luksuuid/") - 1) == 0) - { - for (dev = luks_list; dev != NULL; dev = dev->next) - if (grub_strcasecmp (name + sizeof ("luksuuid/") - 1, dev->uuid) == 0) - break; - } - else - { - unsigned long id = grub_strtoul (name + sizeof ("luks") - 1, 0, 0); - if (grub_errno) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); - /* Search for requested device in the list of LUKS devices. */ - for (dev = luks_list; dev != NULL; dev = dev->next) - if (dev->id == id) - break; - } - if (!dev) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); - #ifdef GRUB_UTIL - if (dev->cheat) - { - if (dev->cheat_fd == -1) - dev->cheat_fd = open (dev->cheat, O_RDONLY); - if (dev->cheat_fd == -1) - return grub_error (GRUB_ERR_IO, "couldn't open %s: %s", - dev->cheat, strerror (errno)); - } -#endif - - if (!dev->source_disk) - { - grub_dprintf ("luks", "Opening device %s\n", name); - /* Try to open the source disk and populate the requested disk. */ - dev->source_disk = grub_disk_open (dev->source); - if (!dev->source_disk) - return grub_errno; - } - - disk->data = dev; - disk->total_sectors = grub_disk_get_size (dev->source_disk) - dev->offset; - disk->id = dev->id; - dev->ref++; - return GRUB_ERR_NONE; -} - -static void -grub_luks_close (grub_disk_t disk) -{ - grub_luks_t dev = (grub_luks_t) disk->data; - grub_dprintf ("luks", "Closing disk\n"); - - dev->ref--; - - if (dev->ref != 0) - return; -#ifdef GRUB_UTIL - if (dev->cheat) - { - close (dev->cheat_fd); - dev->cheat_fd = -1; - } -#endif - grub_disk_close (dev->source_disk); - dev->source_disk = NULL; -} - -static grub_err_t -grub_luks_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_luks_t dev = (grub_luks_t) disk->data; - grub_err_t err; - -#ifdef GRUB_UTIL - if (dev->cheat) - { - err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector); - if (err) - return err; - if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS) - != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) - return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", - dev->cheat); - return GRUB_ERR_NONE; - } -#endif - - grub_dprintf ("luks", - "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" - PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT32_T "\n", - size, sector, dev->offset); - - err = grub_disk_read (dev->source_disk, sector + dev->offset, 0, - size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - { - grub_dprintf ("luks", "grub_disk_read failed with error %d\n", err); - return err; - } - return grub_crypto_gcry_error (luks_decrypt (dev, (grub_uint8_t *) buf, - size << GRUB_DISK_SECTOR_BITS, - sector)); -} - -static grub_err_t -grub_luks_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; -} - -#ifdef GRUB_UTIL -static grub_disk_memberlist_t -grub_luks_memberlist (grub_disk_t disk) -{ - grub_luks_t dev = (grub_luks_t) disk->data; - grub_disk_memberlist_t list = NULL; - - list = grub_malloc (sizeof (*list)); - if (list) - { - list->disk = dev->source_disk; - list->next = NULL; - } - - return list; -} - -void -grub_util_luks_print_ciphers (grub_disk_t disk) -{ - grub_luks_t dev = (grub_luks_t) disk->data; - if (dev->cipher) - grub_printf ("%s ", dev->cipher->cipher->modname); - if (dev->secondary_cipher) - grub_printf ("%s ", dev->secondary_cipher->cipher->modname); - if (dev->essiv_cipher) - grub_printf ("%s ", dev->essiv_cipher->cipher->modname); - if (dev->hash) - grub_printf ("%s ", dev->hash->modname); - if (dev->essiv_hash) - grub_printf ("%s ", dev->essiv_hash->modname); -} void grub_util_luks_print_uuid (grub_disk_t disk) { - grub_luks_t dev = (grub_luks_t) disk->data; + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; grub_printf ("%s ", dev->uuid); } #endif -static void -luks_cleanup (void) -{ - grub_luks_t dev = luks_list; - grub_luks_t tmp; - - while (dev != NULL) - { - grub_free (dev->source); - grub_free (dev->cipher); - grub_free (dev->secondary_cipher); - grub_free (dev->essiv_cipher); - tmp = dev->next; - grub_free (dev); - dev = tmp; - } -} - static grub_err_t grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) { @@ -1116,14 +552,14 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) have_it = 0; if (state[0].set) { - grub_luks_t dev; + grub_cryptodisk_t dev; - for (dev = luks_list; dev != NULL; dev = dev->next) - if (grub_strcasecmp (dev->uuid, args[0]) == 0) - { - grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); - return GRUB_ERR_NONE; - } + dev = grub_cryptodisk_get_by_uuid (args[0]); + if (dev) + { + grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id); + return GRUB_ERR_NONE; + } check_uuid = 1; search_uuid = args[0]; @@ -1146,7 +582,7 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) { grub_err_t err; grub_disk_t disk; - grub_luks_t dev; + grub_cryptodisk_t dev; check_uuid = 0; search_uuid = NULL; @@ -1154,13 +590,13 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) if (!disk) return grub_errno; - for (dev = luks_list; dev != NULL; dev = dev->next) - if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) - { - grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); - grub_disk_close (disk); - return GRUB_ERR_NONE; - } + dev = grub_cryptodisk_get_by_source_disk (disk); + if (dev) + { + grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); + grub_disk_close (disk); + return GRUB_ERR_NONE; + } err = grub_luks_scan_device_real (args[0], disk); @@ -1170,33 +606,18 @@ grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) } } -static struct grub_disk_dev grub_luks_dev = { - .name = "luks", - .id = GRUB_DISK_DEVICE_LUKS_ID, - .iterate = grub_luks_iterate, - .open = grub_luks_open, - .close = grub_luks_close, - .read = grub_luks_read, - .write = grub_luks_write, -#ifdef GRUB_UTIL - .memberlist = grub_luks_memberlist, -#endif - .next = 0 -}; - static grub_extcmd_t cmd; GRUB_MOD_INIT (luks) { + COMPILE_TIME_ASSERT (sizeof (((struct grub_luks_phdr *) 0)->uuid) + < GRUB_CRYPTODISK_MAX_UUID_LENGTH); cmd = grub_register_extcmd ("luksmount", grub_cmd_luksmount, 0, N_("SOURCE|-u UUID|-a"), N_("Mount a LUKS device."), options); - grub_disk_dev_register (&grub_luks_dev); } GRUB_MOD_FINI (luks) { grub_unregister_extcmd (cmd); - grub_disk_dev_unregister (&grub_luks_dev); - luks_cleanup (); } diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index c3a971689..642d77cf0 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -962,7 +962,7 @@ grub_util_get_grub_dev (const char *os_dev) dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-'); if (dash) *dash = 0; - grub_dev = grub_xasprintf ("luksuuid/%s", + grub_dev = grub_xasprintf ("cryptouuid/%s", uuid + sizeof ("CRYPT-LUKS1-") - 1); grub_free (uuid); } diff --git a/include/grub/disk.h b/include/grub/disk.h index 2678ad5eb..52f3e61b0 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -42,7 +42,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_PXE_ID, GRUB_DISK_DEVICE_SCSI_ID, GRUB_DISK_DEVICE_FILE_ID, - GRUB_DISK_DEVICE_LUKS_ID + GRUB_DISK_DEVICE_CRYPTODISK_ID }; struct grub_disk; diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index 7541308e6..c1350a370 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -36,7 +36,6 @@ grub_util_fd_sector_seek (int fd, const char *name, grub_disk_addr_t sector); ssize_t grub_util_fd_read (int fd, char *buf, size_t len); grub_err_t grub_luks_cheat_mount (const char *sourcedev, const char *cheat); -void grub_util_luks_print_ciphers (grub_disk_t disk); void grub_util_luks_print_uuid (grub_disk_t disk); #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/util/grub-probe.c b/util/grub-probe.c index 21fc21b02..f5d93ac4e 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -106,7 +107,8 @@ probe_luks_uuid (grub_disk_t disk) free (list); list = tmp; } - if (disk->dev->id == GRUB_DISK_DEVICE_LUKS_ID) + /* FIXME: support non-LUKS. */ + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) grub_util_luks_print_uuid (disk); } @@ -144,11 +146,8 @@ probe_abstraction (grub_disk_t disk) if (disk->dev->id == GRUB_DISK_DEVICE_LVM_ID) printf ("lvm "); - if (disk->dev->id == GRUB_DISK_DEVICE_LUKS_ID) - { - printf ("luks "); - grub_util_luks_print_ciphers (disk); - } + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + grub_util_cryptodisk_print_abstraction (disk); raid_level = probe_raid_level (disk); if (raid_level >= 0) From 1a1f408f206f2e29b40b172b0e9857f0137d91a6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 00:00:29 +0200 Subject: [PATCH 27/43] geli support --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 + grub-core/disk/cryptodisk.c | 643 ++++++++++++++++++++++++++++++++++++ grub-core/disk/geli.c | 515 +++++++++++++++++++++++++++++ grub-core/disk/luks.c | 4 + grub-core/lib/crypto.c | 10 +- include/grub/crypto.h | 4 +- include/grub/cryptodisk.h | 99 ++++++ util/grub-fstest.c | 8 +- 9 files changed, 1281 insertions(+), 8 deletions(-) create mode 100644 grub-core/disk/cryptodisk.c create mode 100644 grub-core/disk/geli.c create mode 100644 include/grub/cryptodisk.h diff --git a/Makefile.util.def b/Makefile.util.def index ffa7a794f..fe9e8c036 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -23,6 +23,7 @@ library = { common = grub-core/kern/partition.c; common = grub-core/lib/crypto.c; common = grub-core/disk/luks.c; + common = grub-core/disk/geli.c; common = grub-core/disk/cryptodisk.c; common = grub-core/disk/AFSplitter.c; common = grub-core/lib/pbkdf2.c; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index c55fff13a..52add9f1b 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -779,6 +779,11 @@ module = { common = disk/AFSplitter.c; }; +module = { + name = geli; + common = disk/geli.c; +}; + module = { name = lvm; common = disk/lvm.c; diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c new file mode 100644 index 000000000..7f3b60bf5 --- /dev/null +++ b/grub-core/disk/cryptodisk.c @@ -0,0 +1,643 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011 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 + +#ifdef GRUB_UTIL +#include +#include +#include +#include +#include +#include +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ +#define GF_POLYNOM 0x87 +#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GRUB_CRYPTODISK_GF_BYTES) + +static grub_cryptodisk_t cryptodisk_list = NULL; +static grub_uint8_t n = 0; + +static void +gf_mul_x (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[0] ^= GF_POLYNOM; +} + + +static void +gf_mul_x_be (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[GRUB_CRYPTODISK_GF_BYTES - 1] ^= GF_POLYNOM; +} + +static void +gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) +{ + int i; + grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES]; + grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES); + grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES); + for (i = 0; i < GRUB_CRYPTODISK_GF_SIZE; i++) + { + if (((a[GRUB_CRYPTODISK_GF_BYTES - i / 8 - 1] >> (i % 8))) & 1) + grub_crypto_xor (o, o, t, GRUB_CRYPTODISK_GF_BYTES); + gf_mul_x_be (t); + } +} + +static gcry_err_code_t +grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv) +{ + grub_uint8_t *inptr, *outptr, *end; + grub_uint8_t ivt[cipher->cipher->blocksize]; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + { + grub_memcpy (ivt, inptr, cipher->cipher->blocksize); + cipher->cipher->decrypt (cipher->ctx, outptr, inptr); + grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize); + grub_crypto_xor (iv, ivt, outptr, cipher->cipher->blocksize); + } + return GPG_ERR_NO_ERROR; +} + +struct lrw_sector +{ + grub_uint8_t low[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint8_t high[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint8_t low_byte, low_byte_c; +}; + +static void +generate_lrw_sector (struct lrw_sector *sec, + const struct grub_cryptodisk *dev, + const grub_uint8_t *iv) +{ + grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint16_t c; + int j; + grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES); + sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] & (GF_PER_SECTOR - 1)); + sec->low_byte_c = (((GF_PER_SECTOR - 1) & ~sec->low_byte) + 1); + idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR - 1); + gf_mul_be (sec->low, dev->lrw_key, idx); + if (!sec->low_byte) + return; + + c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR; + if (c & 0x100) + { + for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--) + { + idx[j]++; + if (idx[j] != 0) + break; + } + } + idx[GRUB_CRYPTODISK_GF_BYTES - 1] = c; + gf_mul_be (sec->high, dev->lrw_key, idx); +} + +static void __attribute__ ((unused)) +lrw_xor (const struct lrw_sector *sec, + const struct grub_cryptodisk *dev, + grub_uint8_t *b) +{ + int i; + + for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; i += GRUB_CRYPTODISK_GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES); + grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte, + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES); + if (!sec->low_byte) + return; + + for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; + i < GRUB_DISK_SECTOR_SIZE; i += GRUB_CRYPTODISK_GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES); + grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, + b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, + dev->lrw_precalc, sec->low_byte * GRUB_CRYPTODISK_GF_BYTES); +} + +gcry_err_code_t +grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, + grub_uint8_t * data, grub_size_t len, + grub_disk_addr_t sector) +{ + grub_size_t i; + gcry_err_code_t err; + + /* The only mode without IV. */ + if (dev->mode == GRUB_CRYPTODISK_MODE_ECB) + return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); + + for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + { + grub_size_t sz = ((dev->cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)); + grub_uint32_t iv[sz]; + + grub_memset (iv, 0, sz * sizeof (iv[0])); + switch (dev->mode_iv) + { + case GRUB_CRYPTODISK_MODE_IV_NULL: + break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH: + { + grub_uint64_t tmp; + grub_uint64_t ctx[(dev->iv_hash->contextsize + 7) / 8]; + tmp = grub_cpu_to_le64 (sector << GRUB_DISK_SECTOR_BITS); + dev->iv_hash->init (ctx); + dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len); + dev->iv_hash->write (ctx, &tmp, sizeof (tmp)); + dev->iv_hash->final (ctx); + + grub_memcpy (iv, dev->iv_hash->read (ctx), sizeof (iv)); + } + break; + case GRUB_CRYPTODISK_MODE_IV_PLAIN64: + iv[1] = grub_cpu_to_le32 (sector >> 32); + case GRUB_CRYPTODISK_MODE_IV_PLAIN: + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + break; + case GRUB_CRYPTODISK_MODE_IV_BENBI: + { + grub_uint64_t num = (sector << dev->benbi_log) + 1; + iv[sz - 2] = grub_cpu_to_be32 (num >> 32); + iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF); + } + break; + case GRUB_CRYPTODISK_MODE_IV_ESSIV: + iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); + err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + } + + switch (dev->mode) + { + case GRUB_CRYPTODISK_MODE_CBC: + err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, + GRUB_DISK_SECTOR_SIZE, iv); + if (err) + return err; + break; + + case GRUB_CRYPTODISK_MODE_PCBC: + err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, + GRUB_DISK_SECTOR_SIZE, iv); + if (err) + return err; + break; + case GRUB_CRYPTODISK_MODE_XTS: + { + int j; + err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + + for (j = 0; j < GRUB_DISK_SECTOR_SIZE; + j += dev->cipher->cipher->blocksize) + { + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + gf_mul_x ((grub_uint8_t *) iv); + } + } + break; + case GRUB_CRYPTODISK_MODE_LRW: + { + struct lrw_sector sec; + + generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv); + lrw_xor (&sec, dev, data + i); + + err = grub_crypto_ecb_decrypt (dev->cipher, data + i, + data + i, GRUB_DISK_SECTOR_SIZE); + if (err) + return err; + lrw_xor (&sec, dev, data + i); + } + break; + default: + return GPG_ERR_NOT_IMPLEMENTED; + } + sector++; + } + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize) +{ + gcry_err_code_t err; + int real_keysize; + + real_keysize = keysize; + if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) + real_keysize /= 2; + if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) + real_keysize -= dev->cipher->cipher->blocksize; + + /* Set the PBKDF2 output as the cipher key. */ + err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize); + if (err) + return err; + + /* Configure ESSIV if necessary. */ + if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_ESSIV) + { + grub_size_t essiv_keysize = dev->essiv_hash->mdlen; + grub_uint8_t hashed_key[essiv_keysize]; + + grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize); + err = grub_crypto_cipher_set_key (dev->essiv_cipher, + hashed_key, essiv_keysize); + if (err) + return err; + } + if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) + { + err = grub_crypto_cipher_set_key (dev->secondary_cipher, + key + real_keysize, + keysize / 2); + if (err) + return err; + } + + if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) + { + int i; + grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; + + grub_free (dev->lrw_precalc); + grub_memcpy (dev->lrw_key, key + real_keysize, + dev->cipher->cipher->blocksize); + dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (!dev->lrw_precalc) + return GPG_ERR_OUT_OF_MEMORY; + grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES); + for (i = 0; i < GRUB_DISK_SECTOR_SIZE; + i += GRUB_CRYPTODISK_GF_BYTES) + { + idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES; + gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); + } + } + return GPG_ERR_NO_ERROR; +} + +static int +grub_cryptodisk_iterate (int (*hook) (const char *name), + grub_disk_pull_t pull) +{ + grub_cryptodisk_t i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (i = cryptodisk_list; i != NULL; i = i->next) + { + char buf[30]; + grub_snprintf (buf, sizeof (buf), "crypto%lu", i->id); + if (hook (buf)) + return 1; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cryptodisk_open (const char *name, grub_disk_t disk, + grub_disk_pull_t pull __attribute__ ((unused))) +{ + grub_cryptodisk_t dev; + + if (grub_memcmp (name, "crypto", sizeof ("crypto") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + + if (grub_memcmp (name, "cryptouuid/", sizeof ("cryptouuid/") - 1) == 0) + { + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (grub_strcasecmp (name + sizeof ("cryptouuid/") - 1, dev->uuid) == 0) + break; + } + else + { + unsigned long id = grub_strtoul (name + sizeof ("crypto") - 1, 0, 0); + if (grub_errno) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + /* Search for requested device in the list of CRYPTODISK devices. */ + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (dev->id == id) + break; + } + if (!dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + +#ifdef GRUB_UTIL + if (dev->cheat) + { + if (dev->cheat_fd == -1) + dev->cheat_fd = open (dev->cheat, O_RDONLY); + if (dev->cheat_fd == -1) + return grub_error (GRUB_ERR_IO, "couldn't open %s: %s", + dev->cheat, strerror (errno)); + } +#endif + + if (!dev->source_disk) + { + grub_dprintf ("cryptodisk", "Opening device %s\n", name); + /* Try to open the source disk and populate the requested disk. */ + dev->source_disk = grub_disk_open (dev->source); + if (!dev->source_disk) + return grub_errno; + } + + disk->data = dev; + disk->total_sectors = dev->total_length; + disk->id = dev->id; + dev->ref++; + return GRUB_ERR_NONE; +} + +static void +grub_cryptodisk_close (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_dprintf ("cryptodisk", "Closing disk\n"); + + dev->ref--; + + if (dev->ref != 0) + return; +#ifdef GRUB_UTIL + if (dev->cheat) + { + close (dev->cheat_fd); + dev->cheat_fd = -1; + } +#endif + grub_disk_close (dev->source_disk); + dev->source_disk = NULL; +} + +static grub_err_t +grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_err_t err; + gcry_err_code_t gcry_err; + +#ifdef GRUB_UTIL + if (dev->cheat) + { + err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector); + if (err) + return err; + if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS) + != (ssize_t) (size << GRUB_DISK_SECTOR_BITS)) + return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", + dev->cheat); + return GRUB_ERR_NONE; + } +#endif + + grub_dprintf ("cryptodisk", + "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" + PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", + size, sector, dev->offset); + + err = grub_disk_read (dev->source_disk, sector + dev->offset, 0, + size << GRUB_DISK_SECTOR_BITS, buf); + if (err) + { + grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err); + return err; + } + gcry_err = grub_cryptodisk_decrypt (dev, (grub_uint8_t *) buf, + size << GRUB_DISK_SECTOR_BITS, + sector); + return grub_crypto_gcry_error (gcry_err); +} + +static grub_err_t +grub_cryptodisk_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; +} + +#ifdef GRUB_UTIL +static grub_disk_memberlist_t +grub_cryptodisk_memberlist (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_disk_memberlist_t list = NULL; + + list = grub_malloc (sizeof (*list)); + if (list) + { + list->disk = dev->source_disk; + list->next = NULL; + } + + return list; +} +#endif + +static void +cryptodisk_cleanup (void) +{ + grub_cryptodisk_t dev = cryptodisk_list; + grub_cryptodisk_t tmp; + + while (dev != NULL) + { + grub_free (dev->source); + grub_free (dev->cipher); + grub_free (dev->secondary_cipher); + grub_free (dev->essiv_cipher); + tmp = dev->next; + grub_free (dev); + dev = tmp; + } +} + +grub_err_t +grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source) +{ + newdev->source = grub_strdup (name); + if (!newdev->source) + { + grub_free (newdev); + return grub_errno; + } + + newdev->id = n++; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->next = cryptodisk_list; + cryptodisk_list = newdev; + + return GRUB_ERR_NONE; +} + +grub_cryptodisk_t +grub_cryptodisk_get_by_uuid (const char *uuid) +{ + grub_cryptodisk_t dev; + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (grub_strcasecmp (dev->uuid, uuid) == 0) + return dev; + return NULL; +} + +grub_cryptodisk_t +grub_cryptodisk_get_by_source_disk (grub_disk_t disk) +{ + grub_cryptodisk_t dev; + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) + return dev; + return NULL; +} + +#ifdef GRUB_UTIL +grub_err_t +grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source, const char *cheat) +{ + newdev->cheat = grub_strdup (cheat); + newdev->source = grub_strdup (name); + if (!newdev->source || !newdev->cheat) + { + grub_free (newdev->source); + grub_free (newdev->cheat); + return grub_errno; + } + + newdev->cheat_fd = -1; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->id = n++; + newdev->next = cryptodisk_list; + cryptodisk_list = newdev; + + return GRUB_ERR_NONE; +} + +void +grub_util_cryptodisk_print_abstraction (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + + grub_printf ("luks "); + + if (dev->cipher) + grub_printf ("%s ", dev->cipher->cipher->modname); + if (dev->secondary_cipher) + grub_printf ("%s ", dev->secondary_cipher->cipher->modname); + if (dev->essiv_cipher) + grub_printf ("%s ", dev->essiv_cipher->cipher->modname); + if (dev->hash) + grub_printf ("%s ", dev->hash->modname); + if (dev->essiv_hash) + grub_printf ("%s ", dev->essiv_hash->modname); + if (dev->iv_hash) + grub_printf ("%s ", dev->iv_hash->modname); +} +#endif + +static struct grub_disk_dev grub_cryptodisk_dev = { + .name = "cryptodisk", + .id = GRUB_DISK_DEVICE_CRYPTODISK_ID, + .iterate = grub_cryptodisk_iterate, + .open = grub_cryptodisk_open, + .close = grub_cryptodisk_close, + .read = grub_cryptodisk_read, + .write = grub_cryptodisk_write, +#ifdef GRUB_UTIL + .memberlist = grub_cryptodisk_memberlist, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (cryptodisk) +{ + grub_disk_dev_register (&grub_cryptodisk_dev); +} + +GRUB_MOD_FINI (cryptodisk) +{ + grub_disk_dev_unregister (&grub_cryptodisk_dev); + cryptodisk_cleanup (); +} diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c new file mode 100644 index 000000000..6af1de766 --- /dev/null +++ b/grub-core/disk/geli.c @@ -0,0 +1,515 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011 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 . + */ + +/* This file is loosely based on FreeBSD geli implementation + (but no code was directly copied). FreeBSD geli is distributed under + following terms: */ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_geli_key +{ + grub_uint8_t iv_key[64]; + grub_uint8_t cipher_key[64]; + grub_uint8_t hmac[64]; +} __attribute__ ((packed)); + +struct grub_geli_phdr +{ + grub_uint8_t magic[16]; +#define GELI_MAGIC "GEOM::ELI" + grub_uint32_t version; + grub_uint32_t unused1; + grub_uint16_t alg; + grub_uint16_t keylen; + grub_uint16_t unused3[7]; + grub_uint8_t keys_used; + grub_uint32_t niter; + grub_uint8_t salt[64]; + struct grub_geli_key keys[2]; +} __attribute__ ((packed)); + +const char *algorithms[] = { + [0x0b] = "aes", +}; + +#define MAX_PASSPHRASE 256 + +static const struct grub_arg_option options[] = + { + {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, + {"all", 'a', 0, N_("Mount all."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +static int check_uuid, have_it; +static char *search_uuid; + +static grub_cryptodisk_t +configure_ciphers (const struct grub_geli_phdr *header) +{ + grub_cryptodisk_t newdev; + grub_crypto_cipher_handle_t cipher = NULL; + const struct gcry_cipher_spec *ciph; + const char *ciphername = NULL; + const gcry_md_spec_t *hash = NULL, *iv_hash = NULL; + + /* Look for GELI magic sequence. */ + if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) + || grub_le_to_cpu32 (header->version) != 3) + { + grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); + return NULL; + } + +#if 0 + optr = uuid; + for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)]; + iptr++) + { + if (*iptr != '-') + *optr++ = *iptr; + } + *optr = 0; + + if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) + { + grub_dprintf ("luks", "%s != %s", uuid, search_uuid); + return NULL; + } +#endif + + if (grub_le_to_cpu16 (header->alg) >= ARRAY_SIZE (algorithms) + || algorithms[grub_le_to_cpu16 (header->alg)] == NULL) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown", + grub_le_to_cpu16 (header->alg)); + return NULL; + } + + ciphername = algorithms[grub_le_to_cpu16 (header->alg)]; + ciph = grub_crypto_lookup_cipher_by_name (ciphername); + if (!ciph) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + ciphername); + return NULL; + } + + /* Configure the cipher used for the bulk data. */ + cipher = grub_crypto_cipher_open (ciph); + if (!cipher) + return NULL; + + if (grub_le_to_cpu16 (header->keylen) > 1024) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", + grub_le_to_cpu16 (header->keylen)); + return NULL; + } + + hash = grub_crypto_lookup_md_by_name ("sha512"); + if (!hash) + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + "sha512"); + return NULL; + } + + iv_hash = grub_crypto_lookup_md_by_name ("sha256"); + if (!hash) + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + "sha512"); + return NULL; + } + + newdev = grub_zalloc (sizeof (struct grub_cryptodisk)); + if (!newdev) + return NULL; + newdev->cipher = cipher; + newdev->offset = 0; + newdev->source_disk = NULL; + newdev->benbi_log = 0; + newdev->mode = GRUB_CRYPTODISK_MODE_CBC; + newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH; + newdev->secondary_cipher = NULL; + newdev->essiv_cipher = NULL; + newdev->essiv_hash = NULL; + newdev->hash = hash; + newdev->iv_hash = iv_hash; +#if 0 + grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); +#endif + return newdev; +} + +static grub_err_t +recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, + const char *name, grub_disk_t source __attribute__ ((unused))) +{ + grub_size_t keysize = grub_le_to_cpu16 (header->keylen) / 8; + grub_uint8_t digest[dev->hash->mdlen]; + grub_uint8_t geomkey[dev->hash->mdlen]; + grub_uint8_t verify_key[dev->hash->mdlen]; + grub_uint8_t pbkdf_key[64]; + grub_uint8_t zero[dev->cipher->cipher->blocksize]; + char passphrase[MAX_PASSPHRASE] = ""; + unsigned i; + gcry_err_code_t gcry_err; + + grub_memset (zero, 0, sizeof (zero)); + + grub_printf ("Attempting to decrypt master key...\n"); + + /* Get the passphrase from the user. */ + grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid); + if (!grub_password_get (passphrase, MAX_PASSPHRASE)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + + /* Calculate the PBKDF2 of the user supplied passphrase. */ + gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, + grub_strlen (passphrase), + header->salt, + sizeof (header->salt), + grub_le_to_cpu32 (header->niter), + pbkdf_key, sizeof (pbkdf_key)); + + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key, + sizeof (pbkdf_key), geomkey); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, + sizeof (geomkey), "\1", 1, digest); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, + sizeof (geomkey), "\0", 1, verify_key); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize); + + /* Try to recover master key from each active keyslot. */ + for (i = 0; i < ARRAY_SIZE (header->keys); i++) + { + struct grub_geli_key candidate_key; + grub_uint8_t key_hmac[dev->hash->mdlen]; + + /* Check if keyslot is enabled. */ + if (! (header->keys_used & (1 << i))) + continue; + + grub_dprintf ("geli", "Trying keyslot %d\n", i); + + gcry_err = grub_crypto_cipher_set_key (dev->cipher, + digest, keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key, + &header->keys[i], + sizeof (candidate_key), + zero); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key, + sizeof (verify_key), + &candidate_key, + (sizeof (candidate_key) + - sizeof (candidate_key.hmac)), + key_hmac); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0) + continue; + grub_printf ("Slot %d opened\n", i); + + /* Set the master key. */ + gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, + keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + dev->iv_prefix_len = sizeof (candidate_key.iv_key); + grub_memcpy (dev->iv_prefix, candidate_key.iv_key, + sizeof (candidate_key.iv_key)); + + COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key)); + + return GRUB_ERR_NONE; + } + + return GRUB_ACCESS_DENIED; +} + +static void +close (grub_cryptodisk_t luks) +{ + grub_crypto_cipher_close (luks->cipher); + grub_crypto_cipher_close (luks->secondary_cipher); + grub_crypto_cipher_close (luks->essiv_cipher); + grub_free (luks); +} + +static grub_err_t +grub_geli_scan_device_real (const char *name, grub_disk_t source) +{ + grub_err_t err; + struct grub_geli_phdr header; + grub_cryptodisk_t newdev, dev; + grub_disk_addr_t sector; + + grub_dprintf ("geli", "scanning %s\n", source->name); + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + return GRUB_ERR_NONE; + + sector = grub_disk_get_size (source); + if (sector == GRUB_DISK_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli"); + + /* Read the LUKS header. */ + err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); + if (err) + return err; + + newdev = configure_ciphers (&header); + if (!newdev) + return grub_errno; + + newdev->total_length = grub_disk_get_size (source) - 1; + + err = recover_key (newdev, &header, name, source); + if (err) + { + close (newdev); + return err; + } + + grub_cryptodisk_insert (newdev, name, source); + + have_it = 1; + + return GRUB_ERR_NONE; +} + +#ifdef GRUB_UTIL +grub_err_t +grub_geli_cheat_mount (const char *sourcedev, const char *cheat) +{ + grub_err_t err; + struct grub_geli_phdr header; + grub_cryptodisk_t newdev, dev; + grub_disk_t source; + grub_disk_addr_t sector; + + /* Try to open disk. */ + source = grub_disk_open (sourcedev); + if (!source) + return grub_errno; + + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } + + sector = grub_disk_get_size (source); + if (sector == GRUB_DISK_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli"); + + /* Read the LUKS header. */ + err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); + if (err) + return err; + + newdev = configure_ciphers (&header); + if (!newdev) + { + grub_disk_close (source); + return grub_errno; + } + + newdev->total_length = grub_disk_get_size (source) - 1; + + err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat); + grub_disk_close (source); + if (err) + grub_free (newdev); + + return err; +} +#endif + +static int +grub_geli_scan_device (const char *name) +{ + grub_err_t err; + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (name); + if (!source) + return grub_errno; + + err = grub_geli_scan_device_real (name, source); + + grub_disk_close (source); + + if (err) + grub_print_error (); + return have_it && check_uuid ? 0 : 1; +} + +#ifdef GRUB_UTIL + +void +grub_util_geli_print_uuid (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_printf ("%s ", dev->uuid); +} +#endif + +static grub_err_t +grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + + if (argc < 1 && !state[1].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + have_it = 0; + if (state[0].set) + { + grub_cryptodisk_t dev; + + dev = grub_cryptodisk_get_by_uuid (args[0]); + if (dev) + { + grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id); + return GRUB_ERR_NONE; + } + + check_uuid = 1; + search_uuid = args[0]; + grub_device_iterate (&grub_geli_scan_device); + search_uuid = NULL; + + if (!have_it) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); + return GRUB_ERR_NONE; + } + else if (state[1].set) + { + check_uuid = 0; + search_uuid = NULL; + grub_device_iterate (&grub_geli_scan_device); + search_uuid = NULL; + return GRUB_ERR_NONE; + } + else + { + grub_err_t err; + grub_disk_t disk; + grub_cryptodisk_t dev; + + check_uuid = 0; + search_uuid = NULL; + disk = grub_disk_open (args[0]); + if (!disk) + return grub_errno; + + dev = grub_cryptodisk_get_by_source_disk (disk); + if (dev) + { + grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); + grub_disk_close (disk); + return GRUB_ERR_NONE; + } + + err = grub_geli_scan_device_real (args[0], disk); + + grub_disk_close (disk); + + return err; + } +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT (geli) +{ + cmd = grub_register_extcmd ("gelimount", grub_cmd_gelimount, 0, + N_("SOURCE|-u UUID|-a"), + N_("Mount a GELI device."), options); +} + +GRUB_MOD_FINI (geli) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 361beb756..0763da011 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -454,6 +454,8 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source) if (!newdev) return grub_errno; + newdev->total_length = grub_disk_get_size (source) - newdev->offset; + err = luks_recover_key (newdev, &header, name, source); if (err) { @@ -502,6 +504,8 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat) return grub_errno; } + newdev->total_length = grub_disk_get_size (source) - newdev->offset; + err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat); grub_disk_close (source); if (err) diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 2f172ebf8..e6f55062b 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -193,9 +193,10 @@ grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size) gcry_err_code_t grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher, - void *out, void *in, grub_size_t size) + void *out, const void *in, grub_size_t size) { - grub_uint8_t *inptr, *outptr, *end; + const grub_uint8_t *inptr; + grub_uint8_t *outptr, *end; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; if (size % cipher->cipher->blocksize != 0) @@ -249,10 +250,11 @@ grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher, gcry_err_code_t grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher, - void *out, void *in, grub_size_t size, + void *out, const void *in, grub_size_t size, void *iv) { - grub_uint8_t *inptr, *outptr, *end; + const grub_uint8_t *inptr; + grub_uint8_t *outptr, *end; grub_uint8_t ivt[cipher->cipher->blocksize]; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 62ed2015c..ab82da862 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -199,7 +199,7 @@ grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size); gcry_err_code_t grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher, - void *out, void *in, grub_size_t size); + void *out, const void *in, grub_size_t size); gcry_err_code_t grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher, @@ -210,7 +210,7 @@ grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher, void *iv_in); gcry_err_code_t grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher, - void *out, void *in, grub_size_t size, + void *out, const void *in, grub_size_t size, void *iv); void grub_cipher_register (gcry_cipher_spec_t *cipher); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h new file mode 100644 index 000000000..284e599f2 --- /dev/null +++ b/include/grub/cryptodisk.h @@ -0,0 +1,99 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 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 . + */ + +#ifndef GRUB_CRYPTODISK_HEADER +#define GRUB_CRYPTODISK_HEADER 1 + +#include +#include + +typedef enum + { + GRUB_CRYPTODISK_MODE_ECB, + GRUB_CRYPTODISK_MODE_CBC, + GRUB_CRYPTODISK_MODE_PCBC, + GRUB_CRYPTODISK_MODE_XTS, + GRUB_CRYPTODISK_MODE_LRW + } grub_cryptodisk_mode_t; + +typedef enum + { + GRUB_CRYPTODISK_MODE_IV_NULL, + GRUB_CRYPTODISK_MODE_IV_PLAIN, + GRUB_CRYPTODISK_MODE_IV_PLAIN64, + GRUB_CRYPTODISK_MODE_IV_ESSIV, + GRUB_CRYPTODISK_MODE_IV_BENBI, + GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH + } grub_cryptodisk_mode_iv_t; + +#define GRUB_CRYPTODISK_MAX_UUID_LENGTH 63 + +#define GRUB_CRYPTODISK_GF_SIZE 128 +#define GRUB_CRYPTODISK_GF_BYTES (GRUB_CRYPTODISK_GF_SIZE / 8) + +struct grub_cryptodisk +{ + char *source; + grub_disk_addr_t offset; + grub_disk_addr_t total_length; + grub_disk_t source_disk; + int ref; + grub_crypto_cipher_handle_t cipher; + grub_crypto_cipher_handle_t secondary_cipher; + grub_crypto_cipher_handle_t essiv_cipher; + const gcry_md_spec_t *essiv_hash, *hash, *iv_hash; + grub_cryptodisk_mode_t mode; + grub_cryptodisk_mode_iv_t mode_iv; + int benbi_log; + unsigned long id, source_id; + enum grub_disk_dev_id source_dev_id; + char uuid[GRUB_CRYPTODISK_MAX_UUID_LENGTH + 1]; + grub_uint8_t lrw_key[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint8_t *lrw_precalc; + grub_uint8_t iv_prefix[64]; + grub_size_t iv_prefix_len; +#ifdef GRUB_UTIL + char *cheat; + int cheat_fd; +#endif + struct grub_cryptodisk *next; +}; +typedef struct grub_cryptodisk *grub_cryptodisk_t; + +gcry_err_code_t +grub_cryptodisk_setkey (grub_cryptodisk_t dev, + grub_uint8_t *key, grub_size_t keysize); +gcry_err_code_t +grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, + grub_uint8_t * data, grub_size_t len, + grub_disk_addr_t sector); +grub_err_t +grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source); +#ifdef GRUB_UTIL +grub_err_t +grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source, const char *cheat); +void +grub_util_cryptodisk_print_abstraction (grub_disk_t disk); +#endif + +grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid); +grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk); + +#endif diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 2adb2331d..48a5be1ca 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -308,8 +308,12 @@ fstest (int n, char **args) { char *argv[2] = { "-a", NULL}; if (mount_crypt) - if (execute_command ("luksmount", 1, argv)) - grub_util_error (_("luksmount command fails: %s"), grub_errmsg); + { + if (execute_command ("luksmount", 1, argv)) + grub_util_error (_("luksmount command fails: %s"), grub_errmsg); + if (execute_command ("gelimount", 1, argv)) + grub_util_error (_("gelimount command fails: %s"), grub_errmsg); + } } grub_lvm_fini (); From b44cd9e710c0bf0c1d1dfceef5ba883a4eebe98f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 02:34:32 +0200 Subject: [PATCH 28/43] zero-fill hash context for safety --- grub-core/disk/cryptodisk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 7f3b60bf5..dc6ca486e 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -200,6 +200,9 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, { grub_uint64_t tmp; grub_uint64_t ctx[(dev->iv_hash->contextsize + 7) / 8]; + + grub_memset (ctx, 0, sizeof (ctx)); + tmp = grub_cpu_to_le64 (sector << GRUB_DISK_SECTOR_BITS); dev->iv_hash->init (ctx); dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len); From 7ede8f8b5b62f7244f3cc9e9e6927cac43df006b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 02:36:04 +0200 Subject: [PATCH 29/43] support niter == 0 --- grub-core/disk/geli.c | 46 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 6af1de766..565498ece 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -205,7 +205,6 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, grub_uint8_t digest[dev->hash->mdlen]; grub_uint8_t geomkey[dev->hash->mdlen]; grub_uint8_t verify_key[dev->hash->mdlen]; - grub_uint8_t pbkdf_key[64]; grub_uint8_t zero[dev->cipher->cipher->blocksize]; char passphrase[MAX_PASSPHRASE] = ""; unsigned i; @@ -220,21 +219,40 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, if (!grub_password_get (passphrase, MAX_PASSPHRASE)) return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); - /* Calculate the PBKDF2 of the user supplied passphrase. */ - gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, - grub_strlen (passphrase), - header->salt, - sizeof (header->salt), - grub_le_to_cpu32 (header->niter), - pbkdf_key, sizeof (pbkdf_key)); + /* Calculate the PBKDF2 of the user supplied passphrase. */ + if (grub_le_to_cpu32 (header->niter) != 0) + { + grub_uint8_t pbkdf_key[64]; + gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, + grub_strlen (passphrase), + header->salt, + sizeof (header->salt), + grub_le_to_cpu32 (header->niter), + pbkdf_key, sizeof (pbkdf_key)); - if (gcry_err) - return grub_crypto_gcry_error (gcry_err); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); - gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key, - sizeof (pbkdf_key), geomkey); - if (gcry_err) - return grub_crypto_gcry_error (gcry_err); + gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key, + sizeof (pbkdf_key), geomkey); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + else + { + struct grub_crypto_hmac_handle *hnd; + + hnd = grub_crypto_hmac_init (dev->hash, NULL, 0); + if (!hnd) + return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY); + + grub_crypto_hmac_write (hnd, header->salt, sizeof (header->salt)); + grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase)); + + gcry_err = grub_crypto_hmac_fini (hnd, geomkey); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, sizeof (geomkey), "\1", 1, digest); From 972d86df5f68efecb9952fa994aa851bae44aa28 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 02:36:50 +0200 Subject: [PATCH 30/43] accept version 2 geli --- grub-core/disk/geli.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 565498ece..7d0c8621f 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -107,7 +107,8 @@ configure_ciphers (const struct grub_geli_phdr *header) /* Look for GELI magic sequence. */ if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) - || grub_le_to_cpu32 (header->version) != 3) + || grub_le_to_cpu32 (header->version) > 3 + || grub_le_to_cpu32 (header->version) < 2) { grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); return NULL; From b6b4ea5fd1ee71062c05b89bd606d869ebc33517 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 02:37:23 +0200 Subject: [PATCH 31/43] Add IDs for more ciphers --- grub-core/disk/geli.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 7d0c8621f..843eb7d90 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -81,7 +81,14 @@ struct grub_geli_phdr } __attribute__ ((packed)); const char *algorithms[] = { + [0x01] = "des", + [0x02] = "3des", + [0x03] = "blowfish", + [0x04] = "cast5", + /* 0x05 is skipjack, but we don't have it. */ [0x0b] = "aes", + /* 0x10 is null. */ + [0x15] = "camellia128", }; #define MAX_PASSPHRASE 256 From 848c83e75c059b8463788ec1095b84158458c200 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 02:38:42 +0200 Subject: [PATCH 32/43] add few necessarry const qualifiers for pointers --- grub-core/lib/crypto.c | 3 ++- include/grub/crypto.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index e6f55062b..5098f0a66 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -345,7 +345,8 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, } void -grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data, +grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, + const void *data, grub_size_t datalen) { hnd->md->write (hnd->ctx, data, datalen); diff --git a/include/grub/crypto.h b/include/grub/crypto.h index ab82da862..baccbcd06 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -235,7 +235,8 @@ struct grub_crypto_hmac_handle * grub_crypto_hmac_init (const struct gcry_md_spec *md, const void *key, grub_size_t keylen); void -grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data, +grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, + const void *data, grub_size_t datalen); gcry_err_code_t grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out); From 3e90811d884cab7a333ad9a4458732409be9f88f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 14:59:38 +0200 Subject: [PATCH 33/43] support non-512B sectors for geli --- grub-core/disk/cryptodisk.c | 50 +++++++++++++++++++++---------------- grub-core/disk/geli.c | 15 ++++++++++- grub-core/disk/luks.c | 1 + include/grub/cryptodisk.h | 7 ++++-- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 6dbd6bac6..4a716602e 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -35,7 +35,10 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ #define GF_POLYNOM 0x87 -#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GRUB_CRYPTODISK_GF_BYTES) +static inline int GF_PER_SECTOR (const struct grub_cryptodisk *dev) +{ + return 1U << (dev->log_sector_size - GRUB_CRYPTODISK_GF_LOG_BYTES); +} static grub_cryptodisk_t cryptodisk_list = NULL; static grub_uint8_t n = 0; @@ -44,7 +47,7 @@ static void gf_mul_x (grub_uint8_t *g) { int over = 0, over2 = 0; - int j; + unsigned j; for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++) { @@ -64,7 +67,7 @@ gf_mul_x_be (grub_uint8_t *g) int over = 0, over2 = 0; int j; - for (j = GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--) + for (j = (int) GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--) { over2 = !!(g[j] & 0x80); g[j] <<= 1; @@ -78,7 +81,7 @@ gf_mul_x_be (grub_uint8_t *g) static void gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) { - int i; + unsigned i; grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES]; grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES); grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES); @@ -129,14 +132,15 @@ generate_lrw_sector (struct lrw_sector *sec, grub_uint16_t c; int j; grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES); - sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] & (GF_PER_SECTOR - 1)); - sec->low_byte_c = (((GF_PER_SECTOR - 1) & ~sec->low_byte) + 1); - idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR - 1); + sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] + & (GF_PER_SECTOR (dev) - 1)); + sec->low_byte_c = (((GF_PER_SECTOR (dev) - 1) & ~sec->low_byte) + 1); + idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR (dev) - 1); gf_mul_be (sec->low, dev->lrw_key, idx); if (!sec->low_byte) return; - c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR; + c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR (dev); if (c & 0x100) { for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--) @@ -155,9 +159,10 @@ lrw_xor (const struct lrw_sector *sec, const struct grub_cryptodisk *dev, grub_uint8_t *b) { - int i; + unsigned i; - for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; i += GRUB_CRYPTODISK_GF_BYTES) + for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; + i += GRUB_CRYPTODISK_GF_BYTES) grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES); grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte, sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES); @@ -165,7 +170,7 @@ lrw_xor (const struct lrw_sector *sec, return; for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; - i < GRUB_DISK_SECTOR_SIZE; i += GRUB_CRYPTODISK_GF_BYTES) + i < (1U << dev->log_sector_size); i += GRUB_CRYPTODISK_GF_BYTES) grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES); grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, @@ -184,7 +189,7 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, if (dev->mode == GRUB_CRYPTODISK_MODE_ECB) return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); - for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE) + for (i = 0; i < len; i += (1U << dev->log_sector_size)) { grub_size_t sz = ((dev->cipher->cipher->blocksize + sizeof (grub_uint32_t) - 1) @@ -203,7 +208,7 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, grub_memset (ctx, 0, sizeof (ctx)); - tmp = grub_cpu_to_le64 (sector << GRUB_DISK_SECTOR_BITS); + tmp = grub_cpu_to_le64 (sector << dev->log_sector_size); dev->iv_hash->init (ctx); dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len); dev->iv_hash->write (ctx, &tmp, sizeof (tmp)); @@ -236,26 +241,26 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, { case GRUB_CRYPTODISK_MODE_CBC: err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, - GRUB_DISK_SECTOR_SIZE, iv); + (1U << dev->log_sector_size), iv); if (err) return err; break; case GRUB_CRYPTODISK_MODE_PCBC: err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, - GRUB_DISK_SECTOR_SIZE, iv); + (1U << dev->log_sector_size), iv); if (err) return err; break; case GRUB_CRYPTODISK_MODE_XTS: { - int j; + unsigned j; err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, dev->cipher->cipher->blocksize); if (err) return err; - for (j = 0; j < GRUB_DISK_SECTOR_SIZE; + for (j = 0; j < (1U << dev->log_sector_size); j += dev->cipher->cipher->blocksize) { grub_crypto_xor (data + i + j, data + i + j, iv, @@ -279,7 +284,8 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, lrw_xor (&sec, dev, data + i); err = grub_crypto_ecb_decrypt (dev->cipher, data + i, - data + i, GRUB_DISK_SECTOR_SIZE); + data + i, + (1U << dev->log_sector_size)); if (err) return err; lrw_xor (&sec, dev, data + i); @@ -333,17 +339,17 @@ grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t ke if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) { - int i; + unsigned i; grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; grub_free (dev->lrw_precalc); grub_memcpy (dev->lrw_key, key + real_keysize, dev->cipher->cipher->blocksize); - dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE); + dev->lrw_precalc = grub_malloc ((1U << dev->log_sector_size)); if (!dev->lrw_precalc) return GPG_ERR_OUT_OF_MEMORY; grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES); - for (i = 0; i < GRUB_DISK_SECTOR_SIZE; + for (i = 0; i < (1U << dev->log_sector_size); i += GRUB_CRYPTODISK_GF_BYTES) { idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES; @@ -401,6 +407,8 @@ grub_cryptodisk_open (const char *name, grub_disk_t disk, if (!dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + disk->log_sector_size = dev->log_sector_size; + #ifdef GRUB_UTIL if (dev->cheat) { diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 843eb7d90..3c2cbb9ce 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -73,7 +73,8 @@ struct grub_geli_phdr grub_uint32_t unused1; grub_uint16_t alg; grub_uint16_t keylen; - grub_uint16_t unused3[7]; + grub_uint16_t unused3[5]; + grub_uint32_t sector_size; grub_uint8_t keys_used; grub_uint32_t niter; grub_uint8_t salt[64]; @@ -120,6 +121,14 @@ configure_ciphers (const struct grub_geli_phdr *header) grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); return NULL; } + if ((grub_le_to_cpu32 (header->sector_size) + & (grub_le_to_cpu32 (header->sector_size) - 1)) + || grub_le_to_cpu32 (header->sector_size) == 0) + { + grub_dprintf ("geli", "incorrect sector size %d\n", + grub_le_to_cpu32 (header->sector_size)); + return NULL; + } #if 0 optr = uuid; @@ -199,6 +208,10 @@ configure_ciphers (const struct grub_geli_phdr *header) newdev->essiv_hash = NULL; newdev->hash = hash; newdev->iv_hash = iv_hash; + + for (newdev->log_sector_size = 0; + (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size); + newdev->log_sector_size++); #if 0 grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); #endif diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 0763da011..7c39002b8 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -283,6 +283,7 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->essiv_cipher = essiv_cipher; newdev->essiv_hash = essiv_hash; newdev->hash = hash; + newdev->log_sector_size = 9; grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); return newdev; } diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 284e599f2..11181f42d 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -43,8 +43,10 @@ typedef enum #define GRUB_CRYPTODISK_MAX_UUID_LENGTH 63 -#define GRUB_CRYPTODISK_GF_SIZE 128 -#define GRUB_CRYPTODISK_GF_BYTES (GRUB_CRYPTODISK_GF_SIZE / 8) +#define GRUB_CRYPTODISK_GF_LOG_SIZE 7 +#define GRUB_CRYPTODISK_GF_SIZE (1U << GRUB_CRYPTODISK_GF_LOG_SIZE) +#define GRUB_CRYPTODISK_GF_LOG_BYTES (GRUB_CRYPTODISK_GF_LOG_SIZE - 3) +#define GRUB_CRYPTODISK_GF_BYTES (1U << GRUB_CRYPTODISK_GF_LOG_BYTES) struct grub_cryptodisk { @@ -71,6 +73,7 @@ struct grub_cryptodisk char *cheat; int cheat_fd; #endif + int log_sector_size; struct grub_cryptodisk *next; }; typedef struct grub_cryptodisk *grub_cryptodisk_t; From 88ac3146d6ad5d17788821fa4aa296271a2c0550 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 17:15:55 +0200 Subject: [PATCH 34/43] geli v5 (including rekeying support) --- grub-core/disk/cryptodisk.c | 20 ++++++++++++-- grub-core/disk/geli.c | 53 ++++++++++++++++++++++++++++++++----- grub-core/lib/crypto.c | 2 +- include/grub/crypto.h | 2 +- include/grub/cryptodisk.h | 13 ++++++++- 5 files changed, 79 insertions(+), 11 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 4a716602e..5ab607146 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -178,7 +178,7 @@ lrw_xor (const struct lrw_sector *sec, } gcry_err_code_t -grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, +grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) { @@ -186,7 +186,7 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, gcry_err_code_t err; /* The only mode without IV. */ - if (dev->mode == GRUB_CRYPTODISK_MODE_ECB) + if (dev->mode == GRUB_CRYPTODISK_MODE_ECB && !dev->rekey) return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); for (i = 0; i < len; i += (1U << dev->log_sector_size)) @@ -196,6 +196,18 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, / sizeof (grub_uint32_t)); grub_uint32_t iv[sz]; + if (dev->rekey) + { + grub_uint64_t zone = sector >> dev->rekey_shift; + if (zone != dev->last_rekey) + { + err = dev->rekey (dev, zone); + if (err) + return err; + dev->last_rekey = zone; + } + } + grub_memset (iv, 0, sz * sizeof (iv[0])); switch (dev->mode_iv) { @@ -291,6 +303,10 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, lrw_xor (&sec, dev, data + i); } break; + case GRUB_CRYPTODISK_MODE_ECB: + grub_crypto_ecb_decrypt (dev->cipher, data + i, data + i, + (1U << dev->log_sector_size)); + break; default: return GPG_ERR_NOT_IMPLEMENTED; } diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 3c2cbb9ce..b3fa10cc5 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -104,6 +104,28 @@ static const struct grub_arg_option options[] = static int check_uuid, have_it; static char *search_uuid; +static gcry_err_code_t +geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno) +{ + gcry_err_code_t gcry_err; + const struct { + char magic[4]; + grub_uint64_t zone; + } __attribute__ ((packed)) tohash + = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) }; + grub_uint64_t key[(dev->hash->mdlen + 7) / 8]; + + grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n", + zoneno, dev->rekey_derived_size); + gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64, + &tohash, sizeof (tohash), key); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key, + dev->rekey_derived_size); +} + static grub_cryptodisk_t configure_ciphers (const struct grub_geli_phdr *header) { @@ -115,8 +137,8 @@ configure_ciphers (const struct grub_geli_phdr *header) /* Look for GELI magic sequence. */ if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) - || grub_le_to_cpu32 (header->version) > 3 - || grub_le_to_cpu32 (header->version) < 2) + || grub_le_to_cpu32 (header->version) > 5 + || grub_le_to_cpu32 (header->version) < 1) { grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); return NULL; @@ -212,6 +234,12 @@ configure_ciphers (const struct grub_geli_phdr *header) for (newdev->log_sector_size = 0; (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size); newdev->log_sector_size++); + + if (grub_le_to_cpu32 (header->version) >= 5) + { + newdev->rekey = geli_rekey; + newdev->rekey_shift = 20; + } #if 0 grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); #endif @@ -325,10 +353,23 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, - keysize); - if (gcry_err) - return grub_crypto_gcry_error (gcry_err); + if (!dev->rekey) + { + gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, + keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + else + { + /* For a reason I don't know, the IV key is used in rekeying. */ + grub_memcpy (dev->rekey_key, candidate_key.iv_key, + sizeof (candidate_key.iv_key)); + dev->rekey_derived_size = keysize; + dev->last_rekey = -1; + COMPILE_TIME_ASSERT (sizeof (dev->rekey_key) + >= sizeof (candidate_key.iv_key)); + } dev->iv_prefix_len = sizeof (candidate_key.iv_key); grub_memcpy (dev->iv_prefix, candidate_key.iv_key, diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 5098f0a66..8876cc326 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -388,7 +388,7 @@ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out) gcry_err_code_t grub_crypto_hmac_buffer (const struct gcry_md_spec *md, const void *key, grub_size_t keylen, - void *data, grub_size_t datalen, void *out) + const void *data, grub_size_t datalen, void *out) { struct grub_crypto_hmac_handle *hnd; diff --git a/include/grub/crypto.h b/include/grub/crypto.h index baccbcd06..10368d99f 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -244,7 +244,7 @@ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out); gcry_err_code_t grub_crypto_hmac_buffer (const struct gcry_md_spec *md, const void *key, grub_size_t keylen, - void *data, grub_size_t datalen, void *out); + const void *data, grub_size_t datalen, void *out); extern gcry_md_spec_t _gcry_digest_spec_md5; extern gcry_md_spec_t _gcry_digest_spec_sha1; diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 11181f42d..2da3f76d2 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -48,6 +48,12 @@ typedef enum #define GRUB_CRYPTODISK_GF_LOG_BYTES (GRUB_CRYPTODISK_GF_LOG_SIZE - 3) #define GRUB_CRYPTODISK_GF_BYTES (1U << GRUB_CRYPTODISK_GF_LOG_BYTES) +struct grub_cryptodisk; + +typedef gcry_err_code_t +(*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev, + grub_uint64_t zoneno); + struct grub_cryptodisk { char *source; @@ -74,6 +80,11 @@ struct grub_cryptodisk int cheat_fd; #endif int log_sector_size; + grub_cryptodisk_rekey_func_t rekey; + int rekey_shift; + grub_uint8_t rekey_key[64]; + grub_uint64_t last_rekey; + int rekey_derived_size; struct grub_cryptodisk *next; }; typedef struct grub_cryptodisk *grub_cryptodisk_t; @@ -82,7 +93,7 @@ gcry_err_code_t grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize); gcry_err_code_t -grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, +grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector); grub_err_t From 574d268020510d7996c1e263f1fac79aefeaf854 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 17:18:18 +0200 Subject: [PATCH 35/43] Add few FIXME comments --- grub-core/disk/geli.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index b3fa10cc5..6ffc7c26e 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -81,14 +81,19 @@ struct grub_geli_phdr struct grub_geli_key keys[2]; } __attribute__ ((packed)); +/* FIXME: support big-endian pre-version-4 volumes. */ +/* FIXME: support for keyfiles. */ +/* FIXME: support for HMAC. */ +/* FIXME: support for UUID. */ +/* FIXME: support for mounting all boot volumes. */ const char *algorithms[] = { [0x01] = "des", [0x02] = "3des", [0x03] = "blowfish", [0x04] = "cast5", - /* 0x05 is skipjack, but we don't have it. */ + /* FIXME: 0x05 is skipjack, but we don't have it. */ [0x0b] = "aes", - /* 0x10 is null. */ + /* FIXME: 0x10 is null. */ [0x15] = "camellia128", }; From 171e2be183b086b1560630f02231ee15771176a7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 17:41:50 +0200 Subject: [PATCH 36/43] geli xts support --- grub-core/disk/cryptodisk.c | 5 +++++ grub-core/disk/geli.c | 34 ++++++++++++++++++++++++++++------ include/grub/cryptodisk.h | 1 + 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 5ab607146..bf0b1cde2 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -234,6 +234,11 @@ grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, case GRUB_CRYPTODISK_MODE_IV_PLAIN: iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: + iv[1] = grub_cpu_to_le32 (sector >> (32 - dev->log_sector_size)); + iv[0] = grub_cpu_to_le32 ((sector << dev->log_sector_size) + & 0xFFFFFFFF); + break; case GRUB_CRYPTODISK_MODE_IV_BENBI: { grub_uint64_t num = (sector << dev->benbi_log) + 1; diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 6ffc7c26e..334a06340 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -95,6 +95,7 @@ const char *algorithms[] = { [0x0b] = "aes", /* FIXME: 0x10 is null. */ [0x15] = "camellia128", + [0x16] = "aes" }; #define MAX_PASSPHRASE 256 @@ -135,7 +136,7 @@ static grub_cryptodisk_t configure_ciphers (const struct grub_geli_phdr *header) { grub_cryptodisk_t newdev; - grub_crypto_cipher_handle_t cipher = NULL; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; const struct gcry_cipher_spec *ciph; const char *ciphername = NULL; const gcry_md_spec_t *hash = NULL, *iv_hash = NULL; @@ -196,6 +197,13 @@ configure_ciphers (const struct grub_geli_phdr *header) if (!cipher) return NULL; + if (grub_le_to_cpu16 (header->alg) == 0x16) + { + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) + return NULL; + } + if (grub_le_to_cpu16 (header->keylen) > 1024) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", @@ -225,12 +233,20 @@ configure_ciphers (const struct grub_geli_phdr *header) if (!newdev) return NULL; newdev->cipher = cipher; + newdev->secondary_cipher = secondary_cipher; newdev->offset = 0; newdev->source_disk = NULL; newdev->benbi_log = 0; - newdev->mode = GRUB_CRYPTODISK_MODE_CBC; - newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH; - newdev->secondary_cipher = NULL; + if (grub_le_to_cpu16 (header->alg) == 0x16) + { + newdev->mode = GRUB_CRYPTODISK_MODE_XTS; + newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64; + } + else + { + newdev->mode = GRUB_CRYPTODISK_MODE_CBC; + newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH; + } newdev->essiv_cipher = NULL; newdev->essiv_hash = NULL; newdev->hash = hash; @@ -360,17 +376,23 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, /* Set the master key. */ if (!dev->rekey) { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header->alg) == 0x16) + real_keysize *= 2; gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, - keysize); + real_keysize); if (gcry_err) return grub_crypto_gcry_error (gcry_err); } else { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header->alg) == 0x16) + real_keysize *= 2; /* For a reason I don't know, the IV key is used in rekeying. */ grub_memcpy (dev->rekey_key, candidate_key.iv_key, sizeof (candidate_key.iv_key)); - dev->rekey_derived_size = keysize; + dev->rekey_derived_size = real_keysize; dev->last_rekey = -1; COMPILE_TIME_ASSERT (sizeof (dev->rekey_key) >= sizeof (candidate_key.iv_key)); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 2da3f76d2..1911c04be 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -38,6 +38,7 @@ typedef enum GRUB_CRYPTODISK_MODE_IV_PLAIN64, GRUB_CRYPTODISK_MODE_IV_ESSIV, GRUB_CRYPTODISK_MODE_IV_BENBI, + GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64, GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH } grub_cryptodisk_mode_iv_t; From 371a8f1183f2696b1a58e410d89ff131cfc830a7 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 17:50:22 +0200 Subject: [PATCH 37/43] Fix a potential buffer overflow --- grub-core/disk/luks.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 7c39002b8..3f98dfc87 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -32,7 +32,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define MAX_PASSPHRASE 256 #define LUKS_KEY_ENABLED 0x00AC71F3 -#define LUKS_STRIPES 4000 /* On disk LUKS header */ struct grub_luks_phdr @@ -301,10 +300,16 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, unsigned i; grub_size_t length; grub_err_t err; + grub_size_t max_stripes = 1; grub_printf ("Attempting to decrypt master key...\n"); - split_key = grub_malloc (keysize * LUKS_STRIPES); + for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) + if (grub_be_to_cpu32 (header->keyblock[i].active) == LUKS_KEY_ENABLED + && grub_be_to_cpu32 (header->keyblock[i].stripes) > max_stripes) + max_stripes = grub_be_to_cpu32 (header->keyblock[i].stripes); + + split_key = grub_malloc (keysize * max_stripes); if (!split_key) return grub_errno; @@ -351,8 +356,7 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, return grub_crypto_gcry_error (gcry_err); } - length = (grub_be_to_cpu32 (header->keyBytes) - * grub_be_to_cpu32 (header->keyblock[i].stripes)); + length = (keysize * grub_be_to_cpu32 (header->keyblock[i].stripes)); /* Read and decrypt the key material from the disk. */ err = grub_disk_read (source, From 7efb5c9eeaad83e8eb2fdcde0fd182828d157610 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 20:37:56 +0200 Subject: [PATCH 38/43] Use hardcoded reference to sha512 and sha256 in geli rather than runtime lookup since they are always used --- Makefile.util.def | 12 ++++++++++++ grub-core/disk/geli.c | 23 ++--------------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index fe9e8c036..51771b9fa 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -108,6 +108,7 @@ program = { common = util/bin2h.c; ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER)'; mansection = 1; @@ -138,6 +139,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -150,6 +152,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -162,6 +165,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -195,6 +199,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL)'; condition = COND_GRUB_PE2ELF; @@ -228,6 +233,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(freetype_libs)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -248,6 +254,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -277,6 +284,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -292,6 +300,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBGEOM)'; @@ -306,6 +315,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -616,6 +626,7 @@ program = { cflags = -Wno-format; ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -629,6 +640,7 @@ program = { ldadd = libgrubmods.a; ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 334a06340..b86e363c7 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -139,7 +139,6 @@ configure_ciphers (const struct grub_geli_phdr *header) grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; const struct gcry_cipher_spec *ciph; const char *ciphername = NULL; - const gcry_md_spec_t *hash = NULL, *iv_hash = NULL; /* Look for GELI magic sequence. */ if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) @@ -211,24 +210,6 @@ configure_ciphers (const struct grub_geli_phdr *header) return NULL; } - hash = grub_crypto_lookup_md_by_name ("sha512"); - if (!hash) - { - grub_crypto_cipher_close (cipher); - grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", - "sha512"); - return NULL; - } - - iv_hash = grub_crypto_lookup_md_by_name ("sha256"); - if (!hash) - { - grub_crypto_cipher_close (cipher); - grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", - "sha512"); - return NULL; - } - newdev = grub_zalloc (sizeof (struct grub_cryptodisk)); if (!newdev) return NULL; @@ -249,8 +230,8 @@ configure_ciphers (const struct grub_geli_phdr *header) } newdev->essiv_cipher = NULL; newdev->essiv_hash = NULL; - newdev->hash = hash; - newdev->iv_hash = iv_hash; + newdev->hash = GRUB_MD_SHA512; + newdev->iv_hash = GRUB_MD_SHA256; for (newdev->log_sector_size = 0; (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size); From 23432f6542506794cbcf1d3b75b1e9d3fe284ad4 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 21:11:14 +0200 Subject: [PATCH 39/43] support UUID for geli --- grub-core/disk/geli.c | 41 +++++++++++++++++++++++++++++---------- grub-core/disk/luks.c | 1 + include/grub/cryptodisk.h | 2 +- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index b86e363c7..104200546 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -84,7 +84,6 @@ struct grub_geli_phdr /* FIXME: support big-endian pre-version-4 volumes. */ /* FIXME: support for keyfiles. */ /* FIXME: support for HMAC. */ -/* FIXME: support for UUID. */ /* FIXME: support for mounting all boot volumes. */ const char *algorithms[] = { [0x01] = "des", @@ -132,6 +131,18 @@ geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno) dev->rekey_derived_size); } +static inline int +ascii2hex (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return 0; +} + static grub_cryptodisk_t configure_ciphers (const struct grub_geli_phdr *header) { @@ -139,6 +150,11 @@ configure_ciphers (const struct grub_geli_phdr *header) grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; const struct gcry_cipher_spec *ciph; const char *ciphername = NULL; + char uuid[GRUB_MD_SHA256->mdlen * 2 + 1]; + grub_uint8_t uuidbin[GRUB_MD_SHA256->mdlen]; + grub_uint8_t *iptr; + char *optr; + gcry_err_code_t gcry_err; /* Look for GELI magic sequence. */ if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) @@ -157,13 +173,19 @@ configure_ciphers (const struct grub_geli_phdr *header) return NULL; } -#if 0 - optr = uuid; - for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)]; - iptr++) + gcry_err = grub_crypto_hmac_buffer (GRUB_MD_SHA256, + header->salt, sizeof (header->salt), + "uuid", sizeof ("uuid") - 1, uuidbin); + if (gcry_err) { - if (*iptr != '-') - *optr++ = *iptr; + grub_crypto_gcry_error (gcry_err); + return NULL; + } + optr = uuid; + for (iptr = uuidbin; iptr < &uuidbin[ARRAY_SIZE (uuidbin)]; iptr++) + { + grub_snprintf (optr, 3, "%02x", *iptr); + optr += 2; } *optr = 0; @@ -172,7 +194,6 @@ configure_ciphers (const struct grub_geli_phdr *header) grub_dprintf ("luks", "%s != %s", uuid, search_uuid); return NULL; } -#endif if (grub_le_to_cpu16 (header->alg) >= ARRAY_SIZE (algorithms) || algorithms[grub_le_to_cpu16 (header->alg)] == NULL) @@ -242,9 +263,9 @@ configure_ciphers (const struct grub_geli_phdr *header) newdev->rekey = geli_rekey; newdev->rekey_shift = 20; } -#if 0 + grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); -#endif + COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1); return newdev; } diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 3f98dfc87..a94b3cc52 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -284,6 +284,7 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->hash = hash; newdev->log_sector_size = 9; grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); + COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= sizeof (uuid)); return newdev; } diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 1911c04be..169fb119d 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -42,7 +42,7 @@ typedef enum GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH } grub_cryptodisk_mode_iv_t; -#define GRUB_CRYPTODISK_MAX_UUID_LENGTH 63 +#define GRUB_CRYPTODISK_MAX_UUID_LENGTH 71 #define GRUB_CRYPTODISK_GF_LOG_SIZE 7 #define GRUB_CRYPTODISK_GF_SIZE (1U << GRUB_CRYPTODISK_GF_LOG_SIZE) From 8358d7f22154b26c94d440e5c76e6287dfdf1372 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 21:40:13 +0200 Subject: [PATCH 40/43] Skip one-time volumes and add option for skipping non-boot volumes --- grub-core/disk/geli.c | 36 +++++++++++++++++++++++++++++------- grub-core/disk/luks.c | 2 +- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 104200546..8731d3065 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -70,7 +70,7 @@ struct grub_geli_phdr grub_uint8_t magic[16]; #define GELI_MAGIC "GEOM::ELI" grub_uint32_t version; - grub_uint32_t unused1; + grub_uint32_t flags; grub_uint16_t alg; grub_uint16_t keylen; grub_uint16_t unused3[5]; @@ -81,6 +81,12 @@ struct grub_geli_phdr struct grub_geli_key keys[2]; } __attribute__ ((packed)); +enum + { + GRUB_GELI_FLAGS_ONETIME = 1, + GRUB_GELI_FLAGS_BOOT = 2, + }; + /* FIXME: support big-endian pre-version-4 volumes. */ /* FIXME: support for keyfiles. */ /* FIXME: support for HMAC. */ @@ -103,10 +109,11 @@ static const struct grub_arg_option options[] = { {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, {"all", 'a', 0, N_("Mount all."), 0, 0}, + {"boot", 'b', 0, N_("Mount all volumes marked as boot."), 0, 0}, {0, 0, 0, 0, 0, 0} }; -static int check_uuid, have_it; +static int check_uuid, check_boot, have_it; static char *search_uuid; static gcry_err_code_t @@ -171,7 +178,19 @@ configure_ciphers (const struct grub_geli_phdr *header) grub_dprintf ("geli", "incorrect sector size %d\n", grub_le_to_cpu32 (header->sector_size)); return NULL; - } + } + + if (grub_le_to_cpu32 (header->flags) & GRUB_GELI_FLAGS_ONETIME) + { + grub_dprintf ("geli", "skipping one-time volume\n"); + return NULL; + } + + if (check_boot && !(grub_le_to_cpu32 (header->flags) & GRUB_GELI_FLAGS_BOOT)) + { + grub_dprintf ("geli", "not a boot volume\n"); + return NULL; + } gcry_err = grub_crypto_hmac_buffer (GRUB_MD_SHA256, header->salt, sizeof (header->salt), @@ -191,7 +210,7 @@ configure_ciphers (const struct grub_geli_phdr *header) if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) { - grub_dprintf ("luks", "%s != %s", uuid, search_uuid); + grub_dprintf ("geli", "%s != %s\n", uuid, search_uuid); return NULL; } @@ -549,7 +568,7 @@ grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) { struct grub_arg_list *state = ctxt->state; - if (argc < 1 && !state[1].set) + if (argc < 1 && !state[1].set && !state[2].set) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); have_it = 0; @@ -565,6 +584,7 @@ grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) } check_uuid = 1; + check_boot = state[2].set; search_uuid = args[0]; grub_device_iterate (&grub_geli_scan_device); search_uuid = NULL; @@ -573,10 +593,11 @@ grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); return GRUB_ERR_NONE; } - else if (state[1].set) + else if (state[1].set || (argc == 0 && state[2].set)) { check_uuid = 0; search_uuid = NULL; + check_boot = state[2].set; grub_device_iterate (&grub_geli_scan_device); search_uuid = NULL; return GRUB_ERR_NONE; @@ -589,6 +610,7 @@ grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) check_uuid = 0; search_uuid = NULL; + check_boot = state[2].set; disk = grub_disk_open (args[0]); if (!disk) return grub_errno; @@ -614,7 +636,7 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT (geli) { cmd = grub_register_extcmd ("gelimount", grub_cmd_gelimount, 0, - N_("SOURCE|-u UUID|-a"), + N_("SOURCE|-u UUID|-a|-b"), N_("Mount a GELI device."), options); } diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index a94b3cc52..aa23e2e35 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -109,7 +109,7 @@ configure_ciphers (const struct grub_luks_phdr *header) if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) { - grub_dprintf ("luks", "%s != %s", uuid, search_uuid); + grub_dprintf ("luks", "%s != %s\n", uuid, search_uuid); return NULL; } From f718594e32da770ac412d1a02a2002c81d2302c2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 24 Apr 2011 21:41:49 +0200 Subject: [PATCH 41/43] update fixme --- grub-core/disk/geli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 8731d3065..1353be172 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -87,10 +87,10 @@ enum GRUB_GELI_FLAGS_BOOT = 2, }; +/* FIXME: support version 0. */ /* FIXME: support big-endian pre-version-4 volumes. */ /* FIXME: support for keyfiles. */ /* FIXME: support for HMAC. */ -/* FIXME: support for mounting all boot volumes. */ const char *algorithms[] = { [0x01] = "des", [0x02] = "3des", From d7bdab32b8e9e17c0111536b357189f72e6f330d Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 25 Apr 2011 14:46:47 +0200 Subject: [PATCH 42/43] fix linking trouble on FreeBSD --- Makefile.util.def | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index 51771b9fa..3125b8b30 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -107,8 +107,8 @@ program = { name = grub-bin2h; common = util/bin2h.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER)'; mansection = 1; @@ -123,8 +123,8 @@ program = { extra_dist = util/grub-mkimagexx.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBLZMA)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -138,8 +138,8 @@ program = { common = util/grub-mkrelpath.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -151,8 +151,8 @@ program = { common = util/grub-script-check.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -164,8 +164,8 @@ program = { common = util/grub-editenv.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -177,8 +177,8 @@ program = { common = util/grub-mkpasswd-pbkdf2.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; cflags = '$(CFLAGS_GCRY)'; @@ -198,8 +198,8 @@ program = { common = util/grub-pe2elf.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL)'; condition = COND_GRUB_PE2ELF; @@ -217,8 +217,8 @@ program = { cppflags = '$(CPPFLAGS_GCRY)'; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -232,8 +232,8 @@ program = { cflags = '$(freetype_cflags)'; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(freetype_libs)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; @@ -253,8 +253,8 @@ program = { sparc64_ieee1275 = util/ieee1275/devicemap.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -266,8 +266,8 @@ program = { common = util/grub-probe.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -299,8 +299,8 @@ program = { ieee1275 = util/ieee1275/ofpath.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBGEOM)'; @@ -314,8 +314,8 @@ program = { common = util/grub-mklayout.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -625,8 +625,8 @@ program = { common = grub-core/tests/lib/test.c; cflags = -Wno-format; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; @@ -639,8 +639,8 @@ program = { common = grub-core/lib/i386/pc/vesa_modes_table.c; ldadd = libgrubmods.a; - ldadd = libgrubkern.a; ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; From 20a409405b6caf9f51bb391caaac24124c0de3f9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 25 Apr 2011 14:52:07 +0200 Subject: [PATCH 43/43] Integrate geli into autoconfiguration system --- grub-core/disk/cryptodisk.c | 210 ++++++++++++++- grub-core/disk/geli.c | 464 +++++++++++++--------------------- grub-core/disk/luks.c | 329 ++++++------------------ grub-core/kern/emu/getroot.c | 217 +++++++++++++++- grub-core/kern/emu/hostdisk.c | 204 +++++++-------- include/grub/cryptodisk.h | 35 ++- include/grub/emu/getroot.h | 5 + include/grub/emu/hostdisk.h | 8 +- util/grub-fstest.c | 6 +- util/grub-install.in | 6 +- util/grub-mkconfig.in | 2 +- util/grub-mkconfig_lib.in | 10 +- util/grub-probe.c | 21 +- 13 files changed, 835 insertions(+), 682 deletions(-) diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index bf0b1cde2..1a5e8164b 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #ifdef GRUB_UTIL #include @@ -33,6 +35,16 @@ GRUB_MOD_LICENSE ("GPLv3+"); +grub_cryptodisk_dev_t grub_cryptodisk_list; + +static const struct grub_arg_option options[] = + { + {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, + {"all", 'a', 0, N_("Mount all."), 0, 0}, + {"boot", 'b', 0, N_("Mount all volumes marked as boot."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + /* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ #define GF_POLYNOM 0x87 static inline int GF_PER_SECTOR (const struct grub_cryptodisk *dev) @@ -480,7 +492,7 @@ grub_cryptodisk_close (grub_disk_t disk) static grub_err_t grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) + grub_size_t size, char *buf) { grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; grub_err_t err; @@ -635,7 +647,7 @@ grub_util_cryptodisk_print_abstraction (grub_disk_t disk) { grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; - grub_printf ("luks "); + grub_printf ("cryptodisk %s ", dev->modname); if (dev->cipher) grub_printf ("%s ", dev->cipher->cipher->modname); @@ -650,8 +662,197 @@ grub_util_cryptodisk_print_abstraction (grub_disk_t disk) if (dev->iv_hash) grub_printf ("%s ", dev->iv_hash->modname); } + +void +grub_util_cryptodisk_print_uuid (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_printf ("%s ", dev->uuid); +} + #endif +static int check_boot, have_it; +static char *search_uuid; + +static void +cryptodisk_close (grub_cryptodisk_t dev) +{ + grub_crypto_cipher_close (dev->cipher); + grub_crypto_cipher_close (dev->secondary_cipher); + grub_crypto_cipher_close (dev->essiv_cipher); + grub_free (dev); +} + +static grub_err_t +grub_cryptodisk_scan_device_real (const char *name, grub_disk_t source) +{ + grub_err_t err; + grub_cryptodisk_t dev; + grub_cryptodisk_dev_t cr; + + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + return GRUB_ERR_NONE; + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, search_uuid, check_boot); + if (grub_errno) + return grub_errno; + if (!dev) + continue; + + err = cr->recover_key (source, dev); + if (err) + { + cryptodisk_close (dev); + return err; + } + + grub_cryptodisk_insert (dev, name, source); + + have_it = 1; + + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; +} + +#ifdef GRUB_UTIL +#include +grub_err_t +grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat) +{ + grub_err_t err; + grub_cryptodisk_t dev; + grub_cryptodisk_dev_t cr; + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (sourcedev); + if (!source) + return grub_errno; + + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, search_uuid, check_boot); + if (grub_errno) + return grub_errno; + if (!dev) + continue; + + grub_util_info ("cheatmounted %s (%s) at %s", sourcedev, dev->modname, + cheat); + err = grub_cryptodisk_cheat_insert (dev, sourcedev, source, cheat); + grub_disk_close (source); + if (err) + grub_free (dev); + + return GRUB_ERR_NONE; + } + + grub_disk_close (source); + + return GRUB_ERR_NONE; +} +#endif + +static int +grub_cryptodisk_scan_device (const char *name) +{ + grub_err_t err; + grub_disk_t source; + + /* Try to open disk. */ + source = grub_disk_open (name); + if (!source) + return grub_errno; + + err = grub_cryptodisk_scan_device_real (name, source); + + grub_disk_close (source); + + if (err) + grub_print_error (); + return have_it && search_uuid ? 1 : 0; +} + +static grub_err_t +grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + + if (argc < 1 && !state[1].set && !state[2].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + have_it = 0; + if (state[0].set) + { + grub_cryptodisk_t dev; + + dev = grub_cryptodisk_get_by_uuid (args[0]); + if (dev) + { + grub_dprintf ("cryptodisk", + "already mounted as crypto%lu\n", dev->id); + return GRUB_ERR_NONE; + } + + check_boot = state[2].set; + search_uuid = args[0]; + grub_device_iterate (&grub_cryptodisk_scan_device); + search_uuid = NULL; + + if (!have_it) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such cryptodisk found"); + return GRUB_ERR_NONE; + } + else if (state[1].set || (argc == 0 && state[2].set)) + { + search_uuid = NULL; + check_boot = state[2].set; + grub_device_iterate (&grub_cryptodisk_scan_device); + search_uuid = NULL; + return GRUB_ERR_NONE; + } + else + { + grub_err_t err; + grub_disk_t disk; + grub_cryptodisk_t dev; + + search_uuid = NULL; + check_boot = state[2].set; + disk = grub_disk_open (args[0]); + if (!disk) + return grub_errno; + + dev = grub_cryptodisk_get_by_source_disk (disk); + if (dev) + { + grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); + grub_disk_close (disk); + return GRUB_ERR_NONE; + } + + err = grub_cryptodisk_scan_device_real (args[0], disk); + + grub_disk_close (disk); + + return err; + } +} + static struct grub_disk_dev grub_cryptodisk_dev = { .name = "cryptodisk", .id = GRUB_DISK_DEVICE_CRYPTODISK_ID, @@ -666,9 +867,14 @@ static struct grub_disk_dev grub_cryptodisk_dev = { .next = 0 }; +static grub_extcmd_t cmd; + GRUB_MOD_INIT (cryptodisk) { grub_disk_dev_register (&grub_cryptodisk_dev); + cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, + N_("SOURCE|-u UUID|-a|-b"), + N_("Mount a crypto device."), options); } GRUB_MOD_FINI (cryptodisk) diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 1353be172..d2ae5da56 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -105,17 +105,6 @@ const char *algorithms[] = { #define MAX_PASSPHRASE 256 -static const struct grub_arg_option options[] = - { - {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, - {"all", 'a', 0, N_("Mount all."), 0, 0}, - {"boot", 'b', 0, N_("Mount all volumes marked as boot."), 0, 0}, - {0, 0, 0, 0, 0, 0} - }; - -static int check_uuid, check_boot, have_it; -static char *search_uuid; - static gcry_err_code_t geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno) { @@ -150,56 +139,21 @@ ascii2hex (char c) return 0; } -static grub_cryptodisk_t -configure_ciphers (const struct grub_geli_phdr *header) +static inline gcry_err_code_t +make_uuid (const struct grub_geli_phdr *header, + char *uuid) { - grub_cryptodisk_t newdev; - grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; - const struct gcry_cipher_spec *ciph; - const char *ciphername = NULL; - char uuid[GRUB_MD_SHA256->mdlen * 2 + 1]; grub_uint8_t uuidbin[GRUB_MD_SHA256->mdlen]; + gcry_err_code_t err; grub_uint8_t *iptr; char *optr; - gcry_err_code_t gcry_err; - /* Look for GELI magic sequence. */ - if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) - || grub_le_to_cpu32 (header->version) > 5 - || grub_le_to_cpu32 (header->version) < 1) - { - grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); - return NULL; - } - if ((grub_le_to_cpu32 (header->sector_size) - & (grub_le_to_cpu32 (header->sector_size) - 1)) - || grub_le_to_cpu32 (header->sector_size) == 0) - { - grub_dprintf ("geli", "incorrect sector size %d\n", - grub_le_to_cpu32 (header->sector_size)); - return NULL; - } + err = grub_crypto_hmac_buffer (GRUB_MD_SHA256, + header->salt, sizeof (header->salt), + "uuid", sizeof ("uuid") - 1, uuidbin); + if (err) + return err; - if (grub_le_to_cpu32 (header->flags) & GRUB_GELI_FLAGS_ONETIME) - { - grub_dprintf ("geli", "skipping one-time volume\n"); - return NULL; - } - - if (check_boot && !(grub_le_to_cpu32 (header->flags) & GRUB_GELI_FLAGS_BOOT)) - { - grub_dprintf ("geli", "not a boot volume\n"); - return NULL; - } - - gcry_err = grub_crypto_hmac_buffer (GRUB_MD_SHA256, - header->salt, sizeof (header->salt), - "uuid", sizeof ("uuid") - 1, uuidbin); - if (gcry_err) - { - grub_crypto_gcry_error (gcry_err); - return NULL; - } optr = uuid; for (iptr = uuidbin; iptr < &uuidbin[ARRAY_SIZE (uuidbin)]; iptr++) { @@ -207,22 +161,133 @@ configure_ciphers (const struct grub_geli_phdr *header) optr += 2; } *optr = 0; + return GPG_ERR_NO_ERROR; +} - if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) +#ifdef GRUB_UTIL + +#include +#include +#include +#include +#include +#include +#include +#include + +char * +grub_util_get_geli_uuid (const char *dev) +{ + int fd = open (dev, O_RDONLY); + grub_uint64_t s; + unsigned log_secsize; + grub_uint8_t hdr[512]; + struct grub_geli_phdr *header; + char *uuid; + gcry_err_code_t err; + + if (fd < 0) + return NULL; + + s = grub_util_get_fd_sectors (fd, &log_secsize); + grub_util_fd_seek (fd, dev, (s << log_secsize) - 512); + + uuid = xmalloc (GRUB_MD_SHA256->mdlen * 2 + 1); + if (grub_util_fd_read (fd, (void *) &hdr, 512) < 0) + grub_util_error ("couldn't read ELI metadata"); + + COMPILE_TIME_ASSERT (sizeof (header) <= 512); + header = (void *) &hdr; + + /* Look for GELI magic sequence. */ + if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) + || grub_le_to_cpu32 (header->version) > 5 + || grub_le_to_cpu32 (header->version) < 1) + grub_util_error ("wrong ELI magic or version"); + + err = make_uuid ((void *) &hdr, uuid); + if (err) + return NULL; + + return uuid; +} +#endif + +static grub_cryptodisk_t +configure_ciphers (grub_disk_t disk, const char *check_uuid, + int boot_only) +{ + grub_cryptodisk_t newdev; + struct grub_geli_phdr header; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; + const struct gcry_cipher_spec *ciph; + const char *ciphername = NULL; + gcry_err_code_t gcry_err; + char uuid[GRUB_MD_SHA256->mdlen * 2 + 1]; + grub_disk_addr_t sector; + grub_err_t err; + + sector = grub_disk_get_size (disk); + if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0) + return NULL; + + /* Read the GELI header. */ + err = grub_disk_read (disk, sector - 1, 0, sizeof (header), &header); + if (err) + return NULL; + + /* Look for GELI magic sequence. */ + if (grub_memcmp (header.magic, GELI_MAGIC, sizeof (GELI_MAGIC)) + || grub_le_to_cpu32 (header.version) > 5 + || grub_le_to_cpu32 (header.version) < 1) { - grub_dprintf ("geli", "%s != %s\n", uuid, search_uuid); + grub_dprintf ("geli", "wrong magic %02x\n", header.magic[0]); return NULL; } - if (grub_le_to_cpu16 (header->alg) >= ARRAY_SIZE (algorithms) - || algorithms[grub_le_to_cpu16 (header->alg)] == NULL) + if ((grub_le_to_cpu32 (header.sector_size) + & (grub_le_to_cpu32 (header.sector_size) - 1)) + || grub_le_to_cpu32 (header.sector_size) == 0) + { + grub_dprintf ("geli", "incorrect sector size %d\n", + grub_le_to_cpu32 (header.sector_size)); + return NULL; + } + + if (grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_ONETIME) + { + grub_dprintf ("geli", "skipping one-time volume\n"); + return NULL; + } + + if (boot_only && !(grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_BOOT)) + { + grub_dprintf ("geli", "not a boot volume\n"); + return NULL; + } + + gcry_err = make_uuid (&header, uuid); + if (gcry_err) + { + grub_crypto_gcry_error (gcry_err); + return NULL; + } + + if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0) + { + grub_dprintf ("geli", "%s != %s\n", uuid, check_uuid); + return NULL; + } + + if (grub_le_to_cpu16 (header.alg) >= ARRAY_SIZE (algorithms) + || algorithms[grub_le_to_cpu16 (header.alg)] == NULL) { grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown", - grub_le_to_cpu16 (header->alg)); + grub_le_to_cpu16 (header.alg)); return NULL; } - ciphername = algorithms[grub_le_to_cpu16 (header->alg)]; + ciphername = algorithms[grub_le_to_cpu16 (header.alg)]; ciph = grub_crypto_lookup_cipher_by_name (ciphername); if (!ciph) { @@ -236,17 +301,17 @@ configure_ciphers (const struct grub_geli_phdr *header) if (!cipher) return NULL; - if (grub_le_to_cpu16 (header->alg) == 0x16) + if (grub_le_to_cpu16 (header.alg) == 0x16) { secondary_cipher = grub_crypto_cipher_open (ciph); if (!secondary_cipher) return NULL; } - if (grub_le_to_cpu16 (header->keylen) > 1024) + if (grub_le_to_cpu16 (header.keylen) > 1024) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", - grub_le_to_cpu16 (header->keylen)); + grub_le_to_cpu16 (header.keylen)); return NULL; } @@ -258,7 +323,7 @@ configure_ciphers (const struct grub_geli_phdr *header) newdev->offset = 0; newdev->source_disk = NULL; newdev->benbi_log = 0; - if (grub_le_to_cpu16 (header->alg) == 0x16) + if (grub_le_to_cpu16 (header.alg) == 0x16) { newdev->mode = GRUB_CRYPTODISK_MODE_XTS; newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64; @@ -274,25 +339,29 @@ configure_ciphers (const struct grub_geli_phdr *header) newdev->iv_hash = GRUB_MD_SHA256; for (newdev->log_sector_size = 0; - (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size); + (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header.sector_size); newdev->log_sector_size++); - if (grub_le_to_cpu32 (header->version) >= 5) + if (grub_le_to_cpu32 (header.version) >= 5) { newdev->rekey = geli_rekey; newdev->rekey_shift = 20; } +#ifdef GRUB_UTIL + newdev->modname = "geli"; +#endif + + newdev->total_length = grub_disk_get_size (disk) - 1; grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1); return newdev; } static grub_err_t -recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, - const char *name, grub_disk_t source __attribute__ ((unused))) +recover_key (grub_disk_t source, grub_cryptodisk_t dev) { - grub_size_t keysize = grub_le_to_cpu16 (header->keylen) / 8; + grub_size_t keysize; grub_uint8_t digest[dev->hash->mdlen]; grub_uint8_t geomkey[dev->hash->mdlen]; grub_uint8_t verify_key[dev->hash->mdlen]; @@ -300,25 +369,45 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, char passphrase[MAX_PASSPHRASE] = ""; unsigned i; gcry_err_code_t gcry_err; + struct grub_geli_phdr header; + char *tmp; + grub_disk_addr_t sector; + grub_err_t err; + sector = grub_disk_get_size (source); + if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli"); + + /* Read the GELI header. */ + err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); + if (err) + return err; + + keysize = grub_le_to_cpu16 (header.keylen) / 8; grub_memset (zero, 0, sizeof (zero)); grub_printf ("Attempting to decrypt master key...\n"); /* Get the passphrase from the user. */ - grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid); + tmp = NULL; + if (source->partition) + tmp = grub_partition_get_name (source->partition); + grub_printf ("Enter passphrase for %s%s%s (%s): ", source->name, + source->partition ? "," : "", tmp ? : "", + dev->uuid); + grub_free (tmp); if (!grub_password_get (passphrase, MAX_PASSPHRASE)) return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); /* Calculate the PBKDF2 of the user supplied passphrase. */ - if (grub_le_to_cpu32 (header->niter) != 0) + if (grub_le_to_cpu32 (header.niter) != 0) { grub_uint8_t pbkdf_key[64]; gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, grub_strlen (passphrase), - header->salt, - sizeof (header->salt), - grub_le_to_cpu32 (header->niter), + header.salt, + sizeof (header.salt), + grub_le_to_cpu32 (header.niter), pbkdf_key, sizeof (pbkdf_key)); if (gcry_err) @@ -337,7 +426,7 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, if (!hnd) return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY); - grub_crypto_hmac_write (hnd, header->salt, sizeof (header->salt)); + grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt)); grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase)); gcry_err = grub_crypto_hmac_fini (hnd, geomkey); @@ -358,13 +447,13 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize); /* Try to recover master key from each active keyslot. */ - for (i = 0; i < ARRAY_SIZE (header->keys); i++) + for (i = 0; i < ARRAY_SIZE (header.keys); i++) { struct grub_geli_key candidate_key; grub_uint8_t key_hmac[dev->hash->mdlen]; /* Check if keyslot is enabled. */ - if (! (header->keys_used & (1 << i))) + if (! (header.keys_used & (1 << i))) continue; grub_dprintf ("geli", "Trying keyslot %d\n", i); @@ -375,7 +464,7 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, return grub_crypto_gcry_error (gcry_err); gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key, - &header->keys[i], + &header.keys[i], sizeof (candidate_key), zero); if (gcry_err) @@ -398,7 +487,7 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, if (!dev->rekey) { grub_size_t real_keysize = keysize; - if (grub_le_to_cpu16 (header->alg) == 0x16) + if (grub_le_to_cpu16 (header.alg) == 0x16) real_keysize *= 2; gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, real_keysize); @@ -408,7 +497,7 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, else { grub_size_t real_keysize = keysize; - if (grub_le_to_cpu16 (header->alg) == 0x16) + if (grub_le_to_cpu16 (header.alg) == 0x16) real_keysize *= 2; /* For a reason I don't know, the IV key is used in rekeying. */ grub_memcpy (dev->rekey_key, candidate_key.iv_key, @@ -431,216 +520,17 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, return GRUB_ACCESS_DENIED; } -static void -close (grub_cryptodisk_t luks) -{ - grub_crypto_cipher_close (luks->cipher); - grub_crypto_cipher_close (luks->secondary_cipher); - grub_crypto_cipher_close (luks->essiv_cipher); - grub_free (luks); -} - -static grub_err_t -grub_geli_scan_device_real (const char *name, grub_disk_t source) -{ - grub_err_t err; - struct grub_geli_phdr header; - grub_cryptodisk_t newdev, dev; - grub_disk_addr_t sector; - - grub_dprintf ("geli", "scanning %s\n", source->name); - dev = grub_cryptodisk_get_by_source_disk (source); - - if (dev) - return GRUB_ERR_NONE; - - sector = grub_disk_get_size (source); - if (sector == GRUB_DISK_SIZE_UNKNOWN) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli"); - - /* Read the LUKS header. */ - err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); - if (err) - return err; - - newdev = configure_ciphers (&header); - if (!newdev) - return grub_errno; - - newdev->total_length = grub_disk_get_size (source) - 1; - - err = recover_key (newdev, &header, name, source); - if (err) - { - close (newdev); - return err; - } - - grub_cryptodisk_insert (newdev, name, source); - - have_it = 1; - - return GRUB_ERR_NONE; -} - -#ifdef GRUB_UTIL -grub_err_t -grub_geli_cheat_mount (const char *sourcedev, const char *cheat) -{ - grub_err_t err; - struct grub_geli_phdr header; - grub_cryptodisk_t newdev, dev; - grub_disk_t source; - grub_disk_addr_t sector; - - /* Try to open disk. */ - source = grub_disk_open (sourcedev); - if (!source) - return grub_errno; - - dev = grub_cryptodisk_get_by_source_disk (source); - - if (dev) - { - grub_disk_close (source); - return GRUB_ERR_NONE; - } - - sector = grub_disk_get_size (source); - if (sector == GRUB_DISK_SIZE_UNKNOWN) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli"); - - /* Read the LUKS header. */ - err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); - if (err) - return err; - - newdev = configure_ciphers (&header); - if (!newdev) - { - grub_disk_close (source); - return grub_errno; - } - - newdev->total_length = grub_disk_get_size (source) - 1; - - err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat); - grub_disk_close (source); - if (err) - grub_free (newdev); - - return err; -} -#endif - -static int -grub_geli_scan_device (const char *name) -{ - grub_err_t err; - grub_disk_t source; - - /* Try to open disk. */ - source = grub_disk_open (name); - if (!source) - return grub_errno; - - err = grub_geli_scan_device_real (name, source); - - grub_disk_close (source); - - if (err) - grub_print_error (); - return have_it && check_uuid ? 0 : 1; -} - -#ifdef GRUB_UTIL - -void -grub_util_geli_print_uuid (grub_disk_t disk) -{ - grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; - grub_printf ("%s ", dev->uuid); -} -#endif - -static grub_err_t -grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args) -{ - struct grub_arg_list *state = ctxt->state; - - if (argc < 1 && !state[1].set && !state[2].set) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); - - have_it = 0; - if (state[0].set) - { - grub_cryptodisk_t dev; - - dev = grub_cryptodisk_get_by_uuid (args[0]); - if (dev) - { - grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id); - return GRUB_ERR_NONE; - } - - check_uuid = 1; - check_boot = state[2].set; - search_uuid = args[0]; - grub_device_iterate (&grub_geli_scan_device); - search_uuid = NULL; - - if (!have_it) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); - return GRUB_ERR_NONE; - } - else if (state[1].set || (argc == 0 && state[2].set)) - { - check_uuid = 0; - search_uuid = NULL; - check_boot = state[2].set; - grub_device_iterate (&grub_geli_scan_device); - search_uuid = NULL; - return GRUB_ERR_NONE; - } - else - { - grub_err_t err; - grub_disk_t disk; - grub_cryptodisk_t dev; - - check_uuid = 0; - search_uuid = NULL; - check_boot = state[2].set; - disk = grub_disk_open (args[0]); - if (!disk) - return grub_errno; - - dev = grub_cryptodisk_get_by_source_disk (disk); - if (dev) - { - grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); - grub_disk_close (disk); - return GRUB_ERR_NONE; - } - - err = grub_geli_scan_device_real (args[0], disk); - - grub_disk_close (disk); - - return err; - } -} - -static grub_extcmd_t cmd; +struct grub_cryptodisk_dev geli_crypto = { + .scan = configure_ciphers, + .recover_key = recover_key +}; GRUB_MOD_INIT (geli) { - cmd = grub_register_extcmd ("gelimount", grub_cmd_gelimount, 0, - N_("SOURCE|-u UUID|-a|-b"), - N_("Mount a GELI device."), options); + grub_cryptodisk_dev_register (&geli_crypto); } GRUB_MOD_FINI (geli) { - grub_unregister_extcmd (cmd); + grub_cryptodisk_dev_unregister (&geli_crypto); } diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index aa23e2e35..0ec95fccd 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -64,27 +64,19 @@ gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst, grub_size_t blocksize, grub_size_t blocknumbers); -static const struct grub_arg_option options[] = - { - {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, - {"all", 'a', 0, N_("Mount all."), 0, 0}, - {0, 0, 0, 0, 0, 0} - }; - -static int check_uuid, have_it; -static char *search_uuid; - static grub_cryptodisk_t -configure_ciphers (const struct grub_luks_phdr *header) +configure_ciphers (grub_disk_t disk, const char *check_uuid, + int check_boot) { grub_cryptodisk_t newdev; const char *iptr; + struct grub_luks_phdr header; char *optr; - char uuid[sizeof (header->uuid) + 1]; - char ciphername[sizeof (header->cipherName) + 1]; - char ciphermode[sizeof (header->cipherMode) + 1]; + char uuid[sizeof (header.uuid) + 1]; + char ciphername[sizeof (header.cipherName) + 1]; + char ciphermode[sizeof (header.cipherMode) + 1]; char *cipheriv = NULL; - char hashspec[sizeof (header->hashSpec) + 1]; + char hashspec[sizeof (header.hashSpec) + 1]; grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; grub_crypto_cipher_handle_t essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; @@ -92,14 +84,27 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_cryptodisk_mode_t mode; grub_cryptodisk_mode_iv_t mode_iv; int benbi_log = 0; + grub_err_t err; + + if (check_boot) + return NULL; + + /* Read the LUKS header. */ + err = grub_disk_read (disk, 0, 0, sizeof (header), &header); + if (err) + { + if (err == GRUB_ERR_OUT_OF_RANGE) + grub_errno = GRUB_ERR_NONE; + return NULL; + } /* Look for LUKS magic sequence. */ - if (grub_memcmp (header->magic, LUKS_MAGIC, sizeof (header->magic)) - || grub_be_to_cpu16 (header->version) != 1) + if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) + || grub_be_to_cpu16 (header.version) != 1) return NULL; optr = uuid; - for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)]; + for (iptr = header.uuid; iptr < &header.uuid[ARRAY_SIZE (header.uuid)]; iptr++) { if (*iptr != '-') @@ -107,19 +112,19 @@ configure_ciphers (const struct grub_luks_phdr *header) } *optr = 0; - if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0) + if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0) { - grub_dprintf ("luks", "%s != %s\n", uuid, search_uuid); + grub_dprintf ("luks", "%s != %s\n", uuid, check_uuid); return NULL; } /* Make sure that strings are null terminated. */ - grub_memcpy (ciphername, header->cipherName, sizeof (header->cipherName)); - ciphername[sizeof (header->cipherName)] = 0; - grub_memcpy (ciphermode, header->cipherMode, sizeof (header->cipherMode)); - ciphermode[sizeof (header->cipherMode)] = 0; - grub_memcpy (hashspec, header->hashSpec, sizeof (header->hashSpec)); - hashspec[sizeof (header->hashSpec)] = 0; + grub_memcpy (ciphername, header.cipherName, sizeof (header.cipherName)); + ciphername[sizeof (header.cipherName)] = 0; + grub_memcpy (ciphermode, header.cipherMode, sizeof (header.cipherMode)); + ciphermode[sizeof (header.cipherMode)] = 0; + grub_memcpy (hashspec, header.hashSpec, sizeof (header.hashSpec)); + hashspec[sizeof (header.hashSpec)] = 0; ciph = grub_crypto_lookup_cipher_by_name (ciphername); if (!ciph) @@ -134,10 +139,10 @@ configure_ciphers (const struct grub_luks_phdr *header) if (!cipher) return NULL; - if (grub_be_to_cpu32 (header->keyBytes) > 1024) + if (grub_be_to_cpu32 (header.keyBytes) > 1024) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", - grub_be_to_cpu32 (header->keyBytes)); + grub_be_to_cpu32 (header.keyBytes)); return NULL; } @@ -273,7 +278,7 @@ configure_ciphers (const struct grub_luks_phdr *header) if (!newdev) return NULL; newdev->cipher = cipher; - newdev->offset = grub_be_to_cpu32 (header->payloadOffset); + newdev->offset = grub_be_to_cpu32 (header.payloadOffset); newdev->source_disk = NULL; newdev->benbi_log = benbi_log; newdev->mode = mode; @@ -283,39 +288,54 @@ configure_ciphers (const struct grub_luks_phdr *header) newdev->essiv_hash = essiv_hash; newdev->hash = hash; newdev->log_sector_size = 9; + newdev->total_length = grub_disk_get_size (disk) - newdev->offset; grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); +#ifdef GRUB_UTIL + newdev->modname = "luks"; +#endif COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= sizeof (uuid)); return newdev; } static grub_err_t -luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, - const char *name, grub_disk_t source) +luks_recover_key (grub_disk_t source, + grub_cryptodisk_t dev) { - grub_size_t keysize = grub_be_to_cpu32 (header->keyBytes); - grub_uint8_t candidate_key[keysize]; - grub_uint8_t digest[keysize]; + struct grub_luks_phdr header; + grub_size_t keysize; grub_uint8_t *split_key = NULL; char passphrase[MAX_PASSPHRASE] = ""; - grub_uint8_t candidate_digest[sizeof (header->mkDigest)]; + grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; unsigned i; grub_size_t length; grub_err_t err; grub_size_t max_stripes = 1; + char *tmp; + + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + return err; grub_printf ("Attempting to decrypt master key...\n"); + keysize = grub_be_to_cpu32 (header.keyBytes); - for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) - if (grub_be_to_cpu32 (header->keyblock[i].active) == LUKS_KEY_ENABLED - && grub_be_to_cpu32 (header->keyblock[i].stripes) > max_stripes) - max_stripes = grub_be_to_cpu32 (header->keyblock[i].stripes); + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + if (grub_be_to_cpu32 (header.keyblock[i].active) == LUKS_KEY_ENABLED + && grub_be_to_cpu32 (header.keyblock[i].stripes) > max_stripes) + max_stripes = grub_be_to_cpu32 (header.keyblock[i].stripes); split_key = grub_malloc (keysize * max_stripes); if (!split_key) return grub_errno; /* Get the passphrase from the user. */ - grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid); + tmp = NULL; + if (source->partition) + tmp = grub_partition_get_name (source->partition); + grub_printf ("Enter passphrase for %s%s%s (%s): ", source->name, + source->partition ? "," : "", tmp ? : "", + dev->uuid); + grub_free (tmp); if (!grub_password_get (passphrase, MAX_PASSPHRASE)) { grub_free (split_key); @@ -323,12 +343,14 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, } /* Try to recover master key from each active keyslot. */ - for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) { gcry_err_code_t gcry_err; + grub_uint8_t candidate_key[keysize]; + grub_uint8_t digest[keysize]; /* Check if keyslot is enabled. */ - if (grub_be_to_cpu32 (header->keyblock[i].active) != LUKS_KEY_ENABLED) + if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) continue; grub_dprintf ("luks", "Trying keyslot %d\n", i); @@ -336,9 +358,9 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, /* Calculate the PBKDF2 of the user supplied passphrase. */ gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, grub_strlen (passphrase), - header->keyblock[i].passwordSalt, - sizeof (header->keyblock[i].passwordSalt), - grub_be_to_cpu32 (header->keyblock[i]. + header.keyblock[i].passwordSalt, + sizeof (header.keyblock[i].passwordSalt), + grub_be_to_cpu32 (header.keyblock[i]. passwordIterations), digest, keysize); @@ -357,11 +379,11 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, return grub_crypto_gcry_error (gcry_err); } - length = (keysize * grub_be_to_cpu32 (header->keyblock[i].stripes)); + length = (keysize * grub_be_to_cpu32 (header.keyblock[i].stripes)); /* Read and decrypt the key material from the disk. */ err = grub_disk_read (source, - grub_be_to_cpu32 (header->keyblock + grub_be_to_cpu32 (header.keyblock [i].keyMaterialOffset), 0, length, split_key); if (err) @@ -379,7 +401,7 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, /* Merge the decrypted key material to get the candidate master key. */ gcry_err = AF_merge (dev->hash, split_key, candidate_key, keysize, - grub_be_to_cpu32 (header->keyblock[i].stripes)); + grub_be_to_cpu32 (header.keyblock[i].stripes)); if (gcry_err) { grub_free (split_key); @@ -390,11 +412,11 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, /* Calculate the PBKDF2 of the candidate master key. */ gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key, - grub_be_to_cpu32 (header->keyBytes), - header->mkDigestSalt, - sizeof (header->mkDigestSalt), + grub_be_to_cpu32 (header.keyBytes), + header.mkDigestSalt, + sizeof (header.mkDigestSalt), grub_be_to_cpu32 - (header->mkDigestIterations), + (header.mkDigestIterations), candidate_digest, sizeof (candidate_digest)); if (gcry_err) @@ -405,8 +427,8 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, /* Compare the calculated PBKDF2 to the digest stored in the header to see if it's correct. */ - if (grub_memcmp (candidate_digest, header->mkDigest, - sizeof (header->mkDigest)) != 0) + if (grub_memcmp (candidate_digest, header.mkDigest, + sizeof (header.mkDigest)) != 0) { grub_dprintf ("luks", "bad digest\n"); continue; @@ -430,204 +452,19 @@ luks_recover_key (grub_cryptodisk_t dev, const struct grub_luks_phdr *header, return GRUB_ACCESS_DENIED; } -static void -luks_close (grub_cryptodisk_t luks) -{ - grub_crypto_cipher_close (luks->cipher); - grub_crypto_cipher_close (luks->secondary_cipher); - grub_crypto_cipher_close (luks->essiv_cipher); - grub_free (luks); -} - -static grub_err_t -grub_luks_scan_device_real (const char *name, grub_disk_t source) -{ - grub_err_t err; - struct grub_luks_phdr header; - grub_cryptodisk_t newdev, dev; - - dev = grub_cryptodisk_get_by_source_disk (source); - - if (dev) - return GRUB_ERR_NONE; - - /* Read the LUKS header. */ - err = grub_disk_read (source, 0, 0, sizeof (header), &header); - if (err) - return err; - - newdev = configure_ciphers (&header); - if (!newdev) - return grub_errno; - - newdev->total_length = grub_disk_get_size (source) - newdev->offset; - - err = luks_recover_key (newdev, &header, name, source); - if (err) - { - luks_close (newdev); - return err; - } - - grub_cryptodisk_insert (newdev, name, source); - - have_it = 1; - - return GRUB_ERR_NONE; -} - -#ifdef GRUB_UTIL -grub_err_t -grub_luks_cheat_mount (const char *sourcedev, const char *cheat) -{ - grub_err_t err; - struct grub_luks_phdr header; - grub_cryptodisk_t newdev, dev; - grub_disk_t source; - - /* Try to open disk. */ - source = grub_disk_open (sourcedev); - if (!source) - return grub_errno; - - dev = grub_cryptodisk_get_by_source_disk (source); - - if (dev) - { - grub_disk_close (source); - return GRUB_ERR_NONE; - } - - /* Read the LUKS header. */ - err = grub_disk_read (source, 0, 0, sizeof (header), &header); - if (err) - return err; - - newdev = configure_ciphers (&header); - if (!newdev) - { - grub_disk_close (source); - return grub_errno; - } - - newdev->total_length = grub_disk_get_size (source) - newdev->offset; - - err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat); - grub_disk_close (source); - if (err) - grub_free (newdev); - - return err; -} -#endif - -static int -grub_luks_scan_device (const char *name) -{ - grub_err_t err; - grub_disk_t source; - - /* Try to open disk. */ - source = grub_disk_open (name); - if (!source) - return grub_errno; - - err = grub_luks_scan_device_real (name, source); - - grub_disk_close (source); - - if (err) - grub_print_error (); - return have_it && check_uuid ? 0 : 1; -} - -#ifdef GRUB_UTIL - -void -grub_util_luks_print_uuid (grub_disk_t disk) -{ - grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; - grub_printf ("%s ", dev->uuid); -} -#endif - -static grub_err_t -grub_cmd_luksmount (grub_extcmd_context_t ctxt, int argc, char **args) -{ - struct grub_arg_list *state = ctxt->state; - - if (argc < 1 && !state[1].set) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); - - have_it = 0; - if (state[0].set) - { - grub_cryptodisk_t dev; - - dev = grub_cryptodisk_get_by_uuid (args[0]); - if (dev) - { - grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id); - return GRUB_ERR_NONE; - } - - check_uuid = 1; - search_uuid = args[0]; - grub_device_iterate (&grub_luks_scan_device); - search_uuid = NULL; - - if (!have_it) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found"); - return GRUB_ERR_NONE; - } - else if (state[1].set) - { - check_uuid = 0; - search_uuid = NULL; - grub_device_iterate (&grub_luks_scan_device); - search_uuid = NULL; - return GRUB_ERR_NONE; - } - else - { - grub_err_t err; - grub_disk_t disk; - grub_cryptodisk_t dev; - - check_uuid = 0; - search_uuid = NULL; - disk = grub_disk_open (args[0]); - if (!disk) - return grub_errno; - - dev = grub_cryptodisk_get_by_source_disk (disk); - if (dev) - { - grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id); - grub_disk_close (disk); - return GRUB_ERR_NONE; - } - - err = grub_luks_scan_device_real (args[0], disk); - - grub_disk_close (disk); - - return err; - } -} - -static grub_extcmd_t cmd; +struct grub_cryptodisk_dev luks_crypto = { + .scan = configure_ciphers, + .recover_key = luks_recover_key +}; GRUB_MOD_INIT (luks) { COMPILE_TIME_ASSERT (sizeof (((struct grub_luks_phdr *) 0)->uuid) < GRUB_CRYPTODISK_MAX_UUID_LENGTH); - cmd = grub_register_extcmd ("luksmount", grub_cmd_luksmount, 0, - N_("SOURCE|-u UUID|-a"), - N_("Mount a LUKS device."), options); + grub_cryptodisk_dev_register (&luks_crypto); } GRUB_MOD_FINI (luks) { - grub_unregister_extcmd (cmd); + grub_cryptodisk_dev_unregister (&luks_crypto); } diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c index 642d77cf0..b6e8a673f 100644 --- a/grub-core/kern/emu/getroot.c +++ b/grub-core/kern/emu/getroot.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_DEVICE_MAPPER # include @@ -756,6 +757,94 @@ grub_util_get_dm_abstraction (const char *os_dev) #endif } +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) +#include + +/* FIXME: geom actually gives us the whole container hierarchy. + It can be used more efficiently than this. */ +void +grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out) +{ + struct gmesh mesh; + struct gclass *class; + int error; + struct ggeom *geom; + + grub_util_info ("following geom '%s'", name); + + error = geom_gettree (&mesh); + if (error != 0) + grub_util_error ("couldn't open geom"); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + if (strcasecmp (class->lg_name, "part") == 0) + break; + if (!class) + grub_util_error ("couldn't open geom part"); + + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + char *name_tmp = xstrdup (geom->lg_name); + grub_disk_addr_t off = 0; + struct gconfig *config; + grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name); + + grub_util_follow_gpart_up (name_tmp, &off, name_out); + free (name_tmp); + LIST_FOREACH (config, &provider->lg_config, lg_config) + if (strcasecmp (config->lg_name, "start") == 0) + off += strtoull (config->lg_val, 0, 10); + if (off_out) + *off_out = off; + return; + } + } + grub_util_info ("geom '%s' has no parent", name); + if (name_out) + *name_out = xstrdup (name); + if (off_out) + *off_out = 0; +} + +static const char * +grub_util_get_geom_abstraction (const char *dev) +{ + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int error; + + if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 0; + name = dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + error = geom_gettree (&mesh); + if (error != 0) + grub_util_error ("couldn't open geom"); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + return class->lg_name; + } + } + return NULL; +} +#endif + int grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused))) { @@ -777,6 +866,14 @@ grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused))) return GRUB_DEV_ABSTRACTION_RAID; #endif +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) + const char *abs; + abs = grub_util_get_geom_abstraction (os_dev); + grub_util_info ("abstraction of %s is %s", os_dev, abs); + if (abs && grub_strcasecmp (abs, "eli") == 0) + return GRUB_DEV_ABSTRACTION_GELI; +#endif + /* No abstraction found. */ return GRUB_DEV_ABSTRACTION_NONE; } @@ -869,6 +966,71 @@ grub_util_pull_device (const char *os_dev) ab = grub_util_get_dev_abstraction (os_dev); switch (ab) { + case GRUB_DEV_ABSTRACTION_GELI: + { + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int error; + char *lastsubdev = NULL; + + if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return; + name = os_dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + error = geom_gettree (&mesh); + if (error != 0) + grub_util_error ("couldn't open geom"); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + struct gconsumer *consumer; + char *fname; + char *uuid; + + LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) + break; + if (!consumer) + grub_util_error ("couldn't find geli consumer"); + fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); + grub_util_info ("consumer %s", consumer->lg_provider->lg_name); + lastsubdev = consumer->lg_provider->lg_name; + grub_util_pull_device (fname); + free (fname); + } + } + } + if (ab == GRUB_DEV_ABSTRACTION_GELI && lastsubdev) + { + char *fname = xasprintf ("/dev/%s", lastsubdev); + char *grdev = grub_util_get_grub_dev (fname); + free (fname); + + if (grdev) + { + grub_err_t err; + err = grub_cryptodisk_cheat_mount (grdev, os_dev); + if (err) + grub_util_error ("Can't mount crypto: %s", grub_errmsg); + } + + grub_free (grdev); + } + + } + break; + case GRUB_DEV_ABSTRACTION_LVM: case GRUB_DEV_ABSTRACTION_LUKS: #ifdef HAVE_DEVICE_MAPPER @@ -895,7 +1057,7 @@ grub_util_pull_device (const char *os_dev) grub_util_pull_device (subdev); } } - if (ab == GRUB_DEV_ABSTRACTION_LUKS && lastsubdev) + if (ab == GRUB_DEV_ABSTRACTION_CRYPTO && lastsubdev) { char *grdev = grub_util_get_grub_dev (lastsubdev); dm_tree_free (tree); @@ -904,7 +1066,7 @@ grub_util_pull_device (const char *os_dev) grub_err_t err; err = grub_luks_cheat_mount (grdev, os_dev); if (err) - grub_util_error ("Can't mount LUKS: %s", grub_errmsg); + grub_util_error ("Can't mount crypto: %s", grub_errmsg); } grub_free (grdev); } @@ -959,6 +1121,8 @@ grub_util_get_grub_dev (const char *os_dev) { char *uuid, *dash; uuid = get_dm_uuid (os_dev); + if (!uuid) + break; dash = grub_strchr (uuid + sizeof ("CRYPT-LUKS1-") - 1, '-'); if (dash) *dash = 0; @@ -968,6 +1132,55 @@ grub_util_get_grub_dev (const char *os_dev) } break; + case GRUB_DEV_ABSTRACTION_GELI: + { + char *whole; + struct gmesh mesh; + struct gclass *class; + const char *name; + int error; + + if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) + return 0; + name = os_dev + sizeof ("/dev/") - 1; + grub_util_follow_gpart_up (name, NULL, &whole); + + grub_util_info ("following geom '%s'", name); + + error = geom_gettree (&mesh); + if (error != 0) + grub_util_error ("couldn't open geom"); + + LIST_FOREACH (class, &mesh.lg_class, lg_class) + { + struct ggeom *geom; + LIST_FOREACH (geom, &class->lg_geom, lg_geom) + { + struct gprovider *provider; + LIST_FOREACH (provider, &geom->lg_provider, lg_provider) + if (strcmp (provider->lg_name, name) == 0) + { + struct gconsumer *consumer; + char *fname; + char *uuid; + + LIST_FOREACH (consumer, &geom->lg_consumer, lg_consumer) + break; + if (!consumer) + grub_util_error ("couldn't find geli consumer"); + fname = xasprintf ("/dev/%s", consumer->lg_provider->lg_name); + uuid = grub_util_get_geli_uuid (fname); + if (!uuid) + grub_util_error ("couldn't retrieve geli UUID"); + grub_dev = xasprintf ("cryptouuid/%s", uuid); + free (fname); + free (uuid); + } + } + } + } + break; + case GRUB_DEV_ABSTRACTION_RAID: if (os_dev[7] == '_' && os_dev[8] == 'd') diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c index 97e090222..6cace3746 100644 --- a/grub-core/kern/emu/hostdisk.c +++ b/grub-core/kern/emu/hostdisk.c @@ -106,9 +106,7 @@ struct hd_geometry # include #endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include -#elif defined(__NetBSD__) +#if defined(__NetBSD__) # define HAVE_DIOCGDINFO # include # include /* struct disklabel */ @@ -226,6 +224,82 @@ grub_util_biosdisk_iterate (int (*hook) (const char *name), return 0; } +#if !defined(__MINGW32__) +grub_uint64_t +grub_util_get_fd_sectors (int fd, unsigned *log_secsize) +{ +#if defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) +# if defined(__NetBSD__) + struct disklabel label; +# else + unsigned long long nr; +# endif + unsigned sector_size, log_sector_size; + struct stat st; + + if (fstat (fd, &st) < 0) + grub_util_error ("fstat failed"); + +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) + if (! S_ISCHR (st.st_mode)) +# else + if (! S_ISBLK (st.st_mode)) +# endif + goto fail; + +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + if (ioctl (fd, DIOCGMEDIASIZE, &nr)) +# elif defined(__APPLE__) + if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr)) +# elif defined(__NetBSD__) + configure_device_driver (fd); + if (ioctl (fd, DIOCGDINFO, &label) == -1) +# else + if (ioctl (fd, BLKGETSIZE64, &nr)) +# endif + goto fail; + +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + if (ioctl (fd, DIOCGSECTORSIZE, §or_size)) +# else + if (ioctl (fd, BLKSSZGET, §or_size)) +# endif + goto fail; + + if (sector_size & (sector_size - 1) || !sector_size) + goto fail; + for (log_sector_size = 0; + (1 << log_sector_size) < sector_size; + log_sector_size++); + + if (log_secsize) + *log_secsize = log_sector_size; + +# if defined (__APPLE__) + return nr; +# elif defined(__NetBSD__) + return label.d_secperunit; +# else + if (nr & ((1 << log_sector_size) - 1)) + grub_util_error ("unaligned device size"); + + return (nr >> log_sector_size); +# endif + + fail: + /* In GNU/Hurd, stat() will return the right size. */ +#elif !defined (__GNU__) +# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal." +#endif + + if (log_secsize) + *log_secsize = 9; + + return st.st_size >> 9; +} +#endif + static grub_err_t grub_util_biosdisk_open (const char *name, grub_disk_t disk, grub_disk_pull_t pull __attribute__ ((unused))) @@ -262,90 +336,30 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk, return GRUB_ERR_NONE; } -#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) +#else { -# if defined(__NetBSD__) - struct disklabel label; -# else - unsigned long long nr; -# endif - int sector_size; int fd; fd = open (map[drive].device, O_RDONLY); if (fd == -1) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device); + disk->total_sectors = grub_util_get_fd_sectors (fd, &disk->log_sector_size); + # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) # else if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode)) # endif - { - close (fd); - goto fail; - } - data->is_disk = 1; - -# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - if (ioctl (fd, DIOCGMEDIASIZE, &nr)) -# elif defined(__APPLE__) - if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr)) -# elif defined(__NetBSD__) - configure_device_driver (fd); - if (ioctl (fd, DIOCGDINFO, &label) == -1) -# else - if (ioctl (fd, BLKGETSIZE64, &nr)) -# endif - { - close (fd); - goto fail; - } - - if (ioctl (fd, BLKSSZGET, §or_size)) - { - close (fd); - goto fail; - } + data->is_disk = 1; close (fd); - if (sector_size & (sector_size - 1) || !sector_size) - goto fail; - for (disk->log_sector_size = 0; - (1 << disk->log_sector_size) < sector_size; - disk->log_sector_size++); - -# if defined (__APPLE__) - disk->total_sectors = nr; -# elif defined(__NetBSD__) - disk->total_sectors = label.d_secperunit; -# else - disk->total_sectors = nr >> disk->log_sector_size; - - if (nr & ((1 << disk->log_sector_size) - 1)) - grub_util_error ("unaligned device size"); -# endif - grub_util_info ("the size of %s is %llu", name, disk->total_sectors); return GRUB_ERR_NONE; } - - fail: - /* In GNU/Hurd, stat() will return the right size. */ -#elif !defined (__GNU__) -# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal." #endif - if (stat (map[drive].device, &st) < 0) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device); - - disk->total_sectors = st.st_size >> disk->log_sector_size; - - grub_util_info ("the size of %s is %lu", name, disk->total_sectors); - - return GRUB_ERR_NONE; } int @@ -367,55 +381,6 @@ grub_util_device_is_mapped (const char *dev) } #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) -/* FIXME: geom actually gives us the whole container hierarchy. - It can be used more efficiently than this. */ -static void -follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out) -{ - struct gmesh mesh; - struct gclass *class; - int error; - struct ggeom *geom; - - grub_util_info ("following geom '%s'", name); - - error = geom_gettree (&mesh); - if (error != 0) - grub_util_error ("couldn't open geom"); - - LIST_FOREACH (class, &mesh.lg_class, lg_class) - if (strcasecmp (class->lg_name, "part") == 0) - break; - if (!class) - grub_util_error ("couldn't open geom part"); - - LIST_FOREACH (geom, &class->lg_geom, lg_geom) - { - struct gprovider *provider; - LIST_FOREACH (provider, &geom->lg_provider, lg_provider) - if (strcmp (provider->lg_name, name) == 0) - { - char *name_tmp = xstrdup (geom->lg_name); - grub_disk_addr_t off = 0; - struct gconfig *config; - grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name); - - follow_geom_up (name_tmp, &off, name_out); - free (name_tmp); - LIST_FOREACH (config, &provider->lg_config, lg_config) - if (strcasecmp (config->lg_name, "start") == 0) - off += strtoull (config->lg_val, 0, 10); - if (off_out) - *off_out = off; - return; - } - } - grub_util_info ("geom '%s' has no parent", name); - if (name_out) - *name_out = xstrdup (name); - if (off_out) - *off_out = 0; -} static grub_disk_addr_t find_partition_start (const char *dev) @@ -423,10 +388,11 @@ find_partition_start (const char *dev) grub_disk_addr_t out; if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0) return 0; - follow_geom_up (dev + sizeof ("/dev/") - 1, &out, NULL); + grub_util_follow_gpart_up (dev + sizeof ("/dev/") - 1, &out, NULL); return out; } + #elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) static grub_disk_addr_t find_partition_start (const char *dev) @@ -1508,7 +1474,7 @@ devmapper_out: char *out, *out2; if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0) return xstrdup (os_dev); - follow_geom_up (os_dev + sizeof ("/dev/") - 1, NULL, &out); + grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out); out2 = xasprintf ("/dev/%s", out); free (out); @@ -1667,6 +1633,8 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev) struct stat st; int drive; + grub_util_info ("Looking for %s", os_dev); + if (stat (os_dev, &st) < 0) { grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 169fb119d..c6d1ce8de 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -21,6 +21,7 @@ #include #include +#include typedef enum { @@ -57,6 +58,8 @@ typedef gcry_err_code_t struct grub_cryptodisk { + struct grub_cryptodisk *next; + char *source; grub_disk_addr_t offset; grub_disk_addr_t total_length; @@ -78,6 +81,7 @@ struct grub_cryptodisk grub_size_t iv_prefix_len; #ifdef GRUB_UTIL char *cheat; + const char *modname; int cheat_fd; #endif int log_sector_size; @@ -86,10 +90,37 @@ struct grub_cryptodisk grub_uint8_t rekey_key[64]; grub_uint64_t last_rekey; int rekey_derived_size; - struct grub_cryptodisk *next; }; typedef struct grub_cryptodisk *grub_cryptodisk_t; +struct grub_cryptodisk_dev +{ + struct grub_cryptodisk_dev *next; + + grub_cryptodisk_t (*scan) (grub_disk_t disk, const char *check_uuid, + int boot_only); + grub_err_t (*recover_key) (grub_disk_t disk, grub_cryptodisk_t dev); +}; +typedef struct grub_cryptodisk_dev *grub_cryptodisk_dev_t; + +extern grub_cryptodisk_dev_t EXPORT_VAR (grub_cryptodisk_list); + +#ifndef GRUB_LST_GENERATOR +static inline void +grub_cryptodisk_dev_register (grub_cryptodisk_dev_t cr) +{ + grub_list_push (GRUB_AS_LIST_P (&grub_cryptodisk_list), GRUB_AS_LIST (cr)); +} +#endif + +static inline void +grub_cryptodisk_dev_unregister (grub_cryptodisk_dev_t cr) +{ + grub_list_remove (GRUB_AS_LIST_P (&grub_cryptodisk_list), GRUB_AS_LIST (cr)); +} + +#define FOR_CRYPTODISK_DEVS(var) FOR_LIST_ELEMENTS((var), (grub_cryptodisk_list)) + gcry_err_code_t grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize); @@ -106,6 +137,8 @@ grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name, grub_disk_t source, const char *cheat); void grub_util_cryptodisk_print_abstraction (grub_disk_t disk); +char * +grub_util_get_geli_uuid (const char *dev); #endif grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid); diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 57cd911ef..6921e567c 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -26,6 +26,7 @@ enum grub_dev_abstraction_types { GRUB_DEV_ABSTRACTION_LVM, GRUB_DEV_ABSTRACTION_RAID, GRUB_DEV_ABSTRACTION_LUKS, + GRUB_DEV_ABSTRACTION_GELI, }; char *grub_find_device (const char *dir, dev_t dev); @@ -38,5 +39,9 @@ const char *grub_util_check_char_device (const char *blk_dev); #ifdef __linux__ char **grub_util_raid_getmembers (const char *name, int bootable); #endif +#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) +void grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, + char **name_out); +#endif #endif /* ! GRUB_UTIL_GETROOT_HEADER */ diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index 18191c4eb..719faa29b 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -35,7 +35,11 @@ grub_err_t grub_util_fd_seek (int fd, const char *name, grub_uint64_t sector); ssize_t grub_util_fd_read (int fd, char *buf, size_t len); grub_err_t -grub_luks_cheat_mount (const char *sourcedev, const char *cheat); -void grub_util_luks_print_uuid (grub_disk_t disk); +grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat); +void grub_util_cryptodisk_print_uuid (grub_disk_t disk); +#if !defined(__MINGW32__) +grub_uint64_t +grub_util_get_fd_sectors (int fd, unsigned *log_secsize); +#endif #endif /* ! GRUB_BIOSDISK_MACHINE_UTIL_HEADER */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 48a5be1ca..f5880626c 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -309,10 +309,8 @@ fstest (int n, char **args) char *argv[2] = { "-a", NULL}; if (mount_crypt) { - if (execute_command ("luksmount", 1, argv)) - grub_util_error (_("luksmount command fails: %s"), grub_errmsg); - if (execute_command ("gelimount", 1, argv)) - grub_util_error (_("gelimount command fails: %s"), grub_errmsg); + if (execute_command ("cryptomount", 1, argv)) + grub_util_error (_("cryptomount command fails: %s"), grub_errmsg); } } diff --git a/util/grub-install.in b/util/grub-install.in index 3b3383ab4..049444e93 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -538,9 +538,9 @@ if [ "x${devabstraction_module}" = "x" ] ; then exit 1 fi - if [ x$GRUB_LUKS_ENABLE = xy ]; then - for uuid in "`"${grub_probe}" --device "${device}" --target=luks_uuid`"; do - echo "luksmount -u $uuid" + if [ x$GRUB_CRYPTODISK_ENABLE = xy ]; then + for uuid in "`"${grub_probe}" --device "${device}" --target=cryptodisk_uuid`"; do + echo "cryptomount -u $uuid" done fi diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 41e68bc1b..ade7a558d 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -254,7 +254,7 @@ export GRUB_DEFAULT \ GRUB_DISABLE_OS_PROBER \ GRUB_INIT_TUNE \ GRUB_SAVEDEFAULT \ - GRUB_ENABLE_LUKS \ + GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM if test "x${grub_cfg}" != "x"; then diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 1664b6bbe..33ee7e432 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -69,12 +69,12 @@ is_path_readable_by_grub () return 1 fi - if [ x$GRUB_LUKS_ENABLE = xy ]; then + if [ x$GRUB_CRYPTODISK_ENABLE = xy ]; then return 0 fi for abstraction in $abstractions; do - if [ "x$abstraction" = xluks ]; then + if [ "x$abstraction" = xcryptodisk ]; then return 1 fi done @@ -138,9 +138,9 @@ prepare_grub_to_access_device () echo "insmod ${module}" done - if [ x$GRUB_LUKS_ENABLE = xy ]; then - for uuid in "`"${grub_probe}" --device "${device}" --target=luks_uuid`"; do - echo "luksmount -u $uuid" + if [ x$GRUB_CRYPTODISK_ENABLE = xy ]; then + for uuid in "`"${grub_probe}" --device "${device}" --target=cryptodisk_uuid`"; do + echo "cryptomount -u $uuid" done fi diff --git a/util/grub-probe.c b/util/grub-probe.c index f5d93ac4e..7c0a75123 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -56,7 +56,7 @@ enum { PRINT_DEVICE, PRINT_PARTMAP, PRINT_ABSTRACTION, - PRINT_LUKS_UUID + PRINT_CRYPTODISK_UUID }; int print = PRINT_FS; @@ -91,7 +91,7 @@ probe_partmap (grub_disk_t disk) } static void -probe_luks_uuid (grub_disk_t disk) +probe_cryptodisk_uuid (grub_disk_t disk) { grub_disk_memberlist_t list = NULL, tmp; @@ -102,14 +102,13 @@ probe_luks_uuid (grub_disk_t disk) } while (list) { - probe_luks_uuid (list->disk); + probe_cryptodisk_uuid (list->disk); tmp = list->next; free (list); list = tmp; } - /* FIXME: support non-LUKS. */ if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) - grub_util_luks_print_uuid (disk); + grub_util_cryptodisk_print_uuid (disk); } static int @@ -215,9 +214,9 @@ probe (const char *path, char *device_name) goto end; } - if (print == PRINT_LUKS_UUID) + if (print == PRINT_CRYPTODISK_UUID) { - probe_luks_uuid (dev->disk); + probe_cryptodisk_uuid (dev->disk); printf ("\n"); goto end; } @@ -295,8 +294,8 @@ Probe device information for a given path (or device, if the -d option is given) \n\ -d, --device given argument is a system device, not a path\n\ -m, --device-map=FILE use FILE as the device map [default=%s]\n\ - -t, --target=(fs|fs_uuid|fs_label|drive|device|partmap|abstraction|luks_uuid)\n\ - print filesystem module, GRUB drive, system device, partition map module, abstraction module or LUKS UUID [default=fs]\n\ + -t, --target=(fs|fs_uuid|fs_label|drive|device|partmap|abstraction|cryptodisk_uuid)\n\ + print filesystem module, GRUB drive, system device, partition map module, abstraction module or CRYPTO UUID [default=fs]\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -354,8 +353,8 @@ main (int argc, char *argv[]) print = PRINT_PARTMAP; else if (!strcmp (optarg, "abstraction")) print = PRINT_ABSTRACTION; - else if (!strcmp (optarg, "luks_uuid")) - print = PRINT_LUKS_UUID; + else if (!strcmp (optarg, "cryptodisk_uuid")) + print = PRINT_CRYPTODISK_UUID; else usage (1); break;