fish: '-i' option automatically handles whole-disk encryption.
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 5 Nov 2010 11:39:24 +0000 (11:39 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 5 Nov 2010 11:39:24 +0000 (11:39 +0000)
This feature is also available in guestmount because of the
shared option parsing code.

You don't need to do anything to enable it, just using -i
will attempt decryption of encrypted partitions.

Only works for simple Fedora whole-disk encryption.  It's a
work-in-progress to make it work for other types of encryption.

fish/Makefile.am
fish/fish.c
fish/fish.h
fish/inspect.c
fish/keys.c [new file with mode: 0644]
fish/options.h
fuse/Makefile.am
fuse/guestmount.c
fuse/guestmount.pod
generator/generator_fish.ml
po/POTFILES.in

index 61b6346..dadda91 100644 (file)
@@ -46,6 +46,7 @@ EXTRA_DIST = \
 # other guestfish files.
 SHARED_SOURCE_FILES = \
        inspect.c \
 # other guestfish files.
 SHARED_SOURCE_FILES = \
        inspect.c \
+       keys.c \
        options.h \
        options.c \
        virt.c
        options.h \
        options.c \
        virt.c
index 54fd270..d9a92dd 100644 (file)
@@ -30,7 +30,6 @@
 #include <sys/wait.h>
 #include <locale.h>
 #include <langinfo.h>
 #include <sys/wait.h>
 #include <locale.h>
 #include <langinfo.h>
-#include <termios.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
@@ -1480,66 +1479,3 @@ file_out (const char *arg)
   }
   return ret;
 }
   }
   return ret;
 }
-
-/* Read a passphrase ('Key') from /dev/tty with echo off.
- * The caller (cmds.c) will call free on the string afterwards.
- * Based on the code in cryptsetup file lib/utils.c.
- */
-char *
-read_key (const char *param)
-{
-  FILE *infp, *outfp;
-  struct termios orig, temp;
-  char *ret = NULL;
-
-  /* Read and write to /dev/tty if available. */
-  if (keys_from_stdin ||
-      (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
-    infp = stdin;
-    outfp = stdout;
-  }
-
-  /* Print the prompt and set no echo. */
-  int tty = isatty (fileno (infp));
-  int tcset = 0;
-  if (tty) {
-    fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
-
-    if (!echo_keys) {
-      if (tcgetattr (fileno (infp), &orig) == -1) {
-        perror ("tcgetattr");
-        goto error;
-      }
-      memcpy (&temp, &orig, sizeof temp);
-      temp.c_lflag &= ~ECHO;
-
-      tcsetattr (fileno (infp), TCSAFLUSH, &temp);
-      tcset = 1;
-    }
-  }
-
-  size_t n = 0;
-  ssize_t len;
-  len = getline (&ret, &n, infp);
-  if (len == -1) {
-    perror ("getline");
-    ret = NULL;
-    goto error;
-  }
-
-  /* Remove the terminating \n if there is one. */
-  if (len > 0 && ret[len-1] == '\n')
-    ret[len-1] = '\0';
-
- error:
-  /* Restore echo, close file descriptor. */
-  if (tty && tcset) {
-    printf ("\n");
-    tcsetattr (fileno (infp), TCSAFLUSH, &orig);
-  }
-
-  if (infp != stdin)
-    fclose (infp); /* outfp == infp, so this is closed also */
-
-  return ret;
-}
index a3f7caf..8fedf5d 100644 (file)
@@ -78,7 +78,6 @@ extern char *file_in (const char *arg);
 extern void free_file_in (char *s);
 extern char *file_out (const char *arg);
 extern void extended_help_message (void);
 extern void free_file_in (char *s);
 extern char *file_out (const char *arg);
 extern void extended_help_message (void);
-extern char *read_key (const char *param);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
index 8e56553..cc3916b 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
 #include <stdlib.h>
 #include <string.h>
 
+#include "c-ctype.h"
+
 #include "guestfs.h"
 
 #include "options.h"
 
 #include "guestfs.h"
 
 #include "options.h"
 
+static void do_decrypt (void);
+
 /* Global that saves the root device between inspect_mount and
  * print_inspect_prompt.
  */
 /* Global that saves the root device between inspect_mount and
  * print_inspect_prompt.
  */
@@ -71,6 +75,8 @@ compare_keys (const void *p1, const void *p2)
 void
 inspect_mount (void)
 {
 void
 inspect_mount (void)
 {
+  do_decrypt ();
+
   char **roots = guestfs_inspect_os (g);
   if (roots == NULL)
     exit (EXIT_FAILURE);
   char **roots = guestfs_inspect_os (g);
   if (roots == NULL)
     exit (EXIT_FAILURE);
@@ -139,3 +145,73 @@ print_inspect_prompt (void)
 
   free_strings (mountpoints);
 }
 
   free_strings (mountpoints);
 }
+
+/* Make a LUKS map name from the partition name,
+ * eg "/dev/vda2" => "luksvda2"
+ */
+static void
+make_mapname (const char *device, char *mapname, size_t len)
+{
+  size_t i = 0;
+
+  if (len < 5)
+    abort ();
+  strcpy (mapname, "luks");
+  mapname += 4;
+  len -= 4;
+
+  if (STRPREFIX (device, "/dev/"))
+    i = 5;
+
+  for (; device[i] != '\0' && len >= 1; ++i) {
+    if (c_isalnum (device[i])) {
+      *mapname++ = device[i];
+      len--;
+    }
+  }
+
+  *mapname = '\0';
+}
+
+/* Simple implementation of decryption: look for any crypto_LUKS
+ * partitions and decrypt them, then rescan for VGs.  This only works
+ * for Fedora whole-disk encryption.  WIP to make this work for other
+ * encryption schemes.
+ */
+static void
+do_decrypt (void)
+{
+  char **partitions = guestfs_list_partitions (g);
+  if (partitions == NULL)
+    exit (EXIT_FAILURE);
+
+  int need_rescan = 0;
+  size_t i;
+  for (i = 0; partitions[i] != NULL; ++i) {
+    char *type = guestfs_vfs_type (g, partitions[i]);
+    if (type && STREQ (type, "crypto_LUKS")) {
+      char mapname[32];
+      make_mapname (partitions[i], mapname, sizeof mapname);
+
+      char *key = read_key (partitions[i]);
+      /* XXX Should we call guestfs_luks_open_ro if readonly flag
+       * is set?  This might break 'mount_ro'.
+       */
+      if (guestfs_luks_open (g, partitions[i], key, mapname) == -1)
+        exit (EXIT_FAILURE);
+
+      free (key);
+
+      need_rescan = 1;
+    }
+  }
+
+  free_strings (partitions);
+
+  if (need_rescan) {
+    if (guestfs_vgscan (g) == -1)
+      exit (EXIT_FAILURE);
+    if (guestfs_vg_activate_all (g, 1) == -1)
+      exit (EXIT_FAILURE);
+  }
+}
diff --git a/fish/keys.c b/fish/keys.c
new file mode 100644 (file)
index 0000000..deb627f
--- /dev/null
@@ -0,0 +1,91 @@
+/* libguestfs - guestfish and guestmount shared option parsing
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "guestfs.h"
+
+#include "options.h"
+
+/* Read a passphrase ('Key') from /dev/tty with echo off.
+ * The caller (cmds.c) will call free on the string afterwards.
+ * Based on the code in cryptsetup file lib/utils.c.
+ */
+char *
+read_key (const char *param)
+{
+  FILE *infp, *outfp;
+  struct termios orig, temp;
+  char *ret = NULL;
+
+  /* Read and write to /dev/tty if available. */
+  if (keys_from_stdin ||
+      (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
+    infp = stdin;
+    outfp = stdout;
+  }
+
+  /* Print the prompt and set no echo. */
+  int tty = isatty (fileno (infp));
+  int tcset = 0;
+  if (tty) {
+    fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
+
+    if (!echo_keys) {
+      if (tcgetattr (fileno (infp), &orig) == -1) {
+        perror ("tcgetattr");
+        goto error;
+      }
+      memcpy (&temp, &orig, sizeof temp);
+      temp.c_lflag &= ~ECHO;
+
+      tcsetattr (fileno (infp), TCSAFLUSH, &temp);
+      tcset = 1;
+    }
+  }
+
+  size_t n = 0;
+  ssize_t len;
+  len = getline (&ret, &n, infp);
+  if (len == -1) {
+    perror ("getline");
+    ret = NULL;
+    goto error;
+  }
+
+  /* Remove the terminating \n if there is one. */
+  if (len > 0 && ret[len-1] == '\n')
+    ret[len-1] = '\0';
+
+ error:
+  /* Restore echo, close file descriptor. */
+  if (tty && tcset) {
+    printf ("\n");
+    tcsetattr (fileno (infp), TCSAFLUSH, &orig);
+  }
+
+  if (infp != stdin)
+    fclose (infp); /* outfp == infp, so this is closed also */
+
+  return ret;
+}
index b0bbdeb..e36c57a 100644 (file)
@@ -69,6 +69,8 @@ extern guestfs_h *g;
 extern int read_only;
 extern int verbose;
 extern int inspector;
 extern int read_only;
 extern int verbose;
 extern int inspector;
+extern int keys_from_stdin;
+extern int echo_keys;
 extern const char *libvirt_uri;
 extern const char *program_name;
 
 extern const char *libvirt_uri;
 extern const char *program_name;
 
@@ -103,6 +105,9 @@ struct mp {
 extern void inspect_mount (void);
 extern void print_inspect_prompt (void);
 
 extern void inspect_mount (void);
 extern void print_inspect_prompt (void);
 
+/* in key.c */
+extern char *read_key (const char *param);
+
 /* in options.c */
 extern char add_drives (struct drv *drv, char next_drive);
 extern void mount_mps (struct mp *mp);
 /* in options.c */
 extern char add_drives (struct drv *drv, char next_drive);
 extern void mount_mps (struct mp *mp);
index f6f662a..ab63584 100644 (file)
@@ -27,6 +27,7 @@ bin_PROGRAMS = guestmount
 # between guestfish and guestmount.
 SHARED_SOURCE_FILES = \
        ../fish/inspect.c \
 # between guestfish and guestmount.
 SHARED_SOURCE_FILES = \
        ../fish/inspect.c \
+       ../fish/keys.c \
        ../fish/options.h \
        ../fish/options.c \
        ../fish/virt.c
        ../fish/options.h \
        ../fish/options.c \
        ../fish/virt.c
index a32da6b..55b71d7 100644 (file)
@@ -61,6 +61,8 @@ guestfs_h *g = NULL;
 int read_only = 0;
 int verbose = 0;
 int inspector = 0;
 int read_only = 0;
 int verbose = 0;
 int inspector = 0;
+int keys_from_stdin = 0;
+int echo_keys = 0;
 const char *libvirt_uri;
 int dir_cache_timeout = 60;
 
 const char *libvirt_uri;
 int dir_cache_timeout = 60;
 
@@ -850,10 +852,12 @@ usage (int status)
              "  -c|--connect uri     Specify libvirt URI for -d option\n"
              "  --dir-cache-timeout  Set readdir cache timeout (default 5 sec)\n"
              "  -d|--domain guest    Add disks from libvirt guest\n"
              "  -c|--connect uri     Specify libvirt URI for -d option\n"
              "  --dir-cache-timeout  Set readdir cache timeout (default 5 sec)\n"
              "  -d|--domain guest    Add disks from libvirt guest\n"
+             "  --echo-keys          Don't turn off echo for passphrases\n"
              "  --format[=raw|..]    Force disk format for -a option\n"
              "  --fuse-help          Display extra FUSE options\n"
              "  -i|--inspector       Automatically mount filesystems\n"
              "  --help               Display help message and exit\n"
              "  --format[=raw|..]    Force disk format for -a option\n"
              "  --fuse-help          Display extra FUSE options\n"
              "  -i|--inspector       Automatically mount filesystems\n"
              "  --help               Display help message and exit\n"
+             "  --keys-from-stdin    Read passphrases from stdin\n"
              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
              "  -n|--no-sync         Don't autosync\n"
              "  -o|--option opt      Pass extra option to FUSE\n"
              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
              "  -n|--no-sync         Don't autosync\n"
              "  -o|--option opt      Pass extra option to FUSE\n"
@@ -886,10 +890,12 @@ main (int argc, char *argv[])
     { "connect", 1, 0, 'c' },
     { "dir-cache-timeout", 1, 0, 0 },
     { "domain", 1, 0, 'd' },
     { "connect", 1, 0, 'c' },
     { "dir-cache-timeout", 1, 0, 0 },
     { "domain", 1, 0, 'd' },
+    { "echo-keys", 0, 0, 0 },
     { "format", 2, 0, 0 },
     { "fuse-help", 0, 0, 0 },
     { "help", 0, 0, HELP_OPTION },
     { "inspector", 0, 0, 'i' },
     { "format", 2, 0, 0 },
     { "fuse-help", 0, 0, 0 },
     { "help", 0, 0, HELP_OPTION },
     { "inspector", 0, 0, 'i' },
+    { "keys-from-stdin", 0, 0, 0 },
     { "mount", 1, 0, 'm' },
     { "no-sync", 0, 0, 'n' },
     { "option", 1, 0, 'o' },
     { "mount", 1, 0, 'm' },
     { "no-sync", 0, 0, 'n' },
     { "option", 1, 0, 'o' },
@@ -985,8 +991,11 @@ main (int argc, char *argv[])
           format = NULL;
         else
           format = optarg;
           format = NULL;
         else
           format = optarg;
-      }
-      else {
+      } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
+        keys_from_stdin = 1;
+      } else if (STREQ (long_options[option_index].name, "echo-keys")) {
+        echo_keys = 1;
+      } else {
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
         exit (EXIT_FAILURE);
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
         exit (EXIT_FAILURE);
index afa1478..4ddea5f 100644 (file)
@@ -105,6 +105,13 @@ There is also a different attribute cache implemented by FUSE
 (see the FUSE option I<-o attr_timeout>), but the FUSE cache
 does not anticipate future requests, only cache existing ones.
 
 (see the FUSE option I<-o attr_timeout>), but the FUSE cache
 does not anticipate future requests, only cache existing ones.
 
+=item B<--echo-keys>
+
+When prompting for keys and passphrases, guestfish normally turns
+echoing off so you cannot see what you are typing.  If you are not
+worried about Tempest attacks and there is no one else in the room
+you can specify this flag to see what you are typing.
+
 =item B<--format=raw|qcow2|..> | B<--format>
 
 The default for the I<-a> option is to auto-detect the format of the
 =item B<--format=raw|qcow2|..> | B<--format>
 
 The default for the I<-a> option is to auto-detect the format of the
@@ -131,6 +138,11 @@ Using L<virt-inspector(1)> code, inspect the disks looking for
 an operating system and mount filesystems as they would be
 mounted on the real virtual machine.
 
 an operating system and mount filesystems as they would be
 mounted on the real virtual machine.
 
+=item B<--keys-from-stdin>
+
+Read key or passphrase parameters from stdin.  The default is
+to try to read passphrases from the user by opening C</dev/tty>.
+
 =item B<-m dev[:mnt]> | B<--mount dev[:mnt]>
 
 Mount the named partition or logical volume on the given mountpoint
 =item B<-m dev[:mnt]> | B<--mount dev[:mnt]>
 
 Mount the named partition or logical volume on the given mountpoint
index 1341fa2..76b4c7a 100644 (file)
@@ -58,6 +58,7 @@ let generate_fish_cmds () =
   pr "#include \"full-write.h\"\n";
   pr "#include \"xstrtol.h\"\n";
   pr "#include \"fish.h\"\n";
   pr "#include \"full-write.h\"\n";
   pr "#include \"xstrtol.h\"\n";
   pr "#include \"fish.h\"\n";
+  pr "#include \"options.h\"\n";
   pr "#include \"cmds_gperf.h\"\n";
   pr "\n";
   pr "/* Valid suffixes allowed for numbers.  See Gnulib xstrtol function. */\n";
   pr "#include \"cmds_gperf.h\"\n";
   pr "\n";
   pr "/* Valid suffixes allowed for numbers.  See Gnulib xstrtol function. */\n";
index fbb040d..2013281 100644 (file)
@@ -84,6 +84,7 @@ fish/glob.c
 fish/help.c
 fish/hexedit.c
 fish/inspect.c
 fish/help.c
 fish/hexedit.c
 fish/inspect.c
+fish/keys.c
 fish/lcd.c
 fish/man.c
 fish/more.c
 fish/lcd.c
 fish/man.c
 fish/more.c