mrc0mmand / rpms / libguestfs

Forked from rpms/libguestfs 3 years ago
Clone
Blob Blame History Raw
From f9146d6ccbaed3cc6e25f5c7c8462cb8d1514c28 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
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 <source/> 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<root>.
 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<without any trailing newline>, 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<twice>.  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