From f9146d6ccbaed3cc6e25f5c7c8462cb8d1514c28 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 30 Oct 2014 13:34:12 +0000 Subject: [PATCH] v2v: Add --password-file parameter (RHBZ#1158526). This allows you to send passwords to virt-v2v input modes without being interactive. (cherry picked from commit 65abc4420325c1226b002f2304709b2040160877) --- v2v/cmdline.ml | 13 +++++++++- v2v/domainxml-c.c | 52 ++++++++++++++++++++++++++++++++----- v2v/domainxml.ml | 2 +- v2v/domainxml.mli | 4 +-- v2v/input_libvirt.ml | 12 ++++----- v2v/input_libvirt.mli | 4 +-- v2v/input_libvirt_other.ml | 8 +++--- v2v/input_libvirt_other.mli | 4 +-- v2v/input_libvirt_vcenter_https.ml | 29 +++++++++++++-------- v2v/input_libvirt_vcenter_https.mli | 2 +- v2v/input_libvirt_xen_ssh.ml | 6 ++--- v2v/input_libvirt_xen_ssh.mli | 2 +- v2v/virt-v2v.pod | 10 ++++++- 13 files changed, 106 insertions(+), 42 deletions(-) diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml index 6f8a964..9c3253e 100644 --- a/v2v/cmdline.ml +++ b/v2v/cmdline.ml @@ -42,6 +42,7 @@ let parse_cmdline () = let output_format = ref "" in let output_name = ref "" in let output_storage = ref "" in + let password_file = ref "" in let print_source = ref false in let qemu_boot = ref false in let quiet = ref false in @@ -165,6 +166,7 @@ let parse_cmdline () = "-of", Arg.Set_string output_format, "raw|qcow2 " ^ s_"Set output format"; "-on", Arg.Set_string output_name, "name " ^ s_"Rename guest when converting"; "-os", Arg.Set_string output_storage, "storage " ^ s_"Set output storage location"; + "--password-file", Arg.Set_string password_file, "file " ^ s_"Use password from file"; "--print-source", Arg.Set print_source, " " ^ s_"Print source and stop"; "--qemu-boot", Arg.Set qemu_boot, " " ^ s_"Boot in qemu (-o qemu only)"; "-q", Arg.Set quiet, " " ^ s_"Quiet output"; @@ -227,6 +229,7 @@ read the man page virt-v2v(1). let output_mode = !output_mode in let output_name = match !output_name with "" -> None | s -> Some s in let output_storage = !output_storage in + let password_file = match !password_file with "" -> None | s -> Some s in let print_source = !print_source in let qemu_boot = !qemu_boot in let quiet = !quiet in @@ -256,6 +259,14 @@ read the man page virt-v2v(1). exit 0 ); + (* Parse out the password from the password file. *) + let password = + match password_file with + | None -> None + | Some filename -> + let password = read_whole_file filename in + Some password in + (* Parsing of the argument(s) depends on the input mode. *) let input = match input_mode with @@ -278,7 +289,7 @@ read the man page virt-v2v(1). | [guest] -> guest | _ -> error (f_"expecting a libvirt guest name on the command line") in - Input_libvirt.input_libvirt verbose input_conn guest + Input_libvirt.input_libvirt verbose password input_conn guest | `LibvirtXML -> (* -i libvirtxml: Expecting a filename (XML file). *) diff --git a/v2v/domainxml-c.c b/v2v/domainxml-c.c index 8a55030..6fa8270 100644 --- a/v2v/domainxml-c.c +++ b/v2v/domainxml-c.c @@ -74,13 +74,47 @@ get_dom_state (virDomainPtr dom) return -1; } +/* See src/libvirt-auth.c for why we need this. */ +static int +libvirt_auth_default_wrapper (virConnectCredentialPtr cred, + unsigned int ncred, + void *passwordvp) +{ + const char *password = passwordvp; + unsigned int i; + + if (password) { + /* If --password-file was specified on the command line, and the + * libvirt handler is asking for a password, return that. + */ + for (i = 0; i < ncred; ++i) { + if (cred[i].type == VIR_CRED_PASSPHRASE) { + cred[i].result = strdup (password); + cred[i].resultlen = strlen (password); + } + else { + cred[i].result = NULL; + cred[i].resultlen = 0; + } + } + return 0; + } + else { + /* No --password-file so call the default handler. */ + return virConnectAuthPtrDefault->cb (cred, ncred, + virConnectAuthPtrDefault->cbdata); + } +} + value -v2v_dumpxml (value connv, value domnamev) +v2v_dumpxml (value passwordv, value connv, value domnamev) { - CAMLparam2 (connv, domnamev); + CAMLparam3 (passwordv, connv, domnamev); CAMLlocal1 (retv); + const char *password = NULL; const char *conn_uri = NULL; const char *domname; + virConnectAuth authdata; /* We have to assemble the error on the stack because a dynamic * string couldn't be freed. */ @@ -91,16 +125,20 @@ v2v_dumpxml (value connv, value domnamev) int is_test_uri = 0; char *xml; + if (passwordv != Val_int (0)) + password = String_val (Field (passwordv, 0)); /* Some password */ + if (connv != Val_int (0)) { conn_uri = String_val (Field (connv, 0)); /* Some conn */ is_test_uri = STRPREFIX (conn_uri, "test:"); } - /* We have to call the default authentication handler, not least - * since it handles all the PolicyKit crap. However it also makes - * coding this simpler. - */ - conn = virConnectOpenAuth (conn_uri, virConnectAuthPtrDefault, VIR_CONNECT_RO); + /* Set up authentication wrapper. */ + authdata = *virConnectAuthPtrDefault; + authdata.cb = libvirt_auth_default_wrapper; + authdata.cbdata = (void *) password; + + conn = virConnectOpenAuth (conn_uri, &authdata, VIR_CONNECT_RO); if (conn == NULL) { if (conn_uri) snprintf (errmsg, sizeof errmsg, diff --git a/v2v/domainxml.ml b/v2v/domainxml.ml index d240918..61ed5e0 100644 --- a/v2v/domainxml.ml +++ b/v2v/domainxml.ml @@ -18,5 +18,5 @@ (* [virsh dumpxml] but with non-broken authentication handling. *) -external dumpxml : ?conn:string -> string -> string = "v2v_dumpxml" +external dumpxml : ?password:string -> ?conn:string -> string -> string = "v2v_dumpxml" external pool_dumpxml : ?conn:string -> string -> string = "v2v_pool_dumpxml" diff --git a/v2v/domainxml.mli b/v2v/domainxml.mli index ced55ce..ffb1c46 100644 --- a/v2v/domainxml.mli +++ b/v2v/domainxml.mli @@ -23,8 +23,8 @@ password prompt to stdout, which is the same place we would be reading the XML from. This file works around this brokenness. *) -val dumpxml : ?conn:string -> string -> string -(** [dumpxml ?conn dom] returns the libvirt XML of domain [dom]. +val dumpxml : ?password:string -> ?conn:string -> string -> string +(** [dumpxml ?password ?conn dom] returns the libvirt XML of domain [dom]. The optional [?conn] parameter is the libvirt connection URI. [dom] may be a guest name or UUID. *) diff --git a/v2v/input_libvirt.ml b/v2v/input_libvirt.ml index 60e88ac..aff97ac 100644 --- a/v2v/input_libvirt.ml +++ b/v2v/input_libvirt.ml @@ -27,10 +27,10 @@ open Types open Utils (* Choose the right subclass based on the URI. *) -let input_libvirt verbose libvirt_uri guest = +let input_libvirt verbose password libvirt_uri guest = match libvirt_uri with | None -> - Input_libvirt_other.input_libvirt_other verbose libvirt_uri guest + Input_libvirt_other.input_libvirt_other verbose password libvirt_uri guest | Some orig_uri -> let { Xml.uri_server = server; uri_scheme = scheme } as parsed_uri = @@ -45,15 +45,15 @@ let input_libvirt verbose libvirt_uri guest = | Some _, None (* No scheme? *) | Some _, Some "" -> - Input_libvirt_other.input_libvirt_other verbose libvirt_uri guest + Input_libvirt_other.input_libvirt_other verbose password libvirt_uri guest | Some server, Some ("esx"|"gsx"|"vpx" as scheme) -> (* vCenter over https *) Input_libvirt_vcenter_https.input_libvirt_vcenter_https - verbose libvirt_uri parsed_uri scheme server guest + verbose password libvirt_uri parsed_uri scheme server guest | Some server, Some ("xen+ssh" as scheme) -> (* Xen over SSH *) Input_libvirt_xen_ssh.input_libvirt_xen_ssh - verbose libvirt_uri parsed_uri scheme server guest + verbose password libvirt_uri parsed_uri scheme server guest (* Old virt-v2v also supported qemu+ssh://. However I am * deliberately not supporting this in new virt-v2v. Don't @@ -63,6 +63,6 @@ let input_libvirt verbose libvirt_uri guest = | Some _, Some _ -> (* Unknown remote scheme. *) warning ~prog (f_"no support for remote libvirt connections to '-ic %s'. The conversion may fail when it tries to read the source disks.") orig_uri; - Input_libvirt_other.input_libvirt_other verbose libvirt_uri guest + Input_libvirt_other.input_libvirt_other verbose password libvirt_uri guest let () = Modules_list.register_input_module "libvirt" diff --git a/v2v/input_libvirt.mli b/v2v/input_libvirt.mli index 1ed704b..bdd40b6 100644 --- a/v2v/input_libvirt.mli +++ b/v2v/input_libvirt.mli @@ -18,7 +18,7 @@ (** [-i libvirt] source. *) -val input_libvirt : bool -> string option -> string -> Types.input -(** [input_libvirt verbose libvirt_uri guest] creates and returns a +val input_libvirt : bool -> string option -> string option -> string -> Types.input +(** [input_libvirt verbose password libvirt_uri guest] creates and returns a new {!Types.input} object specialized for reading input from libvirt sources. *) diff --git a/v2v/input_libvirt_other.ml b/v2v/input_libvirt_other.ml index 9f3eedb..c704af6 100644 --- a/v2v/input_libvirt_other.ml +++ b/v2v/input_libvirt_other.ml @@ -43,7 +43,7 @@ let error_if_no_ssh_agent () = error (f_"ssh-agent authentication has not been set up ($SSH_AUTH_SOCK is not set). Please read \"INPUT FROM RHEL 5 XEN\" in the virt-v2v(1) man page.") (* Superclass. *) -class virtual input_libvirt verbose libvirt_uri guest = +class virtual input_libvirt verbose password libvirt_uri guest = object inherit input verbose @@ -58,9 +58,9 @@ end (* Subclass specialized for handling anything that's *not* VMware vCenter * or Xen. *) -class input_libvirt_other verbose libvirt_uri guest = +class input_libvirt_other verbose password libvirt_uri guest = object - inherit input_libvirt verbose libvirt_uri guest + inherit input_libvirt verbose password libvirt_uri guest method source () = if verbose then printf "input_libvirt_other: source()\n%!"; @@ -68,7 +68,7 @@ object (* Get the libvirt XML. This also checks (as a side-effect) * that the domain is not running. (RHBZ#1138586) *) - let xml = Domainxml.dumpxml ?conn:libvirt_uri guest in + let xml = Domainxml.dumpxml ?password ?conn:libvirt_uri guest in let source, disks = Input_libvirtxml.parse_libvirt_xml ~verbose xml in let disks = diff --git a/v2v/input_libvirt_other.mli b/v2v/input_libvirt_other.mli index 013d3bb..3eb82cb 100644 --- a/v2v/input_libvirt_other.mli +++ b/v2v/input_libvirt_other.mli @@ -21,10 +21,10 @@ val error_if_libvirt_backend : unit -> unit val error_if_no_ssh_agent : unit -> unit -class virtual input_libvirt : bool -> string option -> string -> object +class virtual input_libvirt : bool -> string option -> string option -> string -> object method as_options : string method virtual source : unit -> Types.source method adjust_overlay_parameters : Types.overlay -> unit end -val input_libvirt_other : bool -> string option -> string -> Types.input +val input_libvirt_other : bool -> string option -> string option -> string -> Types.input diff --git a/v2v/input_libvirt_vcenter_https.ml b/v2v/input_libvirt_vcenter_https.ml index e514362..3d14a27 100644 --- a/v2v/input_libvirt_vcenter_https.ml +++ b/v2v/input_libvirt_vcenter_https.ml @@ -38,7 +38,7 @@ let readahead_for_copying = Some (64 * 1024 * 1024) *) let rec get_session_cookie = let session_cookie = ref "" in - fun verbose scheme uri sslverify url -> + fun verbose password scheme uri sslverify url -> if !session_cookie <> "" then Some !session_cookie else ( @@ -48,9 +48,15 @@ let rec get_session_cookie = "url", Some url; ] in let curl_args = - match uri.uri_user with - | Some user -> ("user", Some user) :: curl_args - | None -> curl_args in + match uri.uri_user, password with + | None, None -> curl_args + | None, Some _ -> + warning ~prog (f_"--password-file parameter ignored because 'user@' was not given in the URL"); + curl_args + | Some user, None -> + ("user", Some user) :: curl_args + | Some user, Some password -> + ("user", Some (user ^ ":" ^ password)) :: curl_args in let curl_args = if not sslverify then ("insecure", None) :: curl_args else curl_args in @@ -204,7 +210,7 @@ let get_datacenter uri scheme = *) let source_re = Str.regexp "^\\[\\(.*\\)\\] \\(.*\\)\\.vmdk$" -let map_source_to_uri ?readahead verbose uri scheme server path = +let map_source_to_uri ?readahead verbose password uri scheme server path = if not (Str.string_match source_re path 0) then path else ( @@ -237,7 +243,8 @@ let map_source_to_uri ?readahead verbose uri scheme server path = string_find query "no_verify=1" = -1 in (* Now we have to query the server to get the session cookie. *) - let session_cookie = get_session_cookie verbose scheme uri sslverify url in + let session_cookie = + get_session_cookie verbose password scheme uri sslverify url in (* Construct the JSON parameters. *) let json_params = [ @@ -274,9 +281,9 @@ let map_source_to_uri ?readahead verbose uri scheme server path = (* Subclass specialized for handling VMware vCenter over https. *) class input_libvirt_vcenter_https - verbose libvirt_uri parsed_uri scheme server guest = + verbose password libvirt_uri parsed_uri scheme server guest = object - inherit input_libvirt verbose libvirt_uri guest + inherit input_libvirt verbose password libvirt_uri guest val saved_source_paths = Hashtbl.create 13 @@ -290,7 +297,7 @@ object (* Get the libvirt XML. This also checks (as a side-effect) * that the domain is not running. (RHBZ#1138586) *) - let xml = Domainxml.dumpxml ?conn:libvirt_uri guest in + let xml = Domainxml.dumpxml ?password ?conn:libvirt_uri guest in let source, disks = parse_libvirt_xml ~verbose xml in (* Save the original source paths, so that we can remap them again @@ -314,7 +321,7 @@ object | { p_source_disk = disk; p_source = P_dont_rewrite } -> disk | { p_source_disk = disk; p_source = P_source_file path } -> let qemu_uri = map_source_to_uri ?readahead - verbose parsed_uri scheme server path in + verbose password parsed_uri scheme server path in (* The libvirt ESX driver doesn't normally specify a format, but * the format of the -flat file is *always* raw, so force it here. @@ -335,7 +342,7 @@ object let readahead = readahead_for_copying in let backing_qemu_uri = map_source_to_uri ?readahead - verbose parsed_uri scheme server orig_path in + verbose password parsed_uri scheme server orig_path in (* Rebase the qcow2 overlay to adjust the readahead parameter. *) let cmd = diff --git a/v2v/input_libvirt_vcenter_https.mli b/v2v/input_libvirt_vcenter_https.mli index 82dce53..800c6ab 100644 --- a/v2v/input_libvirt_vcenter_https.mli +++ b/v2v/input_libvirt_vcenter_https.mli @@ -18,4 +18,4 @@ (** [-i libvirt] when the source is VMware vCenter *) -val input_libvirt_vcenter_https : bool -> string option -> Xml.uri -> string -> string -> string -> Types.input +val input_libvirt_vcenter_https : bool -> string option -> string option -> Xml.uri -> string -> string -> string -> Types.input diff --git a/v2v/input_libvirt_xen_ssh.ml b/v2v/input_libvirt_xen_ssh.ml index e1600a0..cf5f1ae 100644 --- a/v2v/input_libvirt_xen_ssh.ml +++ b/v2v/input_libvirt_xen_ssh.ml @@ -30,9 +30,9 @@ open Input_libvirt_other open Printf (* Subclass specialized for handling Xen over SSH. *) -class input_libvirt_xen_ssh verbose libvirt_uri parsed_uri scheme server guest = +class input_libvirt_xen_ssh verbose password libvirt_uri parsed_uri scheme server guest = object - inherit input_libvirt verbose libvirt_uri guest + inherit input_libvirt verbose password libvirt_uri guest method source () = if verbose then @@ -45,7 +45,7 @@ object (* Get the libvirt XML. This also checks (as a side-effect) * that the domain is not running. (RHBZ#1138586) *) - let xml = Domainxml.dumpxml ?conn:libvirt_uri guest in + let xml = Domainxml.dumpxml ?password ?conn:libvirt_uri guest in let source, disks = parse_libvirt_xml ~verbose xml in (* Map the filename (which is relative to the remote diff --git a/v2v/input_libvirt_xen_ssh.mli b/v2v/input_libvirt_xen_ssh.mli index 85473ed..47eb62c 100644 --- a/v2v/input_libvirt_xen_ssh.mli +++ b/v2v/input_libvirt_xen_ssh.mli @@ -18,4 +18,4 @@ (** [-i libvirt] when the source is Xen *) -val input_libvirt_xen_ssh : bool -> string option -> Xml.uri -> string -> string -> string -> Types.input +val input_libvirt_xen_ssh : bool -> string option -> string option -> Xml.uri -> string -> string -> string -> Types.input diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod index 6e449cf..bebe105 100644 --- a/v2v/virt-v2v.pod +++ b/v2v/virt-v2v.pod @@ -423,6 +423,13 @@ C. You will get an error if virt-v2v is unable to mount/write to the Export Storage Domain. +=item B<--password-file> file + +Instead of asking for password(s) interactively, pass the password +through a file. Note the file should contain the whole password, +B, and for security the file should have +mode C<0600> so that others cannot read it. + =item B<--print-source> Print information about the source guest and stop. This option is @@ -789,7 +796,8 @@ down). Note that you may be asked for the vCenter password I. This happens once because libvirt needs it, and a second time because -virt-v2v itself connects directly to the server. +virt-v2v itself connects directly to the server. Use +I<--password-file> to supply a password via a file. In this case the output flags are set to write the converted guest to a temporary directory as this is just an example, but you can also -- 1.8.3.1