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;