Here's a compile-tested patch that attempts to add '-p FILE' support to cryptomount, so that the passphrase can be read from a usb key or somesuch.
I was unsure about how much to massage or verify the file, say, dropping a trailing newline or truncating the passphrase at the first newline or whatever. I ended up deciding to keep it simple and just use the file contents unchanged in any way (though the passphrase users will use it as a C string, so any NUL in the file will effectively truncate the passphrase). Thoughts? I've only checked that it builds, but I haven't really tested it yet I'm not sure yet how to go about that without risking rendering my boxes unbootable :-) Suggestions are welcome. Thanks,
Index: ChangeLog from Alexandre Oliva <ol...@gnu.org> * docs/grub.texi (cryptomount): Add -p file. * grub-core/disk/cryptodisk.c (options): Likewise. (grub_cryptodisk_scan_device_real): Add key parameter. Pass it to recover_key. (grub_cryptodisk_scan_device): Likewise. (grub_cmd_cryptomount): Read key from file. Pass it on. (GRUB_MOD_INIT): Adjust cryptomount usage. * include/grub/cryptodisk.h (GRUB_CRYPTODISK_MAX_PASSPHRASE): New. (grub_cyrptodisk_dev::recover_key): Add key parameter. * grub-core/disk/geli.c (MAX_PASSPHRASE): Define to GRUB_CRYPTODISK_MAX_PASSPHRASE. (recover_key): Add key parameter; try it first if given, retry with keyboard-read passphrase. * grub-core/disk/luks.c (MAX_PASSPHRASE, luks_recover_key): Likewise. Index: docs/grub.texi --- grub.orig/docs/grub.texi 2014-10-14 23:30:59.000000000 -0300 +++ grub/docs/grub.texi 2015-01-18 01:26:49.499924206 -0200 @@ -4073,9 +4073,12 @@ Alias for @code{hashsum --hash crc32 arg @node cryptomount @subsection cryptomount -@deffn Command cryptomount device|@option{-u} uuid|@option{-a}|@option{-b} -Setup access to encrypted device. If necessary, passphrase -is requested interactively. Option @var{device} configures specific grub device +@deffn Command cryptomount [@option{-p} file] device|@option{-u} uuid|@option{-a}|@option{-b} +Setup access to encrypted device. The passphrase will be asked +interactively, unless @option{-p} @var{file} is specified and the +passphrase read from the file succeeds. The file should contain +@emph{only} the passphrase, not even a trailing line break. Option +@var{device} configures specific grub device (@pxref{Naming convention}); option @option{-u} @var{uuid} configures device with specified @var{uuid}; option @option{-a} configures all detected encrypted devices; option @option{-b} configures all geli containers that have boot flag set. Index: grub-core/disk/cryptodisk.c --- grub.orig/grub-core/disk/cryptodisk.c 2014-10-14 23:30:57.000000000 -0300 +++ grub/grub-core/disk/cryptodisk.c 2015-01-18 01:08:27.506796052 -0200 @@ -40,6 +40,8 @@ static const struct grub_arg_option opti /* TRANSLATORS: It's still restricted to cryptodisks only. */ {"all", 'a', 0, N_("Mount all."), 0, 0}, {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0}, + {"passfile", 'p', 0, N_("Read the passphrase from the named file."), + N_("FILE"), ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; @@ -806,7 +808,8 @@ cryptodisk_close (grub_cryptodisk_t dev) } static grub_err_t -grub_cryptodisk_scan_device_real (const char *name, grub_disk_t source) +grub_cryptodisk_scan_device_real (const char *name, grub_disk_t source, + const char *key) { grub_err_t err; grub_cryptodisk_t dev; @@ -825,7 +828,7 @@ grub_cryptodisk_scan_device_real (const if (!dev) continue; - err = cr->recover_key (source, dev); + err = cr->recover_key (source, dev, key); if (err) { cryptodisk_close (dev); @@ -890,10 +893,11 @@ grub_cryptodisk_cheat_mount (const char static int grub_cryptodisk_scan_device (const char *name, - void *data __attribute__ ((unused))) + void *data) { grub_err_t err; grub_disk_t source; + const char *key = data; /* Try to open disk. */ source = grub_disk_open (name); @@ -903,7 +907,7 @@ grub_cryptodisk_scan_device (const char return 0; } - err = grub_cryptodisk_scan_device_real (name, source); + err = grub_cryptodisk_scan_device_real (name, source, key); grub_disk_close (source); @@ -916,10 +920,49 @@ static grub_err_t grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { struct grub_arg_list *state = ctxt->state; + char *key = NULL; + char buf[GRUB_CRYPTODISK_MAX_PASSPHRASE]; if (argc < 1 && !state[1].set && !state[2].set) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + /* Read key from file. */ + if (ctxt->state[3].set) + { + char *fname = ctxt->state[3].arg; + grub_file_t file; + file = grub_file_open (fname); + if (!file) + grub_printf_ (N_("Failed to open passphrase file\n")); + else if (file) + { + grub_ssize_t size; + size = grub_file_read (file, buf, sizeof (buf)); + if (size < 0) + grub_printf_ (N_("Error reading from passphrase file\n")); + else if (size == 0) + grub_printf_ (N_("Passphrase file is empty\n")); + else if ((grub_size_t)size >= sizeof (buf)) + grub_printf_ (N_("Passphrase file is too big\n")); + else + { +#if 0 + int count; + for (count = 0; count < size; count++) + if (buf[count] == 0 + || buf[count] == '\n' + || buf[count] == '\r') + break; + if (count != size) + grub_printf_ (N_("Extraneous byte in passphrase file!\n")); +#endif + grub_memset (buf + size, 0, sizeof (buf) - size); + key = buf; + } + } + grub_file_close (file); + } + have_it = 0; if (state[0].set) { @@ -935,7 +978,7 @@ grub_cmd_cryptomount (grub_extcmd_contex check_boot = state[2].set; search_uuid = args[0]; - grub_device_iterate (&grub_cryptodisk_scan_device, NULL); + grub_device_iterate (&grub_cryptodisk_scan_device, key); search_uuid = NULL; if (!have_it) @@ -946,7 +989,7 @@ grub_cmd_cryptomount (grub_extcmd_contex { search_uuid = NULL; check_boot = state[2].set; - grub_device_iterate (&grub_cryptodisk_scan_device, NULL); + grub_device_iterate (&grub_cryptodisk_scan_device, key); search_uuid = NULL; return GRUB_ERR_NONE; } @@ -980,7 +1023,7 @@ grub_cmd_cryptomount (grub_extcmd_contex return GRUB_ERR_NONE; } - err = grub_cryptodisk_scan_device_real (args[0], disk); + err = grub_cryptodisk_scan_device_real (args[0], disk, key); grub_disk_close (disk); @@ -1117,7 +1160,7 @@ 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_("[-p FILE] SOURCE|-u UUID|-a|-b"), N_("Mount a crypto device."), options); grub_procfs_register ("luks_script", &luks_script); } Index: include/grub/cryptodisk.h --- grub.orig/include/grub/cryptodisk.h 2014-10-14 23:31:00.000000000 -0300 +++ grub/include/grub/cryptodisk.h 2015-01-18 00:14:22.572709178 -0200 @@ -54,6 +54,10 @@ typedef enum #define GRUB_CRYPTODISK_GF_BYTES (1U << GRUB_CRYPTODISK_GF_LOG_BYTES) #define GRUB_CRYPTODISK_MAX_KEYLEN 128 +/* Backends may limit passphrases to smaller sizes, but this should be + the max among them all. */ +#define GRUB_CRYPTODISK_MAX_PASSPHRASE 256 + struct grub_cryptodisk; typedef gcry_err_code_t @@ -107,7 +111,8 @@ struct grub_cryptodisk_dev 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); + grub_err_t (*recover_key) (grub_disk_t disk, grub_cryptodisk_t dev, + const char *key); }; typedef struct grub_cryptodisk_dev *grub_cryptodisk_dev_t; Index: grub-core/disk/geli.c --- grub.orig/grub-core/disk/geli.c 2014-10-14 23:30:57.000000000 -0300 +++ grub/grub-core/disk/geli.c 2015-01-18 01:06:31.422050391 -0200 @@ -135,7 +135,7 @@ const char *algorithms[] = { [0x16] = "aes" }; -#define MAX_PASSPHRASE 256 +#define MAX_PASSPHRASE GRUB_CRYPTODISK_MAX_PASSPHRASE static gcry_err_code_t geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno) @@ -384,7 +384,7 @@ configure_ciphers (grub_disk_t disk, con } static grub_err_t -recover_key (grub_disk_t source, grub_cryptodisk_t dev) +recover_key (grub_disk_t source, grub_cryptodisk_t dev, const char *key) { grub_size_t keysize; grub_uint8_t digest[GRUB_CRYPTO_MAX_MDLEN]; @@ -419,16 +419,31 @@ recover_key (grub_disk_t source, grub_cr grub_puts_ (N_("Attempting to decrypt master key...")); - /* Get the passphrase from the user. */ + retry: tmp = NULL; - if (source->partition) - tmp = grub_partition_get_name (source->partition); - grub_printf_ (N_("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"); + if (key && grub_strlen (key) >= MAX_PASSPHRASE) + { + grub_printf_ (N_("Discarding too-long supplied passphrase\n")); + key = NULL; + } + if (key) + { + grub_size_t len = grub_strlen (key); + grub_memcpy (passphrase, key, len); + grub_memset (passphrase + len, 0, sizeof (passphrase) - len); + } + else + { + /* Get the passphrase from the user. */ + if (source->partition) + tmp = grub_partition_get_name (source->partition); + grub_printf_ (N_("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) @@ -548,6 +563,13 @@ recover_key (grub_disk_t source, grub_cr return GRUB_ERR_NONE; } + if (key) + { + grub_printf_ (N_("Supplied passphrase failed to unlock key\n")); + key = NULL; + goto retry; + } + return GRUB_ACCESS_DENIED; } Index: grub-core/disk/luks.c --- grub.orig/grub-core/disk/luks.c 2014-10-14 23:30:57.000000000 -0300 +++ grub/grub-core/disk/luks.c 2015-01-18 01:06:26.336192968 -0200 @@ -29,7 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); -#define MAX_PASSPHRASE 256 +#define MAX_PASSPHRASE GRUB_CRYPTODISK_MAX_PASSPHRASE #define LUKS_KEY_ENABLED 0x00AC71F3 @@ -297,7 +297,8 @@ configure_ciphers (grub_disk_t disk, con static grub_err_t luks_recover_key (grub_disk_t source, - grub_cryptodisk_t dev) + grub_cryptodisk_t dev, + const char *key) { struct grub_luks_phdr header; grub_size_t keysize; @@ -328,18 +329,33 @@ luks_recover_key (grub_disk_t source, if (!split_key) return grub_errno; - /* Get the passphrase from the user. */ + retry: tmp = NULL; - if (source->partition) - tmp = grub_partition_get_name (source->partition); - grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, - source->partition ? "," : "", tmp ? : "", - dev->uuid); - grub_free (tmp); - if (!grub_password_get (passphrase, MAX_PASSPHRASE)) + if (key && grub_strlen (key) >= MAX_PASSPHRASE) { - grub_free (split_key); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + grub_printf_ (N_("Discarding too-long supplied passphrase\n")); + key = NULL; + } + if (key) + { + grub_size_t len = grub_strlen (key); + grub_memcpy (passphrase, key, len); + grub_memset (passphrase + len, 0, sizeof (passphrase) - len); + } + else + { + /* Get the passphrase from the user. */ + if (source->partition) + tmp = grub_partition_get_name (source->partition); + grub_printf_ (N_("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); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + } } /* Try to recover master key from each active keyslot. */ @@ -451,6 +467,13 @@ luks_recover_key (grub_disk_t source, return GRUB_ERR_NONE; } + if (key) + { + grub_printf_ (N_("Supplied passphrase failed to unlock key\n")); + key = NULL; + goto retry; + } + return GRUB_ACCESS_DENIED; }
-- Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ You must be the change you wish to see in the world. -- Gandhi Be Free! -- http://FSFLA.org/ FSF Latin America board member Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel