--- /dev/null
- grub_luks_iterate (int (*hook) (const char *name))
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/disk.h>
+#include <grub/crypto.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+
+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;
+ char 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 *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;
+ unsigned long id, source_id;
+ enum grub_disk_dev_id source_dev_id;
+ char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1];
+ 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 *search_uuid;
+
+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_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;
+
+ /* 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;
+
+ 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)
+ 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)
+ return grub_errno;
+
+ keysize = grub_be_to_cpu32 (header.keyBytes);
+ if (keysize > 1024)
+ 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);
+ 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);
+ 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;
+ }
+ }
+ else
+ {
+ grub_crypto_cipher_close (cipher);
+ 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);
+ 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);
+ return grub_errno;
+ }
+
+ /* Get the passphrase from the user. */
+ grub_printf ("Enter passphrase for %s (%s): ", name, 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++)
+ {
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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_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);
+ 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, 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);
+ 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 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;
+}
+
+static int
- grub_luks_open (const char *name, grub_disk_t disk)
++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");
+
+ 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->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)
+ {
+ grub_luks_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;
+ }
+
+ 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
+ {
+ grub_err_t err;
+ grub_disk_t disk;
+ grub_luks_t dev;
+
+ check_uuid = 0;
+ search_uuid = NULL;
+ 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 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 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 ();
+}
return os_dev;
}
-static int
-grub_util_is_lvm (const char *os_dev)
+ #ifdef HAVE_DEVICE_MAPPER
+
+ static int
+ grub_util_open_dm (const char *os_dev, struct dm_tree **tree,
+ struct dm_tree_node **node)
+ {
+ uint32_t maj, min;
+ struct stat st;
+
+ *node = NULL;
+ *tree = NULL;
+
+ if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
+ return 0;
+
+ if (stat (os_dev, &st) < 0)
+ return 0;
+
+ *tree = dm_tree_create ();
+ if (! *tree)
+ {
+ grub_printf ("Failed to create tree\n");
+ grub_dprintf ("hostdisk", "dm_tree_create failed\n");
+ return 0;
+ }
+
+ maj = major (st.st_rdev);
+ min = minor (st.st_rdev);
+
+ if (! dm_tree_add_dev (*tree, maj, min))
+ {
+ grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
+ dm_tree_free (*tree);
+ *tree = NULL;
+ return 0;
+ }
+
+ *node = dm_tree_find_node (*tree, maj, min);
+ if (! *node)
+ {
+ grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
+ dm_tree_free (*tree);
+ *tree = NULL;
+ return 0;
+ }
+ return 1;
+ }
+
+ #endif
+
+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
{
- const char *node_uuid;
struct dm_tree *tree;
- uint32_t maj, min;
- struct dm_tree_node *node = NULL;
+ struct dm_tree_node *node;
+ const char *node_uuid;
+ char *ret;
- struct stat st;
- if (stat (os_dev, &st) < 0)
+ if (!grub_util_open_dm (os_dev, &tree, &node))
- 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 NULL;
- }
-
- maj = major (st.st_rdev);
- min = minor (st.st_rdev);
-
- if (! dm_tree_add_dev (tree, maj, min))
- {
- grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
- dm_tree_free (tree);
- return NULL;
- }
-
- node = dm_tree_find_node (tree, maj, min);
- if (! node)
- {
- grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
- dm_tree_free (tree);
- 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
++#endif
++
+ return NULL;
- #endif /* HAVE_DEVICE_MAPPER */
+}
+
+static enum grub_dev_abstraction_types
+grub_util_get_dm_abstraction (const char *os_dev)
+{
++#ifdef HAVE_DEVICE_MAPPER
+ 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;
+ #else
- return 1;
-#endif /* HAVE_DEVICE_MAPPER */
++ if ((strncmp ("/dev/mapper/", os_dev, 12) != 0))
++ return GRUB_DEV_ABSTRACTION_NONE;
++ return GRUB_DEV_ABSTRACTION_LVM;
++#endif
}
int
}
#endif /* __linux__ */
+ void
+ grub_util_pull_device (const char *os_dev)
+ {
+ switch (grub_util_get_dev_abstraction (os_dev))
+ {
+ case GRUB_DEV_ABSTRACTION_LVM:
++ case GRUB_DEV_ABSTRACTION_LUKS:
+ #ifdef HAVE_DEVICE_MAPPER
+ {
+ struct dm_tree *tree;
+ struct dm_tree_node *node;
+ struct dm_tree_node *child;
+ void *handle = NULL;
+
+ if (!grub_util_open_dm (os_dev, &tree, &node))
+ return;
+
+ while ((child = dm_tree_next_child (&handle, node, 0)))
+ {
+ const struct dm_info *dm = dm_tree_node_get_info (child);
+ char *subdev;
+ if (!dm)
+ continue;
+ subdev = grub_find_device ("/dev", makedev (dm->major, dm->minor));
+ if (subdev)
+ grub_util_pull_device (subdev);
+ }
+ dm_tree_free (tree);
+ }
+ #endif
+ return;
+ case GRUB_DEV_ABSTRACTION_RAID:
+ #ifdef __linux__
+ {
+ char **devicelist = grub_util_raid_getmembers (os_dev, 0);
+ int i;
+ for (i = 0; devicelist[i];i++)
+ grub_util_pull_device (devicelist[i]);
+ free (devicelist);
+ }
+ #endif
+ return;
+
+ default: /* GRUB_DEV_ABSTRACTION_NONE */
+ grub_util_biosdisk_get_grub_dev (os_dev);
+ return;
+ }
+ }
+
char *
grub_util_get_grub_dev (const char *os_dev)
{