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 \
+       keys.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 <termios.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
@@ -1480,66 +1479,3 @@ file_out (const char *arg)
   }
   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 char *read_key (const char *param);
 
 /* in cmds.c (auto-generated) */
 extern void list_commands (void);
index 8e56553..cc3916b 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "c-ctype.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.
  */
@@ -71,6 +75,8 @@ compare_keys (const void *p1, const void *p2)
 void
 inspect_mount (void)
 {
+  do_decrypt ();
+
   char **roots = guestfs_inspect_os (g);
   if (roots == NULL)
     exit (EXIT_FAILURE);
@@ -139,3 +145,73 @@ print_inspect_prompt (void)
 
   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 keys_from_stdin;
+extern int echo_keys;
 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);
 
+/* 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);
index f6f662a..ab63584 100644 (file)
@@ -27,6 +27,7 @@ bin_PROGRAMS = guestmount
 # between guestfish and guestmount.
 SHARED_SOURCE_FILES = \
        ../fish/inspect.c \
+       ../fish/keys.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 keys_from_stdin = 0;
+int echo_keys = 0;
 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"
+             "  --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"
+             "  --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"
@@ -886,10 +890,12 @@ main (int argc, char *argv[])
     { "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' },
+    { "keys-from-stdin", 0, 0, 0 },
     { "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;
-      }
-      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);
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.
 
+=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
@@ -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.
 
+=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
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 \"options.h\"\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/keys.c
 fish/lcd.c
 fish/man.c
 fish/more.c