From a232e62dcf508517a32b9a8d7e4529e827be721b Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 5 Nov 2010 11:39:24 +0000 Subject: [PATCH] fish: '-i' option automatically handles whole-disk encryption. 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 | 1 + fish/fish.c | 64 ------------------------------- fish/fish.h | 1 - fish/inspect.c | 76 +++++++++++++++++++++++++++++++++++++ fish/keys.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ fish/options.h | 5 +++ fuse/Makefile.am | 1 + fuse/guestmount.c | 13 ++++++- fuse/guestmount.pod | 12 ++++++ generator/generator_fish.ml | 1 + po/POTFILES.in | 1 + 11 files changed, 199 insertions(+), 67 deletions(-) create mode 100644 fish/keys.c diff --git a/fish/Makefile.am b/fish/Makefile.am index 61b6346..dadda91 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -46,6 +46,7 @@ EXTRA_DIST = \ # other guestfish files. SHARED_SOURCE_FILES = \ inspect.c \ + keys.c \ options.h \ options.c \ virt.c diff --git a/fish/fish.c b/fish/fish.c index 54fd270..d9a92dd 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -30,7 +30,6 @@ #include #include #include -#include #ifdef HAVE_LIBREADLINE #include @@ -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; -} diff --git a/fish/fish.h b/fish/fish.h index a3f7caf..8fedf5d 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -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); diff --git a/fish/inspect.c b/fish/inspect.c index 8e56553..cc3916b 100644 --- a/fish/inspect.c +++ b/fish/inspect.c @@ -22,10 +22,14 @@ #include #include +#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 index 0000000..deb627f --- /dev/null +++ b/fish/keys.c @@ -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 + +#include +#include +#include +#include + +#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; +} diff --git a/fish/options.h b/fish/options.h index b0bbdeb..e36c57a 100644 --- a/fish/options.h +++ b/fish/options.h @@ -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); diff --git a/fuse/Makefile.am b/fuse/Makefile.am index f6f662a..ab63584 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -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 diff --git a/fuse/guestmount.c b/fuse/guestmount.c index a32da6b..55b71d7 100644 --- a/fuse/guestmount.c +++ b/fuse/guestmount.c @@ -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); diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod index afa1478..4ddea5f 100644 --- a/fuse/guestmount.pod +++ b/fuse/guestmount.pod @@ -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 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. + =item B<-m dev[:mnt]> | B<--mount dev[:mnt]> Mount the named partition or logical volume on the given mountpoint diff --git a/generator/generator_fish.ml b/generator/generator_fish.ml index 1341fa2..76b4c7a 100644 --- a/generator/generator_fish.ml +++ b/generator/generator_fish.ml @@ -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"; diff --git a/po/POTFILES.in b/po/POTFILES.in index fbb040d..2013281 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 -- 1.8.3.1