All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandre Oliva <oliva@gnu.org>
To: grub-devel@gnu.org
Subject: enable cryptomount to read passphrase from file
Date: Sun, 18 Jan 2015 01:29:45 -0200	[thread overview]
Message-ID: <orfvb8kbpy.fsf@livre.home> (raw)

[-- Attachment #1: Type: text/plain, Size: 723 bytes --]

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,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: grub-cryptomount-passphrase-from-file.patch --]
[-- Type: text/x-patch, Size: 11949 bytes --]

Index: ChangeLog
from  Alexandre Oliva  <oliva@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;
 }
 

[-- Attachment #3: Type: text/plain, Size: 257 bytes --]


-- 
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

             reply	other threads:[~2015-01-18  7:28 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-18  3:29 Alexandre Oliva [this message]
2015-01-18 14:57 ` enable cryptomount to read passphrase from file Andrei Borzenkov
2015-01-19  6:33   ` Alexandre Oliva

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=orfvb8kbpy.fsf@livre.home \
    --to=oliva@gnu.org \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.