#include <sys/types.h>
#include <sys/wait.h>
#include <locale.h>
+#include <termios.h>
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
int remote_control = 0;
int exit_on_error = 1;
int command_num = 0;
+int keys_from_stdin = 0;
static void __attribute__((noreturn))
usage (int status)
" -D|--no-dest-paths Don't tab-complete paths from guest fs\n"
" -f|--file file Read commands from file\n"
" -i|--inspector Run virt-inspector to get disk mountpoints\n"
+ " --keys-from-stdin Read passphrases from stdin\n"
" --listen Listen for remote commands\n"
" -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
" -n|--no-sync Don't autosync\n"
{ "file", 1, 0, 'f' },
{ "help", 0, 0, HELP_OPTION },
{ "inspector", 0, 0, 'i' },
+ { "keys-from-stdin", 0, 0, 0 },
{ "listen", 0, 0, 0 },
{ "mount", 1, 0, 'm' },
{ "new", 1, 0, 'N' },
}
} else if (STREQ (long_options[option_index].name, "selinux")) {
guestfs_set_selinux (g, 1);
+ } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
+ keys_from_stdin = 1;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
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 (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;
+}
+
static void
print_shell_quote (FILE *stream, const char *str)
{
* To return an arbitrary buffer, use RBufferOut.
*)
| BufferIn of string
+ (* Key material / passphrase. Eventually we should treat this
+ * as sensitive and mlock it into physical RAM. However this
+ * is highly complex because of all the places that XDR-encoded
+ * strings can end up. So currently the only difference from
+ * 'String' is the way that guestfish requests these parameters
+ * from the user.
+ *)
+ | Key of string
type flags =
| ProtocolLimitWarning (* display warning about protocol size limits *)
let name_of_argt = function
| Pathname n | Device n | Dev_or_Path n | String n | OptString n
| StringList n | DeviceList n | Bool n | Int n | Int64 n
- | FileIn n | FileOut n | BufferIn n -> n
+ | FileIn n | FileOut n | BufferIn n | Key n -> n
let java_name_of_struct typ =
try List.assoc typ java_structs
pr "%s\n\n" protocol_limit_warning;
if List.mem DangerWillRobinson flags then
pr "%s\n\n" danger_will_robinson;
+ if List.exists (function Key _ -> true | _ -> false) (snd style) then
+ pr "This function takes a key or passphrase parameter which
+could contain sensitive material. Read the section
+L</KEYS AND PASSPHRASES> for more information.\n\n";
match deprecation_notice flags with
| None -> ()
| Some txt -> pr "%s\n\n" txt
pr "struct %s_args {\n" name;
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n ->
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n ->
pr " string %s<>;\n" n
| OptString n -> pr " str *%s;\n" n
| StringList n | DeviceList n -> pr " str %s<>;\n" n
| FileOut n
| BufferIn n
| StringList n
- | DeviceList n ->
+ | DeviceList n
+ | Key n ->
pr " if (%s == NULL) {\n" n;
pr " error (g, \"%%s: %%s: parameter cannot be NULL\",\n";
pr " \"%s\", \"%s\");\n" shortname n;
| Dev_or_Path n
| FileIn n
| FileOut n
- | BufferIn n ->
+ | BufferIn n
+ | Key n ->
(* guestfish doesn't support string escaping, so neither do we *)
pr " printf (\" \\\"%%s\\\"\", %s);\n" n
| OptString n -> (* string option *)
| args ->
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n ->
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n ->
pr " args.%s = (char *) %s;\n" n n
| OptString n ->
pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n
function
| Device n | Dev_or_Path n
| Pathname n
- | String n -> ()
+ | String n
+ | Key n -> ()
| OptString n -> pr " char *%s;\n" n
| StringList n | DeviceList n -> pr " char **%s;\n" n
| Bool n -> pr " int %s;\n" n
pr_args n;
pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n"
n (if is_filein then "cancel_receive ()" else "0");
- | String n -> pr_args n
+ | String n | Key n -> pr_args n
| OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
| StringList n ->
pr_list_handling_code n;
| Device n, arg
| Dev_or_Path n, arg
| String n, arg
- | OptString n, arg ->
+ | OptString n, arg
+ | Key n, arg ->
pr " const char *%s = \"%s\";\n" n (c_quote arg);
| BufferIn n, arg ->
pr " const char *%s = \"%s\";\n" n (c_quote arg);
| Pathname n, _
| Device n, _ | Dev_or_Path n, _
| String n, _
- | OptString n, _ ->
+ | OptString n, _
+ | Key n, _ ->
pr ", %s" n
| BufferIn n, _ ->
pr ", %s, %s_size" n n
match snd style with
| [] -> name2
| args ->
+ let args = List.filter (function Key _ -> false | _ -> true) args in
sprintf "%s %s"
name2 (String.concat " " (List.map name_of_argt args)) in
| Pathname n
| Dev_or_Path n
| FileIn n
- | FileOut n -> pr " char *%s;\n" n
+ | FileOut n
+ | Key n -> pr " char *%s;\n" n
| BufferIn n ->
pr " const char *%s;\n" n;
pr " size_t %s_size;\n" n
) (snd style);
(* Check and convert parameters. *)
- let argc_expected = List.length (snd style) in
+ let argc_expected =
+ let args_no_keys =
+ List.filter (function Key _ -> false | _ -> true) (snd style) in
+ List.length args_no_keys in
pr " if (argc != %d) {\n" argc_expected;
pr " fprintf (stderr, _(\"%%s should have %%d parameter(s)\\n\"), cmd, %d);\n"
argc_expected;
pr " return -1;\n";
pr " }\n";
- let parse_integer fn fntyp rtyp range name i =
+ let parse_integer fn fntyp rtyp range name =
pr " {\n";
pr " strtol_error xerr;\n";
pr " %s r;\n" fntyp;
pr "\n";
- pr " xerr = %s (argv[%d], NULL, 0, &r, xstrtol_suffixes);\n" fn i;
+ pr " xerr = %s (argv[i++], NULL, 0, &r, xstrtol_suffixes);\n" fn;
pr " if (xerr != LONGINT_OK) {\n";
pr " fprintf (stderr,\n";
pr " _(\"%%s: %%s: invalid integer parameter (%%s returned %%d)\\n\"),\n";
pr " }\n";
in
- iteri (
- fun i ->
- function
- | Device name
- | String name ->
- pr " %s = argv[%d];\n" name i
- | Pathname name
- | Dev_or_Path name ->
- pr " %s = resolve_win_path (argv[%d]);\n" name i;
- pr " if (%s == NULL) return -1;\n" name
- | OptString name ->
- pr " %s = STRNEQ (argv[%d], \"\") ? argv[%d] : NULL;\n"
- name i i
- | BufferIn name ->
- pr " %s = argv[%d];\n" name i;
- pr " %s_size = strlen (argv[%d]);\n" name i
- | FileIn name ->
- pr " %s = file_in (argv[%d]);\n" name i;
- pr " if (%s == NULL) return -1;\n" name
- | FileOut name ->
- pr " %s = file_out (argv[%d]);\n" name i;
- pr " if (%s == NULL) return -1;\n" name
- | StringList name | DeviceList name ->
- pr " %s = parse_string_list (argv[%d]);\n" name i;
- pr " if (%s == NULL) return -1;\n" name;
- | Bool name ->
- pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i
- | Int name ->
- let range =
- let min = "(-(2LL<<30))"
- and max = "((2LL<<30)-1)"
- and comment =
- "The Int type in the generator is a signed 31 bit int." in
- Some (min, max, comment) in
- parse_integer "xstrtoll" "long long" "int" range name i
- | Int64 name ->
- parse_integer "xstrtoll" "long long" "int64_t" None name i
+ if snd style <> [] then
+ pr " size_t i = 0;\n";
+
+ List.iter (
+ function
+ | Device name
+ | String name ->
+ pr " %s = argv[i++];\n" name
+ | Pathname name
+ | Dev_or_Path name ->
+ pr " %s = resolve_win_path (argv[i++]);\n" name;
+ pr " if (%s == NULL) return -1;\n" name
+ | OptString name ->
+ pr " %s = STRNEQ (argv[i], \"\") ? argv[i] : NULL;\n" name;
+ pr " i++;\n"
+ | BufferIn name ->
+ pr " %s = argv[i];\n" name;
+ pr " %s_size = strlen (argv[i]);\n" name;
+ pr " i++;\n"
+ | FileIn name ->
+ pr " %s = file_in (argv[i++]);\n" name;
+ pr " if (%s == NULL) return -1;\n" name
+ | FileOut name ->
+ pr " %s = file_out (argv[i++]);\n" name;
+ pr " if (%s == NULL) return -1;\n" name
+ | StringList name | DeviceList name ->
+ pr " %s = parse_string_list (argv[i++]);\n" name;
+ pr " if (%s == NULL) return -1;\n" name
+ | Key name ->
+ pr " %s = read_key (\"%s\");\n" name name;
+ pr " if (%s == NULL) return -1;\n" name
+ | Bool name ->
+ pr " %s = is_true (argv[i++]) ? 1 : 0;\n" name
+ | Int name ->
+ let range =
+ let min = "(-(2LL<<30))"
+ and max = "((2LL<<30)-1)"
+ and comment =
+ "The Int type in the generator is a signed 31 bit int." in
+ Some (min, max, comment) in
+ parse_integer "xstrtoll" "long long" "int" range name
+ | Int64 name ->
+ parse_integer "xstrtoll" "long long" "int64_t" None name
) (snd style);
(* Call C API function. *)
| OptString _ | Bool _
| Int _ | Int64 _
| BufferIn _ -> ()
- | Pathname name | Dev_or_Path name | FileOut name ->
+ | Pathname name | Dev_or_Path name | FileOut name
+ | Key name ->
pr " free (%s);\n" name
| FileIn name ->
pr " free_file_in (%s);\n" name
pr " %s" name;
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n -> pr " %s" n
+ | Pathname n | Device n | Dev_or_Path n | String n ->
+ pr " %s" n
| OptString n -> pr " %s" n
| StringList n | DeviceList n -> pr " '%s ...'" n
| Bool _ -> pr " true|false"
| Int64 n -> pr " %s" n
| FileIn n | FileOut n -> pr " (%s|-)" n
| BufferIn n -> pr " %s" n
+ | Key _ -> () (* keys are entered at a prompt *)
) (snd style);
pr "\n";
pr "\n";
| _ -> false) (snd style) then
pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
+ if List.exists (function Key _ -> true | _ -> false) (snd style) then
+ pr "This command has one or more key or passphrase parameters.
+Guestfish will prompt for these separately.\n\n";
+
if List.mem ProtocolLimitWarning flags then
pr "%s\n\n" protocol_limit_warning;
| Pathname n
| Device n | Dev_or_Path n
| String n
- | OptString n ->
+ | OptString n
+ | Key n ->
next ();
pr "const char *%s" n
| StringList n | DeviceList n ->
| Device n | Dev_or_Path n
| String n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
(* Copy strings in case the GC moves them: RHBZ#604691 *)
pr " char *%s = guestfs_safe_strdup (g, String_val (%sv));\n" n n
| OptString n ->
List.iter (
function
| Pathname n | Device n | Dev_or_Path n | String n | OptString n
- | FileIn n | FileOut n | BufferIn n ->
+ | FileIn n | FileOut n | BufferIn n | Key n ->
pr " free (%s);\n" n
| StringList n | DeviceList n ->
pr " ocaml_guestfs_free_strings (%s);\n" n;
List.iter (
function
| Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _
- | BufferIn _ -> pr "string -> "
+ | BufferIn _ | Key _ -> pr "string -> "
| OptString _ -> pr "string option -> "
| StringList _ | DeviceList _ -> pr "string array -> "
| Bool _ -> pr "bool -> "
fun i ->
function
| Pathname n | Device n | Dev_or_Path n | String n
- | FileIn n | FileOut n ->
+ | FileIn n | FileOut n | Key n ->
pr " char *%s;\n" n
| BufferIn n ->
pr " char *%s;\n" n;
| Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _
| Bool _ | Int _ | Int64 _
| FileIn _ | FileOut _
- | BufferIn _ -> ()
+ | BufferIn _ | Key _ -> ()
| StringList n | DeviceList n -> pr " free (%s);\n" n
) (snd style)
in
match arg with
| Pathname n | Device n | Dev_or_Path n | String n
| OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n
- | BufferIn n ->
+ | BufferIn n | Key n ->
pr "$%s" n
| StringList n | DeviceList n ->
pr "\\@%s" n
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n
| FileIn n | FileOut n ->
pr " const char *%s;\n" n
| OptString n -> pr " const char *%s;\n" n
pr " if (!PyArg_ParseTuple (args, (char *) \"O";
List.iter (
function
- | Pathname _ | Device _ | Dev_or_Path _ | String _ | FileIn _ | FileOut _ -> pr "s"
+ | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
+ | FileIn _ | FileOut _ -> pr "s"
| OptString _ -> pr "z"
| StringList _ | DeviceList _ -> pr "O"
| Bool _ -> pr "i" (* XXX Python has booleans? *)
pr " &py_g";
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n | FileIn n | FileOut n -> pr ", &%s" n
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n
+ | FileIn n | FileOut n -> pr ", &%s" n
| OptString n -> pr ", &%s" n
| StringList n | DeviceList n -> pr ", &py_%s" n
| Bool n -> pr ", &%s" n
pr " g = get_handle (py_g);\n";
List.iter (
function
- | Pathname _ | Device _ | Dev_or_Path _ | String _
+ | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
| FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
| BufferIn _ -> ()
| StringList n | DeviceList n ->
List.iter (
function
- | Pathname _ | Device _ | Dev_or_Path _ | String _
+ | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
| FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
| BufferIn _ -> ()
| StringList n | DeviceList n ->
List.iter (
function
- | Pathname n | Device n | Dev_or_Path n | String n | FileIn n | FileOut n ->
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n
+ | FileIn n | FileOut n ->
pr " Check_Type (%sv, T_STRING);\n" n;
pr " const char *%s = StringValueCStr (%sv);\n" n n;
pr " if (!%s)\n" n;
List.iter (
function
- | Pathname _ | Device _ | Dev_or_Path _ | String _
+ | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _
| FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _
| BufferIn _ -> ()
| StringList n | DeviceList n ->
| String n
| OptString n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
pr "String %s" n
| BufferIn n ->
pr "byte[] %s" n
| String n
| OptString n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
pr ", jstring j%s" n
| BufferIn n ->
pr ", jbyteArray j%s" n
| String n
| OptString n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
pr " const char *%s;\n" n
| BufferIn n ->
pr " jbyte *%s;\n" n;
| Device n | Dev_or_Path n
| String n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
pr " %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
| OptString n ->
(* This is completely undocumented, but Java null becomes
| Device n | Dev_or_Path n
| String n
| FileIn n
- | FileOut n ->
+ | FileOut n
+ | Key n ->
pr " (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
| OptString n ->
pr " if (j%s)\n" n;
function
| FileIn n
| FileOut n
- | Pathname n | Device n | Dev_or_Path n | String n ->
+ | Pathname n | Device n | Dev_or_Path n | String n | Key n ->
pr "withCString %s $ \\%s -> " n n
| BufferIn n ->
pr "withCStringLen %s $ \\(%s, %s_size) -> " n n n
| Int n -> sprintf "(fromIntegral %s)" n
| Int64 n -> sprintf "(fromIntegral %s)" n
| FileIn n | FileOut n
- | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n -> n
+ | Pathname n | Device n | Dev_or_Path n
+ | String n | OptString n
+ | StringList n | DeviceList n
+ | Key n -> n
| BufferIn n -> sprintf "%s (fromIntegral %s_size)" n n
) (snd style) in
pr "withForeignPtr h (\\p -> c_%s %s)\n" name
List.iter (
fun arg ->
(match arg with
- | Pathname _ | Device _ | Dev_or_Path _ | String _ -> pr "%s" string
+ | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ ->
+ pr "%s" string
| BufferIn _ ->
if hs then pr "String"
else pr "CString -> CInt"
function
| Pathname n | Device n | Dev_or_Path n | String n | OptString n
| FileIn n | FileOut n
+ | Key n
| BufferIn n ->
pr ", [In] string %s" n
| StringList n | DeviceList n ->
function
| Pathname n | Device n | Dev_or_Path n | String n | OptString n
| FileIn n | FileOut n
+ | Key n
| BufferIn n ->
next (); pr "string %s" n
| StringList n | DeviceList n ->
| Device n | Dev_or_Path n
| String n
| FileIn n
- | FileOut n -> pr " printf (\"%%s\\n\", %s);\n" n
+ | FileOut n
+ | Key n -> pr " printf (\"%%s\\n\", %s);\n" n
| BufferIn n ->
pr " {\n";
pr " size_t i;\n";