b38b0f
From 4d4eb9e793e7722c05c697812320a14ae5797bae Mon Sep 17 00:00:00 2001
b38b0f
From: Pino Toscano <ptoscano@redhat.com>
b38b0f
Date: Mon, 8 Jul 2019 15:26:00 +0100
b38b0f
Subject: [PATCH 14/39] ssh: switch from libssh2 to libssh
b38b0f
MIME-Version: 1.0
b38b0f
Content-Type: text/plain; charset=UTF-8
b38b0f
Content-Transfer-Encoding: 8bit
b38b0f
b38b0f
RH-Author: Pino Toscano <ptoscano@redhat.com>
b38b0f
Message-id: <20190708152601.21123-10-ptoscano@redhat.com>
b38b0f
Patchwork-id: 89421
b38b0f
O-Subject: [RHEL-8.1.0 qemu-kvm PATCH v3 09/10] ssh: switch from libssh2 to libssh
b38b0f
Bugzilla: 1513367
b38b0f
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
b38b0f
RH-Acked-by: Max Reitz <mreitz@redhat.com>
b38b0f
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
b38b0f
RH-Acked-by: Markus Armbruster <armbru@redhat.com>
b38b0f
b38b0f
Rewrite the implementation of the ssh block driver to use libssh instead
b38b0f
of libssh2.  The libssh library has various advantages over libssh2:
b38b0f
- easier API for authentication (for example for using ssh-agent)
b38b0f
- easier API for known_hosts handling
b38b0f
- supports newer types of keys in known_hosts
b38b0f
b38b0f
Use APIs/features available in libssh 0.8 conditionally, to support
b38b0f
older versions (which are not recommended though).
b38b0f
b38b0f
Adjust the iotest 207 according to the different error message, and to
b38b0f
find the default key type for localhost (to properly compare the
b38b0f
fingerprint with).
b38b0f
Contributed-by: Max Reitz <mreitz@redhat.com>
b38b0f
b38b0f
Adjust the various Docker/Travis scripts to use libssh when available
b38b0f
instead of libssh2. The mingw/mxe testing is dropped for now, as there
b38b0f
are no packages for it.
b38b0f
b38b0f
Signed-off-by: Pino Toscano <ptoscano@redhat.com>
b38b0f
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
b38b0f
Acked-by: Alex Bennée <alex.bennee@linaro.org>
b38b0f
Message-id: 20190620200840.17655-1-ptoscano@redhat.com
b38b0f
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
b38b0f
Message-id: 5873173.t2JhDm7DL7@lindworm.usersys.redhat.com
b38b0f
Signed-off-by: Max Reitz <mreitz@redhat.com>
b38b0f
(cherry picked from commit b10d49d7619e4957b4b971f816661b57e5061d71)
b38b0f
Signed-off-by: Pino Toscano <ptoscano@redhat.com>
b38b0f
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
b38b0f
---
b38b0f
 .travis.yml                                        |   4 +-
b38b0f
 block/Makefile.objs                                |   6 +-
b38b0f
 block/ssh.c                                        | 652 ++++++++++++---------
b38b0f
 block/trace-events                                 |  14 +-
b38b0f
 configure                                          |  65 +-
b38b0f
 docs/qemu-block-drivers.texi                       |   2 +-
b38b0f
 tests/docker/dockerfiles/debian-win32-cross.docker |   1 -
b38b0f
 tests/docker/dockerfiles/debian-win64-cross.docker |   1 -
b38b0f
 tests/docker/dockerfiles/fedora.docker             |   6 +-
b38b0f
 tests/docker/dockerfiles/ubuntu.docker             |   2 +-
b38b0f
 tests/qemu-iotests/207                             |  54 +-
b38b0f
 tests/qemu-iotests/207.out                         |   2 +-
b38b0f
 12 files changed, 450 insertions(+), 359 deletions(-)
b38b0f
b38b0f
diff --git a/.travis.yml b/.travis.yml
b38b0f
index c1e9923..d174d82 100644
b38b0f
--- a/.travis.yml
b38b0f
+++ b/.travis.yml
b38b0f
@@ -28,7 +28,7 @@ addons:
b38b0f
       - libseccomp-dev
b38b0f
       - libspice-protocol-dev
b38b0f
       - libspice-server-dev
b38b0f
-      - libssh2-1-dev
b38b0f
+      - libssh-dev
b38b0f
       - liburcu-dev
b38b0f
       - libusb-1.0-0-dev
b38b0f
       - libvte-2.90-dev
b38b0f
@@ -188,7 +188,7 @@ matrix:
b38b0f
             - libseccomp-dev
b38b0f
             - libspice-protocol-dev
b38b0f
             - libspice-server-dev
b38b0f
-            - libssh2-1-dev
b38b0f
+            - libssh-dev
b38b0f
             - liburcu-dev
b38b0f
             - libusb-1.0-0-dev
b38b0f
             - libvte-2.90-dev
b38b0f
diff --git a/block/Makefile.objs b/block/Makefile.objs
b38b0f
index ac7a1f8..51cd336 100644
b38b0f
--- a/block/Makefile.objs
b38b0f
+++ b/block/Makefile.objs
b38b0f
@@ -30,7 +30,7 @@ block-obj-$(CONFIG_CURL) += curl.o
b38b0f
 block-obj-$(CONFIG_RBD) += rbd.o
b38b0f
 block-obj-$(CONFIG_GLUSTERFS) += gluster.o
b38b0f
 block-obj-$(CONFIG_VXHS) += vxhs.o
b38b0f
-block-obj-$(CONFIG_LIBSSH2) += ssh.o
b38b0f
+block-obj-$(CONFIG_LIBSSH) += ssh.o
b38b0f
 block-obj-y += accounting.o dirty-bitmap.o
b38b0f
 block-obj-y += write-threshold.o
b38b0f
 block-obj-y += backup.o
b38b0f
@@ -51,8 +51,8 @@ rbd.o-libs         := $(RBD_LIBS)
b38b0f
 gluster.o-cflags   := $(GLUSTERFS_CFLAGS)
b38b0f
 gluster.o-libs     := $(GLUSTERFS_LIBS)
b38b0f
 vxhs.o-libs        := $(VXHS_LIBS)
b38b0f
-ssh.o-cflags       := $(LIBSSH2_CFLAGS)
b38b0f
-ssh.o-libs         := $(LIBSSH2_LIBS)
b38b0f
+ssh.o-cflags       := $(LIBSSH_CFLAGS)
b38b0f
+ssh.o-libs         := $(LIBSSH_LIBS)
b38b0f
 block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
b38b0f
 block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
b38b0f
 dmg-bz2.o-libs     := $(BZIP2_LIBS)
b38b0f
diff --git a/block/ssh.c b/block/ssh.c
b38b0f
index f0ef874..a4a374c 100644
b38b0f
--- a/block/ssh.c
b38b0f
+++ b/block/ssh.c
b38b0f
@@ -24,8 +24,8 @@
b38b0f
 
b38b0f
 #include "qemu/osdep.h"
b38b0f
 
b38b0f
-#include <libssh2.h>
b38b0f
-#include <libssh2_sftp.h>
b38b0f
+#include <libssh/libssh.h>
b38b0f
+#include <libssh/sftp.h>
b38b0f
 
b38b0f
 #include "block/block_int.h"
b38b0f
 #include "block/qdict.h"
b38b0f
@@ -44,13 +44,11 @@
b38b0f
 #include "trace.h"
b38b0f
 
b38b0f
 /*
b38b0f
- * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself.  Note
b38b0f
- * that this requires that libssh2 was specially compiled with the
b38b0f
- * `./configure --enable-debug' option, so most likely you will have
b38b0f
- * to compile it yourself.  The meaning of <bitmask> is described
b38b0f
- * here: http://www.libssh2.org/libssh2_trace.html
b38b0f
+ * TRACE_LIBSSH=<level> enables tracing in libssh itself.
b38b0f
+ * The meaning of <level> is described here:
b38b0f
+ * http://api.libssh.org/master/group__libssh__log.html
b38b0f
  */
b38b0f
-#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
b38b0f
+#define TRACE_LIBSSH  0 /* see: SSH_LOG_* */
b38b0f
 
b38b0f
 typedef struct BDRVSSHState {
b38b0f
     /* Coroutine. */
b38b0f
@@ -58,18 +56,15 @@ typedef struct BDRVSSHState {
b38b0f
 
b38b0f
     /* SSH connection. */
b38b0f
     int sock;                         /* socket */
b38b0f
-    LIBSSH2_SESSION *session;         /* ssh session */
b38b0f
-    LIBSSH2_SFTP *sftp;               /* sftp session */
b38b0f
-    LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
b38b0f
+    ssh_session session;              /* ssh session */
b38b0f
+    sftp_session sftp;                /* sftp session */
b38b0f
+    sftp_file sftp_handle;            /* sftp remote file handle */
b38b0f
 
b38b0f
-    /* See ssh_seek() function below. */
b38b0f
-    int64_t offset;
b38b0f
-    bool offset_op_read;
b38b0f
-
b38b0f
-    /* File attributes at open.  We try to keep the .filesize field
b38b0f
+    /*
b38b0f
+     * File attributes at open.  We try to keep the .size field
b38b0f
      * updated if it changes (eg by writing at the end of the file).
b38b0f
      */
b38b0f
-    LIBSSH2_SFTP_ATTRIBUTES attrs;
b38b0f
+    sftp_attributes attrs;
b38b0f
 
b38b0f
     InetSocketAddress *inet;
b38b0f
 
b38b0f
@@ -89,7 +84,6 @@ static void ssh_state_init(BDRVSSHState *s)
b38b0f
 {
b38b0f
     memset(s, 0, sizeof *s);
b38b0f
     s->sock = -1;
b38b0f
-    s->offset = -1;
b38b0f
     qemu_co_mutex_init(&s->lock);
b38b0f
 }
b38b0f
 
b38b0f
@@ -97,20 +91,18 @@ static void ssh_state_free(BDRVSSHState *s)
b38b0f
 {
b38b0f
     g_free(s->user);
b38b0f
 
b38b0f
+    if (s->attrs) {
b38b0f
+        sftp_attributes_free(s->attrs);
b38b0f
+    }
b38b0f
     if (s->sftp_handle) {
b38b0f
-        libssh2_sftp_close(s->sftp_handle);
b38b0f
+        sftp_close(s->sftp_handle);
b38b0f
     }
b38b0f
     if (s->sftp) {
b38b0f
-        libssh2_sftp_shutdown(s->sftp);
b38b0f
+        sftp_free(s->sftp);
b38b0f
     }
b38b0f
     if (s->session) {
b38b0f
-        libssh2_session_disconnect(s->session,
b38b0f
-                                   "from qemu ssh client: "
b38b0f
-                                   "user closed the connection");
b38b0f
-        libssh2_session_free(s->session);
b38b0f
-    }
b38b0f
-    if (s->sock >= 0) {
b38b0f
-        close(s->sock);
b38b0f
+        ssh_disconnect(s->session);
b38b0f
+        ssh_free(s->session); /* This frees s->sock */
b38b0f
     }
b38b0f
 }
b38b0f
 
b38b0f
@@ -125,13 +117,13 @@ session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
b38b0f
     va_end(args);
b38b0f
 
b38b0f
     if (s->session) {
b38b0f
-        char *ssh_err;
b38b0f
+        const char *ssh_err;
b38b0f
         int ssh_err_code;
b38b0f
 
b38b0f
-        /* This is not an errno.  See <libssh2.h>. */
b38b0f
-        ssh_err_code = libssh2_session_last_error(s->session,
b38b0f
-                                                  &ssh_err, NULL, 0);
b38b0f
-        error_setg(errp, "%s: %s (libssh2 error code: %d)",
b38b0f
+        /* This is not an errno.  See <libssh/libssh.h>. */
b38b0f
+        ssh_err = ssh_get_error(s->session);
b38b0f
+        ssh_err_code = ssh_get_error_code(s->session);
b38b0f
+        error_setg(errp, "%s: %s (libssh error code: %d)",
b38b0f
                    msg, ssh_err, ssh_err_code);
b38b0f
     } else {
b38b0f
         error_setg(errp, "%s", msg);
b38b0f
@@ -150,18 +142,18 @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
b38b0f
     va_end(args);
b38b0f
 
b38b0f
     if (s->sftp) {
b38b0f
-        char *ssh_err;
b38b0f
+        const char *ssh_err;
b38b0f
         int ssh_err_code;
b38b0f
-        unsigned long sftp_err_code;
b38b0f
+        int sftp_err_code;
b38b0f
 
b38b0f
-        /* This is not an errno.  See <libssh2.h>. */
b38b0f
-        ssh_err_code = libssh2_session_last_error(s->session,
b38b0f
-                                                  &ssh_err, NULL, 0);
b38b0f
-        /* See <libssh2_sftp.h>. */
b38b0f
-        sftp_err_code = libssh2_sftp_last_error((s)->sftp);
b38b0f
+        /* This is not an errno.  See <libssh/libssh.h>. */
b38b0f
+        ssh_err = ssh_get_error(s->session);
b38b0f
+        ssh_err_code = ssh_get_error_code(s->session);
b38b0f
+        /* See <libssh/sftp.h>. */
b38b0f
+        sftp_err_code = sftp_get_error(s->sftp);
b38b0f
 
b38b0f
         error_setg(errp,
b38b0f
-                   "%s: %s (libssh2 error code: %d, sftp error code: %lu)",
b38b0f
+                   "%s: %s (libssh error code: %d, sftp error code: %d)",
b38b0f
                    msg, ssh_err, ssh_err_code, sftp_err_code);
b38b0f
     } else {
b38b0f
         error_setg(errp, "%s", msg);
b38b0f
@@ -171,15 +163,15 @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
b38b0f
 
b38b0f
 static void sftp_error_trace(BDRVSSHState *s, const char *op)
b38b0f
 {
b38b0f
-    char *ssh_err;
b38b0f
+    const char *ssh_err;
b38b0f
     int ssh_err_code;
b38b0f
-    unsigned long sftp_err_code;
b38b0f
+    int sftp_err_code;
b38b0f
 
b38b0f
-    /* This is not an errno.  See <libssh2.h>. */
b38b0f
-    ssh_err_code = libssh2_session_last_error(s->session,
b38b0f
-                                              &ssh_err, NULL, 0);
b38b0f
-    /* See <libssh2_sftp.h>. */
b38b0f
-    sftp_err_code = libssh2_sftp_last_error((s)->sftp);
b38b0f
+    /* This is not an errno.  See <libssh/libssh.h>. */
b38b0f
+    ssh_err = ssh_get_error(s->session);
b38b0f
+    ssh_err_code = ssh_get_error_code(s->session);
b38b0f
+    /* See <libssh/sftp.h>. */
b38b0f
+    sftp_err_code = sftp_get_error(s->sftp);
b38b0f
 
b38b0f
     trace_sftp_error(op, ssh_err, ssh_err_code, sftp_err_code);
b38b0f
 }
b38b0f
@@ -280,82 +272,120 @@ static void ssh_parse_filename(const char *filename, QDict *options,
b38b0f
     parse_uri(filename, options, errp);
b38b0f
 }
b38b0f
 
b38b0f
-static int check_host_key_knownhosts(BDRVSSHState *s,
b38b0f
-                                     const char *host, int port, Error **errp)
b38b0f
+static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
b38b0f
 {
b38b0f
-    const char *home;
b38b0f
-    char *knh_file = NULL;
b38b0f
-    LIBSSH2_KNOWNHOSTS *knh = NULL;
b38b0f
-    struct libssh2_knownhost *found;
b38b0f
-    int ret, r;
b38b0f
-    const char *hostkey;
b38b0f
-    size_t len;
b38b0f
-    int type;
b38b0f
-
b38b0f
-    hostkey = libssh2_session_hostkey(s->session, &len, &type);
b38b0f
-    if (!hostkey) {
b38b0f
+    int ret;
b38b0f
+#ifdef HAVE_LIBSSH_0_8
b38b0f
+    enum ssh_known_hosts_e state;
b38b0f
+    int r;
b38b0f
+    ssh_key pubkey;
b38b0f
+    enum ssh_keytypes_e pubkey_type;
b38b0f
+    unsigned char *server_hash = NULL;
b38b0f
+    size_t server_hash_len;
b38b0f
+    char *fingerprint = NULL;
b38b0f
+
b38b0f
+    state = ssh_session_is_known_server(s->session);
b38b0f
+    trace_ssh_server_status(state);
b38b0f
+
b38b0f
+    switch (state) {
b38b0f
+    case SSH_KNOWN_HOSTS_OK:
b38b0f
+        /* OK */
b38b0f
+        trace_ssh_check_host_key_knownhosts();
b38b0f
+        break;
b38b0f
+    case SSH_KNOWN_HOSTS_CHANGED:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s, "failed to read remote host key");
b38b0f
+        r = ssh_get_server_publickey(s->session, &pubkey);
b38b0f
+        if (r == 0) {
b38b0f
+            r = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256,
b38b0f
+                                       &server_hash, &server_hash_len);
b38b0f
+            pubkey_type = ssh_key_type(pubkey);
b38b0f
+            ssh_key_free(pubkey);
b38b0f
+        }
b38b0f
+        if (r == 0) {
b38b0f
+            fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256,
b38b0f
+                                                   server_hash,
b38b0f
+                                                   server_hash_len);
b38b0f
+            ssh_clean_pubkey_hash(&server_hash);
b38b0f
+        }
b38b0f
+        if (fingerprint) {
b38b0f
+            error_setg(errp,
b38b0f
+                       "host key (%s key with fingerprint %s) does not match "
b38b0f
+                       "the one in known_hosts; this may be a possible attack",
b38b0f
+                       ssh_key_type_to_char(pubkey_type), fingerprint);
b38b0f
+            ssh_string_free_char(fingerprint);
b38b0f
+        } else  {
b38b0f
+            error_setg(errp,
b38b0f
+                       "host key does not match the one in known_hosts; this "
b38b0f
+                       "may be a possible attack");
b38b0f
+        }
b38b0f
         goto out;
b38b0f
-    }
b38b0f
-
b38b0f
-    knh = libssh2_knownhost_init(s->session);
b38b0f
-    if (!knh) {
b38b0f
+    case SSH_KNOWN_HOSTS_OTHER:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s,
b38b0f
-                           "failed to initialize known hosts support");
b38b0f
+        error_setg(errp,
b38b0f
+                   "host key for this server not found, another type exists");
b38b0f
+        goto out;
b38b0f
+    case SSH_KNOWN_HOSTS_UNKNOWN:
b38b0f
+        ret = -EINVAL;
b38b0f
+        error_setg(errp, "no host key was found in known_hosts");
b38b0f
+        goto out;
b38b0f
+    case SSH_KNOWN_HOSTS_NOT_FOUND:
b38b0f
+        ret = -ENOENT;
b38b0f
+        error_setg(errp, "known_hosts file not found");
b38b0f
+        goto out;
b38b0f
+    case SSH_KNOWN_HOSTS_ERROR:
b38b0f
+        ret = -EINVAL;
b38b0f
+        error_setg(errp, "error while checking the host");
b38b0f
+        goto out;
b38b0f
+    default:
b38b0f
+        ret = -EINVAL;
b38b0f
+        error_setg(errp, "error while checking for known server (%d)", state);
b38b0f
         goto out;
b38b0f
     }
b38b0f
+#else /* !HAVE_LIBSSH_0_8 */
b38b0f
+    int state;
b38b0f
 
b38b0f
-    home = getenv("HOME");
b38b0f
-    if (home) {
b38b0f
-        knh_file = g_strdup_printf("%s/.ssh/known_hosts", home);
b38b0f
-    } else {
b38b0f
-        knh_file = g_strdup_printf("/root/.ssh/known_hosts");
b38b0f
-    }
b38b0f
-
b38b0f
-    /* Read all known hosts from OpenSSH-style known_hosts file. */
b38b0f
-    libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
b38b0f
+    state = ssh_is_server_known(s->session);
b38b0f
+    trace_ssh_server_status(state);
b38b0f
 
b38b0f
-    r = libssh2_knownhost_checkp(knh, host, port, hostkey, len,
b38b0f
-                                 LIBSSH2_KNOWNHOST_TYPE_PLAIN|
b38b0f
-                                 LIBSSH2_KNOWNHOST_KEYENC_RAW,
b38b0f
-                                 &found);
b38b0f
-    switch (r) {
b38b0f
-    case LIBSSH2_KNOWNHOST_CHECK_MATCH:
b38b0f
+    switch (state) {
b38b0f
+    case SSH_SERVER_KNOWN_OK:
b38b0f
         /* OK */
b38b0f
-        trace_ssh_check_host_key_knownhosts(found->key);
b38b0f
+        trace_ssh_check_host_key_knownhosts();
b38b0f
         break;
b38b0f
-    case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
b38b0f
+    case SSH_SERVER_KNOWN_CHANGED:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s,
b38b0f
-                      "host key does not match the one in known_hosts"
b38b0f
-                      " (found key %s)", found->key);
b38b0f
+        error_setg(errp,
b38b0f
+                   "host key does not match the one in known_hosts; this "
b38b0f
+                   "may be a possible attack");
b38b0f
         goto out;
b38b0f
-    case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
b38b0f
+    case SSH_SERVER_FOUND_OTHER:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s, "no host key was found in known_hosts");
b38b0f
+        error_setg(errp,
b38b0f
+                   "host key for this server not found, another type exists");
b38b0f
         goto out;
b38b0f
-    case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
b38b0f
+    case SSH_SERVER_FILE_NOT_FOUND:
b38b0f
+        ret = -ENOENT;
b38b0f
+        error_setg(errp, "known_hosts file not found");
b38b0f
+        goto out;
b38b0f
+    case SSH_SERVER_NOT_KNOWN:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s,
b38b0f
-                      "failure matching the host key with known_hosts");
b38b0f
+        error_setg(errp, "no host key was found in known_hosts");
b38b0f
+        goto out;
b38b0f
+    case SSH_SERVER_ERROR:
b38b0f
+        ret = -EINVAL;
b38b0f
+        error_setg(errp, "server error");
b38b0f
         goto out;
b38b0f
     default:
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s, "unknown error matching the host key"
b38b0f
-                      " with known_hosts (%d)", r);
b38b0f
+        error_setg(errp, "error while checking for known server (%d)", state);
b38b0f
         goto out;
b38b0f
     }
b38b0f
+#endif /* !HAVE_LIBSSH_0_8 */
b38b0f
 
b38b0f
     /* known_hosts checking successful. */
b38b0f
     ret = 0;
b38b0f
 
b38b0f
  out:
b38b0f
-    if (knh != NULL) {
b38b0f
-        libssh2_knownhost_free(knh);
b38b0f
-    }
b38b0f
-    g_free(knh_file);
b38b0f
     return ret;
b38b0f
 }
b38b0f
 
b38b0f
@@ -399,18 +429,34 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
b38b0f
 
b38b0f
 static int
b38b0f
 check_host_key_hash(BDRVSSHState *s, const char *hash,
b38b0f
-                    int hash_type, size_t fingerprint_len, Error **errp)
b38b0f
+                    enum ssh_publickey_hash_type type, Error **errp)
b38b0f
 {
b38b0f
-    const char *fingerprint;
b38b0f
-
b38b0f
-    fingerprint = libssh2_hostkey_hash(s->session, hash_type);
b38b0f
-    if (!fingerprint) {
b38b0f
+    int r;
b38b0f
+    ssh_key pubkey;
b38b0f
+    unsigned char *server_hash;
b38b0f
+    size_t server_hash_len;
b38b0f
+
b38b0f
+#ifdef HAVE_LIBSSH_0_8
b38b0f
+    r = ssh_get_server_publickey(s->session, &pubkey);
b38b0f
+#else
b38b0f
+    r = ssh_get_publickey(s->session, &pubkey);
b38b0f
+#endif
b38b0f
+    if (r != SSH_OK) {
b38b0f
         session_error_setg(errp, s, "failed to read remote host key");
b38b0f
         return -EINVAL;
b38b0f
     }
b38b0f
 
b38b0f
-    if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
b38b0f
-                           hash) != 0) {
b38b0f
+    r = ssh_get_publickey_hash(pubkey, type, &server_hash, &server_hash_len);
b38b0f
+    ssh_key_free(pubkey);
b38b0f
+    if (r != 0) {
b38b0f
+        session_error_setg(errp, s,
b38b0f
+                           "failed reading the hash of the server SSH key");
b38b0f
+        return -EINVAL;
b38b0f
+    }
b38b0f
+
b38b0f
+    r = compare_fingerprint(server_hash, server_hash_len, hash);
b38b0f
+    ssh_clean_pubkey_hash(&server_hash);
b38b0f
+    if (r != 0) {
b38b0f
         error_setg(errp, "remote host key does not match host_key_check '%s'",
b38b0f
                    hash);
b38b0f
         return -EPERM;
b38b0f
@@ -419,8 +465,7 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
b38b0f
     return 0;
b38b0f
 }
b38b0f
 
b38b0f
-static int check_host_key(BDRVSSHState *s, const char *host, int port,
b38b0f
-                          SshHostKeyCheck *hkc, Error **errp)
b38b0f
+static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp)
b38b0f
 {
b38b0f
     SshHostKeyCheckMode mode;
b38b0f
 
b38b0f
@@ -436,15 +481,15 @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
b38b0f
     case SSH_HOST_KEY_CHECK_MODE_HASH:
b38b0f
         if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
b38b0f
             return check_host_key_hash(s, hkc->u.hash.hash,
b38b0f
-                                       LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
b38b0f
+                                       SSH_PUBLICKEY_HASH_MD5, errp);
b38b0f
         } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
b38b0f
             return check_host_key_hash(s, hkc->u.hash.hash,
b38b0f
-                                       LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
b38b0f
+                                       SSH_PUBLICKEY_HASH_SHA1, errp);
b38b0f
         }
b38b0f
         g_assert_not_reached();
b38b0f
         break;
b38b0f
     case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
b38b0f
-        return check_host_key_knownhosts(s, host, port, errp);
b38b0f
+        return check_host_key_knownhosts(s, errp);
b38b0f
     default:
b38b0f
         g_assert_not_reached();
b38b0f
     }
b38b0f
@@ -452,60 +497,43 @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
b38b0f
     return -EINVAL;
b38b0f
 }
b38b0f
 
b38b0f
-static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
b38b0f
+static int authenticate(BDRVSSHState *s, Error **errp)
b38b0f
 {
b38b0f
     int r, ret;
b38b0f
-    const char *userauthlist;
b38b0f
-    LIBSSH2_AGENT *agent = NULL;
b38b0f
-    struct libssh2_agent_publickey *identity;
b38b0f
-    struct libssh2_agent_publickey *prev_identity = NULL;
b38b0f
+    int method;
b38b0f
 
b38b0f
-    userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
b38b0f
-    if (strstr(userauthlist, "publickey") == NULL) {
b38b0f
+    /* Try to authenticate with the "none" method. */
b38b0f
+    r = ssh_userauth_none(s->session, NULL);
b38b0f
+    if (r == SSH_AUTH_ERROR) {
b38b0f
         ret = -EPERM;
b38b0f
-        error_setg(errp,
b38b0f
-                "remote server does not support \"publickey\" authentication");
b38b0f
+        session_error_setg(errp, s, "failed to authenticate using none "
b38b0f
+                                    "authentication");
b38b0f
         goto out;
b38b0f
-    }
b38b0f
-
b38b0f
-    /* Connect to ssh-agent and try each identity in turn. */
b38b0f
-    agent = libssh2_agent_init(s->session);
b38b0f
-    if (!agent) {
b38b0f
-        ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s, "failed to initialize ssh-agent support");
b38b0f
-        goto out;
b38b0f
-    }
b38b0f
-    if (libssh2_agent_connect(agent)) {
b38b0f
-        ret = -ECONNREFUSED;
b38b0f
-        session_error_setg(errp, s, "failed to connect to ssh-agent");
b38b0f
-        goto out;
b38b0f
-    }
b38b0f
-    if (libssh2_agent_list_identities(agent)) {
b38b0f
-        ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s,
b38b0f
-                           "failed requesting identities from ssh-agent");
b38b0f
+    } else if (r == SSH_AUTH_SUCCESS) {
b38b0f
+        /* Authenticated! */
b38b0f
+        ret = 0;
b38b0f
         goto out;
b38b0f
     }
b38b0f
 
b38b0f
-    for(;;) {
b38b0f
-        r = libssh2_agent_get_identity(agent, &identity, prev_identity);
b38b0f
-        if (r == 1) {           /* end of list */
b38b0f
-            break;
b38b0f
-        }
b38b0f
-        if (r < 0) {
b38b0f
+    method = ssh_userauth_list(s->session, NULL);
b38b0f
+    trace_ssh_auth_methods(method);
b38b0f
+
b38b0f
+    /*
b38b0f
+     * Try to authenticate with publickey, using the ssh-agent
b38b0f
+     * if available.
b38b0f
+     */
b38b0f
+    if (method & SSH_AUTH_METHOD_PUBLICKEY) {
b38b0f
+        r = ssh_userauth_publickey_auto(s->session, NULL, NULL);
b38b0f
+        if (r == SSH_AUTH_ERROR) {
b38b0f
             ret = -EINVAL;
b38b0f
-            session_error_setg(errp, s,
b38b0f
-                               "failed to obtain identity from ssh-agent");
b38b0f
+            session_error_setg(errp, s, "failed to authenticate using "
b38b0f
+                                        "publickey authentication");
b38b0f
             goto out;
b38b0f
-        }
b38b0f
-        r = libssh2_agent_userauth(agent, user, identity);
b38b0f
-        if (r == 0) {
b38b0f
+        } else if (r == SSH_AUTH_SUCCESS) {
b38b0f
             /* Authenticated! */
b38b0f
             ret = 0;
b38b0f
             goto out;
b38b0f
         }
b38b0f
-        /* Failed to authenticate with this identity, try the next one. */
b38b0f
-        prev_identity = identity;
b38b0f
     }
b38b0f
 
b38b0f
     ret = -EPERM;
b38b0f
@@ -513,13 +541,6 @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
b38b0f
                "and the identities held by your ssh-agent");
b38b0f
 
b38b0f
  out:
b38b0f
-    if (agent != NULL) {
b38b0f
-        /* Note: libssh2 implementation implicitly calls
b38b0f
-         * libssh2_agent_disconnect if necessary.
b38b0f
-         */
b38b0f
-        libssh2_agent_free(agent);
b38b0f
-    }
b38b0f
-
b38b0f
     return ret;
b38b0f
 }
b38b0f
 
b38b0f
@@ -638,7 +659,8 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
b38b0f
                           int ssh_flags, int creat_mode, Error **errp)
b38b0f
 {
b38b0f
     int r, ret;
b38b0f
-    long port = 0;
b38b0f
+    unsigned int port = 0;
b38b0f
+    int new_sock = -1;
b38b0f
 
b38b0f
     if (opts->has_user) {
b38b0f
         s->user = g_strdup(opts->user);
b38b0f
@@ -655,71 +677,147 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
b38b0f
     s->inet = opts->server;
b38b0f
     opts->server = NULL;
b38b0f
 
b38b0f
-    if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
b38b0f
+    if (qemu_strtoui(s->inet->port, NULL, 10, &port) < 0) {
b38b0f
         error_setg(errp, "Use only numeric port value");
b38b0f
         ret = -EINVAL;
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
     /* Open the socket and connect. */
b38b0f
-    s->sock = inet_connect_saddr(s->inet, errp);
b38b0f
-    if (s->sock < 0) {
b38b0f
+    new_sock = inet_connect_saddr(s->inet, errp);
b38b0f
+    if (new_sock < 0) {
b38b0f
         ret = -EIO;
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
+    /*
b38b0f
+     * Try to disable the Nagle algorithm on TCP sockets to reduce latency,
b38b0f
+     * but do not fail if it cannot be disabled.
b38b0f
+     */
b38b0f
+    r = socket_set_nodelay(new_sock);
b38b0f
+    if (r < 0) {
b38b0f
+        warn_report("can't set TCP_NODELAY for the ssh server %s: %s",
b38b0f
+                    s->inet->host, strerror(errno));
b38b0f
+    }
b38b0f
+
b38b0f
     /* Create SSH session. */
b38b0f
-    s->session = libssh2_session_init();
b38b0f
+    s->session = ssh_new();
b38b0f
     if (!s->session) {
b38b0f
         ret = -EINVAL;
b38b0f
-        session_error_setg(errp, s, "failed to initialize libssh2 session");
b38b0f
+        session_error_setg(errp, s, "failed to initialize libssh session");
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
-#if TRACE_LIBSSH2 != 0
b38b0f
-    libssh2_trace(s->session, TRACE_LIBSSH2);
b38b0f
-#endif
b38b0f
+    /*
b38b0f
+     * Make sure we are in blocking mode during the connection and
b38b0f
+     * authentication phases.
b38b0f
+     */
b38b0f
+    ssh_set_blocking(s->session, 1);
b38b0f
 
b38b0f
-    r = libssh2_session_handshake(s->session, s->sock);
b38b0f
-    if (r != 0) {
b38b0f
+    r = ssh_options_set(s->session, SSH_OPTIONS_USER, s->user);
b38b0f
+    if (r < 0) {
b38b0f
+        ret = -EINVAL;
b38b0f
+        session_error_setg(errp, s,
b38b0f
+                           "failed to set the user in the libssh session");
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+
b38b0f
+    r = ssh_options_set(s->session, SSH_OPTIONS_HOST, s->inet->host);
b38b0f
+    if (r < 0) {
b38b0f
+        ret = -EINVAL;
b38b0f
+        session_error_setg(errp, s,
b38b0f
+                           "failed to set the host in the libssh session");
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+
b38b0f
+    if (port > 0) {
b38b0f
+        r = ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
b38b0f
+        if (r < 0) {
b38b0f
+            ret = -EINVAL;
b38b0f
+            session_error_setg(errp, s,
b38b0f
+                               "failed to set the port in the libssh session");
b38b0f
+            goto err;
b38b0f
+        }
b38b0f
+    }
b38b0f
+
b38b0f
+    r = ssh_options_set(s->session, SSH_OPTIONS_COMPRESSION, "none");
b38b0f
+    if (r < 0) {
b38b0f
+        ret = -EINVAL;
b38b0f
+        session_error_setg(errp, s,
b38b0f
+                           "failed to disable the compression in the libssh "
b38b0f
+                           "session");
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+
b38b0f
+    /* Read ~/.ssh/config. */
b38b0f
+    r = ssh_options_parse_config(s->session, NULL);
b38b0f
+    if (r < 0) {
b38b0f
+        ret = -EINVAL;
b38b0f
+        session_error_setg(errp, s, "failed to parse ~/.ssh/config");
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+
b38b0f
+    r = ssh_options_set(s->session, SSH_OPTIONS_FD, &new_sock);
b38b0f
+    if (r < 0) {
b38b0f
+        ret = -EINVAL;
b38b0f
+        session_error_setg(errp, s,
b38b0f
+                           "failed to set the socket in the libssh session");
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+    /* libssh took ownership of the socket. */
b38b0f
+    s->sock = new_sock;
b38b0f
+    new_sock = -1;
b38b0f
+
b38b0f
+    /* Connect. */
b38b0f
+    r = ssh_connect(s->session);
b38b0f
+    if (r != SSH_OK) {
b38b0f
         ret = -EINVAL;
b38b0f
         session_error_setg(errp, s, "failed to establish SSH session");
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
     /* Check the remote host's key against known_hosts. */
b38b0f
-    ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
b38b0f
+    ret = check_host_key(s, opts->host_key_check, errp);
b38b0f
     if (ret < 0) {
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
     /* Authenticate. */
b38b0f
-    ret = authenticate(s, s->user, errp);
b38b0f
+    ret = authenticate(s, errp);
b38b0f
     if (ret < 0) {
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
     /* Start SFTP. */
b38b0f
-    s->sftp = libssh2_sftp_init(s->session);
b38b0f
+    s->sftp = sftp_new(s->session);
b38b0f
     if (!s->sftp) {
b38b0f
-        session_error_setg(errp, s, "failed to initialize sftp handle");
b38b0f
+        session_error_setg(errp, s, "failed to create sftp handle");
b38b0f
+        ret = -EINVAL;
b38b0f
+        goto err;
b38b0f
+    }
b38b0f
+
b38b0f
+    r = sftp_init(s->sftp);
b38b0f
+    if (r < 0) {
b38b0f
+        sftp_error_setg(errp, s, "failed to initialize sftp handle");
b38b0f
         ret = -EINVAL;
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
     /* Open the remote file. */
b38b0f
     trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode);
b38b0f
-    s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
b38b0f
-                                       creat_mode);
b38b0f
+    s->sftp_handle = sftp_open(s->sftp, opts->path, ssh_flags, creat_mode);
b38b0f
     if (!s->sftp_handle) {
b38b0f
-        session_error_setg(errp, s, "failed to open remote file '%s'",
b38b0f
-                           opts->path);
b38b0f
+        sftp_error_setg(errp, s, "failed to open remote file '%s'",
b38b0f
+                        opts->path);
b38b0f
         ret = -EINVAL;
b38b0f
         goto err;
b38b0f
     }
b38b0f
 
b38b0f
-    r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
b38b0f
-    if (r < 0) {
b38b0f
+    /* Make sure the SFTP file is handled in blocking mode. */
b38b0f
+    sftp_file_set_blocking(s->sftp_handle);
b38b0f
+
b38b0f
+    s->attrs = sftp_fstat(s->sftp_handle);
b38b0f
+    if (!s->attrs) {
b38b0f
         sftp_error_setg(errp, s, "failed to read file attributes");
b38b0f
         return -EINVAL;
b38b0f
     }
b38b0f
@@ -727,21 +825,27 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
b38b0f
     return 0;
b38b0f
 
b38b0f
  err:
b38b0f
+    if (s->attrs) {
b38b0f
+        sftp_attributes_free(s->attrs);
b38b0f
+    }
b38b0f
+    s->attrs = NULL;
b38b0f
     if (s->sftp_handle) {
b38b0f
-        libssh2_sftp_close(s->sftp_handle);
b38b0f
+        sftp_close(s->sftp_handle);
b38b0f
     }
b38b0f
     s->sftp_handle = NULL;
b38b0f
     if (s->sftp) {
b38b0f
-        libssh2_sftp_shutdown(s->sftp);
b38b0f
+        sftp_free(s->sftp);
b38b0f
     }
b38b0f
     s->sftp = NULL;
b38b0f
     if (s->session) {
b38b0f
-        libssh2_session_disconnect(s->session,
b38b0f
-                                   "from qemu ssh client: "
b38b0f
-                                   "error opening connection");
b38b0f
-        libssh2_session_free(s->session);
b38b0f
+        ssh_disconnect(s->session);
b38b0f
+        ssh_free(s->session);
b38b0f
     }
b38b0f
     s->session = NULL;
b38b0f
+    s->sock = -1;
b38b0f
+    if (new_sock >= 0) {
b38b0f
+        close(new_sock);
b38b0f
+    }
b38b0f
 
b38b0f
     return ret;
b38b0f
 }
b38b0f
@@ -756,9 +860,11 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
b38b0f
 
b38b0f
     ssh_state_init(s);
b38b0f
 
b38b0f
-    ssh_flags = LIBSSH2_FXF_READ;
b38b0f
+    ssh_flags = 0;
b38b0f
     if (bdrv_flags & BDRV_O_RDWR) {
b38b0f
-        ssh_flags |= LIBSSH2_FXF_WRITE;
b38b0f
+        ssh_flags |= O_RDWR;
b38b0f
+    } else {
b38b0f
+        ssh_flags |= O_RDONLY;
b38b0f
     }
b38b0f
 
b38b0f
     opts = ssh_parse_options(options, errp);
b38b0f
@@ -773,18 +879,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
b38b0f
     }
b38b0f
 
b38b0f
     /* Go non-blocking. */
b38b0f
-    libssh2_session_set_blocking(s->session, 0);
b38b0f
+    ssh_set_blocking(s->session, 0);
b38b0f
 
b38b0f
     qapi_free_BlockdevOptionsSsh(opts);
b38b0f
 
b38b0f
     return 0;
b38b0f
 
b38b0f
  err:
b38b0f
-    if (s->sock >= 0) {
b38b0f
-        close(s->sock);
b38b0f
-    }
b38b0f
-    s->sock = -1;
b38b0f
-
b38b0f
     qapi_free_BlockdevOptionsSsh(opts);
b38b0f
 
b38b0f
     return ret;
b38b0f
@@ -795,25 +896,25 @@ static int ssh_grow_file(BDRVSSHState *s, int64_t offset, Error **errp)
b38b0f
 {
b38b0f
     ssize_t ret;
b38b0f
     char c[1] = { '\0' };
b38b0f
-    int was_blocking = libssh2_session_get_blocking(s->session);
b38b0f
+    int was_blocking = ssh_is_blocking(s->session);
b38b0f
 
b38b0f
     /* offset must be strictly greater than the current size so we do
b38b0f
      * not overwrite anything */
b38b0f
-    assert(offset > 0 && offset > s->attrs.filesize);
b38b0f
+    assert(offset > 0 && offset > s->attrs->size);
b38b0f
 
b38b0f
-    libssh2_session_set_blocking(s->session, 1);
b38b0f
+    ssh_set_blocking(s->session, 1);
b38b0f
 
b38b0f
-    libssh2_sftp_seek64(s->sftp_handle, offset - 1);
b38b0f
-    ret = libssh2_sftp_write(s->sftp_handle, c, 1);
b38b0f
+    sftp_seek64(s->sftp_handle, offset - 1);
b38b0f
+    ret = sftp_write(s->sftp_handle, c, 1);
b38b0f
 
b38b0f
-    libssh2_session_set_blocking(s->session, was_blocking);
b38b0f
+    ssh_set_blocking(s->session, was_blocking);
b38b0f
 
b38b0f
     if (ret < 0) {
b38b0f
         sftp_error_setg(errp, s, "Failed to grow file");
b38b0f
         return -EIO;
b38b0f
     }
b38b0f
 
b38b0f
-    s->attrs.filesize = offset;
b38b0f
+    s->attrs->size = offset;
b38b0f
     return 0;
b38b0f
 }
b38b0f
 
b38b0f
@@ -841,8 +942,7 @@ static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
b38b0f
     ssh_state_init(&s);
b38b0f
 
b38b0f
     ret = connect_to_ssh(&s, opts->location,
b38b0f
-                         LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
b38b0f
-                         LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
b38b0f
+                         O_RDWR | O_CREAT | O_TRUNC,
b38b0f
                          0644, errp);
b38b0f
     if (ret < 0) {
b38b0f
         goto fail;
b38b0f
@@ -911,10 +1011,8 @@ static int ssh_has_zero_init(BlockDriverState *bs)
b38b0f
     /* Assume false, unless we can positively prove it's true. */
b38b0f
     int has_zero_init = 0;
b38b0f
 
b38b0f
-    if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
b38b0f
-        if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
b38b0f
-            has_zero_init = 1;
b38b0f
-        }
b38b0f
+    if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) {
b38b0f
+        has_zero_init = 1;
b38b0f
     }
b38b0f
 
b38b0f
     return has_zero_init;
b38b0f
@@ -951,12 +1049,12 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
b38b0f
         .co = qemu_coroutine_self()
b38b0f
     };
b38b0f
 
b38b0f
-    r = libssh2_session_block_directions(s->session);
b38b0f
+    r = ssh_get_poll_flags(s->session);
b38b0f
 
b38b0f
-    if (r & LIBSSH2_SESSION_BLOCK_INBOUND) {
b38b0f
+    if (r & SSH_READ_PENDING) {
b38b0f
         rd_handler = restart_coroutine;
b38b0f
     }
b38b0f
-    if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
b38b0f
+    if (r & SSH_WRITE_PENDING) {
b38b0f
         wr_handler = restart_coroutine;
b38b0f
     }
b38b0f
 
b38b0f
@@ -968,33 +1066,6 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
b38b0f
     trace_ssh_co_yield_back(s->sock);
b38b0f
 }
b38b0f
 
b38b0f
-/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
b38b0f
- * in the remote file.  Notice that it just updates a field in the
b38b0f
- * sftp_handle structure, so there is no network traffic and it cannot
b38b0f
- * fail.
b38b0f
- *
b38b0f
- * However, `libssh2_sftp_seek64' does have a catastrophic effect on
b38b0f
- * performance since it causes the handle to throw away all in-flight
b38b0f
- * reads and buffered readahead data.  Therefore this function tries
b38b0f
- * to be intelligent about when to call the underlying libssh2 function.
b38b0f
- */
b38b0f
-#define SSH_SEEK_WRITE 0
b38b0f
-#define SSH_SEEK_READ  1
b38b0f
-#define SSH_SEEK_FORCE 2
b38b0f
-
b38b0f
-static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
b38b0f
-{
b38b0f
-    bool op_read = (flags & SSH_SEEK_READ) != 0;
b38b0f
-    bool force = (flags & SSH_SEEK_FORCE) != 0;
b38b0f
-
b38b0f
-    if (force || op_read != s->offset_op_read || offset != s->offset) {
b38b0f
-        trace_ssh_seek(offset);
b38b0f
-        libssh2_sftp_seek64(s->sftp_handle, offset);
b38b0f
-        s->offset = offset;
b38b0f
-        s->offset_op_read = op_read;
b38b0f
-    }
b38b0f
-}
b38b0f
-
b38b0f
 static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
b38b0f
                                  int64_t offset, size_t size,
b38b0f
                                  QEMUIOVector *qiov)
b38b0f
@@ -1006,7 +1077,8 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
b38b0f
 
b38b0f
     trace_ssh_read(offset, size);
b38b0f
 
b38b0f
-    ssh_seek(s, offset, SSH_SEEK_READ);
b38b0f
+    trace_ssh_seek(offset);
b38b0f
+    sftp_seek64(s->sftp_handle, offset);
b38b0f
 
b38b0f
     /* This keeps track of the current iovec element ('i'), where we
b38b0f
      * will write to next ('buf'), and the end of the current iovec
b38b0f
@@ -1016,35 +1088,35 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
b38b0f
     buf = i->iov_base;
b38b0f
     end_of_vec = i->iov_base + i->iov_len;
b38b0f
 
b38b0f
-    /* libssh2 has a hard-coded limit of 2000 bytes per request,
b38b0f
-     * although it will also do readahead behind our backs.  Therefore
b38b0f
-     * we may have to do repeated reads here until we have read 'size'
b38b0f
-     * bytes.
b38b0f
-     */
b38b0f
     for (got = 0; got < size; ) {
b38b0f
+        size_t request_read_size;
b38b0f
     again:
b38b0f
-        trace_ssh_read_buf(buf, end_of_vec - buf);
b38b0f
-        r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
b38b0f
-        trace_ssh_read_return(r);
b38b0f
+        /*
b38b0f
+         * The size of SFTP packets is limited to 32K bytes, so limit
b38b0f
+         * the amount of data requested to 16K, as libssh currently
b38b0f
+         * does not handle multiple requests on its own.
b38b0f
+         */
b38b0f
+        request_read_size = MIN(end_of_vec - buf, 16384);
b38b0f
+        trace_ssh_read_buf(buf, end_of_vec - buf, request_read_size);
b38b0f
+        r = sftp_read(s->sftp_handle, buf, request_read_size);
b38b0f
+        trace_ssh_read_return(r, sftp_get_error(s->sftp));
b38b0f
 
b38b0f
-        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
b38b0f
+        if (r == SSH_AGAIN) {
b38b0f
             co_yield(s, bs);
b38b0f
             goto again;
b38b0f
         }
b38b0f
-        if (r < 0) {
b38b0f
-            sftp_error_trace(s, "read");
b38b0f
-            s->offset = -1;
b38b0f
-            return -EIO;
b38b0f
-        }
b38b0f
-        if (r == 0) {
b38b0f
+        if (r == SSH_EOF || (r == 0 && sftp_get_error(s->sftp) == SSH_FX_EOF)) {
b38b0f
             /* EOF: Short read so pad the buffer with zeroes and return it. */
b38b0f
             qemu_iovec_memset(qiov, got, 0, size - got);
b38b0f
             return 0;
b38b0f
         }
b38b0f
+        if (r <= 0) {
b38b0f
+            sftp_error_trace(s, "read");
b38b0f
+            return -EIO;
b38b0f
+        }
b38b0f
 
b38b0f
         got += r;
b38b0f
         buf += r;
b38b0f
-        s->offset += r;
b38b0f
         if (buf >= end_of_vec && got < size) {
b38b0f
             i++;
b38b0f
             buf = i->iov_base;
b38b0f
@@ -1081,7 +1153,8 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
b38b0f
 
b38b0f
     trace_ssh_write(offset, size);
b38b0f
 
b38b0f
-    ssh_seek(s, offset, SSH_SEEK_WRITE);
b38b0f
+    trace_ssh_seek(offset);
b38b0f
+    sftp_seek64(s->sftp_handle, offset);
b38b0f
 
b38b0f
     /* This keeps track of the current iovec element ('i'), where we
b38b0f
      * will read from next ('buf'), and the end of the current iovec
b38b0f
@@ -1092,46 +1165,37 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
b38b0f
     end_of_vec = i->iov_base + i->iov_len;
b38b0f
 
b38b0f
     for (written = 0; written < size; ) {
b38b0f
+        size_t request_write_size;
b38b0f
     again:
b38b0f
-        trace_ssh_write_buf(buf, end_of_vec - buf);
b38b0f
-        r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
b38b0f
-        trace_ssh_write_return(r);
b38b0f
+        /*
b38b0f
+         * Avoid too large data packets, as libssh currently does not
b38b0f
+         * handle multiple requests on its own.
b38b0f
+         */
b38b0f
+        request_write_size = MIN(end_of_vec - buf, 131072);
b38b0f
+        trace_ssh_write_buf(buf, end_of_vec - buf, request_write_size);
b38b0f
+        r = sftp_write(s->sftp_handle, buf, request_write_size);
b38b0f
+        trace_ssh_write_return(r, sftp_get_error(s->sftp));
b38b0f
 
b38b0f
-        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
b38b0f
+        if (r == SSH_AGAIN) {
b38b0f
             co_yield(s, bs);
b38b0f
             goto again;
b38b0f
         }
b38b0f
         if (r < 0) {
b38b0f
             sftp_error_trace(s, "write");
b38b0f
-            s->offset = -1;
b38b0f
             return -EIO;
b38b0f
         }
b38b0f
-        /* The libssh2 API is very unclear about this.  A comment in
b38b0f
-         * the code says "nothing was acked, and no EAGAIN was
b38b0f
-         * received!" which apparently means that no data got sent
b38b0f
-         * out, and the underlying channel didn't return any EAGAIN
b38b0f
-         * indication.  I think this is a bug in either libssh2 or
b38b0f
-         * OpenSSH (server-side).  In any case, forcing a seek (to
b38b0f
-         * discard libssh2 internal buffers), and then trying again
b38b0f
-         * works for me.
b38b0f
-         */
b38b0f
-        if (r == 0) {
b38b0f
-            ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
b38b0f
-            co_yield(s, bs);
b38b0f
-            goto again;
b38b0f
-        }
b38b0f
 
b38b0f
         written += r;
b38b0f
         buf += r;
b38b0f
-        s->offset += r;
b38b0f
         if (buf >= end_of_vec && written < size) {
b38b0f
             i++;
b38b0f
             buf = i->iov_base;
b38b0f
             end_of_vec = i->iov_base + i->iov_len;
b38b0f
         }
b38b0f
 
b38b0f
-        if (offset + written > s->attrs.filesize)
b38b0f
-            s->attrs.filesize = offset + written;
b38b0f
+        if (offset + written > s->attrs->size) {
b38b0f
+            s->attrs->size = offset + written;
b38b0f
+        }
b38b0f
     }
b38b0f
 
b38b0f
     return 0;
b38b0f
@@ -1164,24 +1228,24 @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
b38b0f
     }
b38b0f
 }
b38b0f
 
b38b0f
-#ifdef HAS_LIBSSH2_SFTP_FSYNC
b38b0f
+#ifdef HAVE_LIBSSH_0_8
b38b0f
 
b38b0f
 static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
b38b0f
 {
b38b0f
     int r;
b38b0f
 
b38b0f
     trace_ssh_flush();
b38b0f
+
b38b0f
+    if (!sftp_extension_supported(s->sftp, "fsync@openssh.com", "1")) {
b38b0f
+        unsafe_flush_warning(s, "OpenSSH >= 6.3");
b38b0f
+        return 0;
b38b0f
+    }
b38b0f
  again:
b38b0f
-    r = libssh2_sftp_fsync(s->sftp_handle);
b38b0f
-    if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
b38b0f
+    r = sftp_fsync(s->sftp_handle);
b38b0f
+    if (r == SSH_AGAIN) {
b38b0f
         co_yield(s, bs);
b38b0f
         goto again;
b38b0f
     }
b38b0f
-    if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
b38b0f
-        libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
b38b0f
-        unsafe_flush_warning(s, "OpenSSH >= 6.3");
b38b0f
-        return 0;
b38b0f
-    }
b38b0f
     if (r < 0) {
b38b0f
         sftp_error_trace(s, "fsync");
b38b0f
         return -EIO;
b38b0f
@@ -1202,25 +1266,25 @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
b38b0f
     return ret;
b38b0f
 }
b38b0f
 
b38b0f
-#else /* !HAS_LIBSSH2_SFTP_FSYNC */
b38b0f
+#else /* !HAVE_LIBSSH_0_8 */
b38b0f
 
b38b0f
 static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
b38b0f
 {
b38b0f
     BDRVSSHState *s = bs->opaque;
b38b0f
 
b38b0f
-    unsafe_flush_warning(s, "libssh2 >= 1.4.4");
b38b0f
+    unsafe_flush_warning(s, "libssh >= 0.8.0");
b38b0f
     return 0;
b38b0f
 }
b38b0f
 
b38b0f
-#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
b38b0f
+#endif /* !HAVE_LIBSSH_0_8 */
b38b0f
 
b38b0f
 static int64_t ssh_getlength(BlockDriverState *bs)
b38b0f
 {
b38b0f
     BDRVSSHState *s = bs->opaque;
b38b0f
     int64_t length;
b38b0f
 
b38b0f
-    /* Note we cannot make a libssh2 call here. */
b38b0f
-    length = (int64_t) s->attrs.filesize;
b38b0f
+    /* Note we cannot make a libssh call here. */
b38b0f
+    length = (int64_t) s->attrs->size;
b38b0f
     trace_ssh_getlength(length);
b38b0f
 
b38b0f
     return length;
b38b0f
@@ -1237,12 +1301,12 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
b38b0f
         return -ENOTSUP;
b38b0f
     }
b38b0f
 
b38b0f
-    if (offset < s->attrs.filesize) {
b38b0f
+    if (offset < s->attrs->size) {
b38b0f
         error_setg(errp, "ssh driver does not support shrinking files");
b38b0f
         return -ENOTSUP;
b38b0f
     }
b38b0f
 
b38b0f
-    if (offset == s->attrs.filesize) {
b38b0f
+    if (offset == s->attrs->size) {
b38b0f
         return 0;
b38b0f
     }
b38b0f
 
b38b0f
@@ -1307,12 +1371,16 @@ static void bdrv_ssh_init(void)
b38b0f
 {
b38b0f
     int r;
b38b0f
 
b38b0f
-    r = libssh2_init(0);
b38b0f
+    r = ssh_init();
b38b0f
     if (r != 0) {
b38b0f
-        fprintf(stderr, "libssh2 initialization failed, %d\n", r);
b38b0f
+        fprintf(stderr, "libssh initialization failed, %d\n", r);
b38b0f
         exit(EXIT_FAILURE);
b38b0f
     }
b38b0f
 
b38b0f
+#if TRACE_LIBSSH != 0
b38b0f
+    ssh_set_log_level(TRACE_LIBSSH);
b38b0f
+#endif
b38b0f
+
b38b0f
     bdrv_register(&bdrv_ssh);
b38b0f
 }
b38b0f
 
b38b0f
diff --git a/block/trace-events b/block/trace-events
b38b0f
index 23c9963..0017e33 100644
b38b0f
--- a/block/trace-events
b38b0f
+++ b/block/trace-events
b38b0f
@@ -154,19 +154,21 @@ nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pa
b38b0f
 # block/ssh.c
b38b0f
 ssh_restart_coroutine(void *co) "co=%p"
b38b0f
 ssh_flush(void) "fsync"
b38b0f
-ssh_check_host_key_knownhosts(const char *key) "host key OK: %s"
b38b0f
+ssh_check_host_key_knownhosts(void) "host key OK"
b38b0f
 ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o"
b38b0f
 ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p"
b38b0f
 ssh_co_yield_back(int sock) "s->sock=%d - back"
b38b0f
 ssh_getlength(int64_t length) "length=%" PRIi64
b38b0f
 ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64
b38b0f
 ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
b38b0f
-ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu"
b38b0f
-ssh_read_return(ssize_t ret) "sftp_read returned %zd"
b38b0f
+ssh_read_buf(void *buf, size_t size, size_t actual_size) "sftp_read buf=%p size=%zu (actual size=%zu)"
b38b0f
+ssh_read_return(ssize_t ret, int sftp_err) "sftp_read returned %zd (sftp error=%d)"
b38b0f
 ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
b38b0f
-ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu"
b38b0f
-ssh_write_return(ssize_t ret) "sftp_write returned %zd"
b38b0f
+ssh_write_buf(void *buf, size_t size, size_t actual_size) "sftp_write buf=%p size=%zu (actual size=%zu)"
b38b0f
+ssh_write_return(ssize_t ret, int sftp_err) "sftp_write returned %zd (sftp error=%d)"
b38b0f
 ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
b38b0f
+ssh_auth_methods(int methods) "auth methods=0x%x"
b38b0f
+ssh_server_status(int status) "server status=%d"
b38b0f
 
b38b0f
 # ssh.c
b38b0f
-sftp_error(const char *op, const char *ssh_err, int ssh_err_code, unsigned long sftp_err_code) "%s failed: %s (libssh2 error code: %d, sftp error code: %lu)"
b38b0f
+sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
b38b0f
diff --git a/configure b/configure
b38b0f
index c9a1034..eefec38 100755
b38b0f
--- a/configure
b38b0f
+++ b/configure
b38b0f
@@ -445,7 +445,7 @@ gcrypt_kdf="no"
b38b0f
 vte=""
b38b0f
 virglrenderer=""
b38b0f
 tpm="yes"
b38b0f
-libssh2=""
b38b0f
+libssh=""
b38b0f
 live_block_migration="yes"
b38b0f
 numa=""
b38b0f
 tcmalloc="no"
b38b0f
@@ -1334,9 +1334,9 @@ for opt do
b38b0f
   ;;
b38b0f
   --enable-tpm) tpm="yes"
b38b0f
   ;;
b38b0f
-  --disable-libssh2) libssh2="no"
b38b0f
+  --disable-libssh) libssh="no"
b38b0f
   ;;
b38b0f
-  --enable-libssh2) libssh2="yes"
b38b0f
+  --enable-libssh) libssh="yes"
b38b0f
   ;;
b38b0f
   --disable-live-block-migration) live_block_migration="no"
b38b0f
   ;;
b38b0f
@@ -1669,7 +1669,7 @@ disabled with --disable-FEATURE, default is enabled if available:
b38b0f
   coroutine-pool  coroutine freelist (better performance)
b38b0f
   glusterfs       GlusterFS backend
b38b0f
   tpm             TPM support
b38b0f
-  libssh2         ssh block device support
b38b0f
+  libssh          ssh block device support
b38b0f
   numa            libnuma support
b38b0f
   libxml2         for Parallels image format
b38b0f
   tcmalloc        tcmalloc support
b38b0f
@@ -3656,43 +3656,34 @@ EOF
b38b0f
 fi
b38b0f
 
b38b0f
 ##########################################
b38b0f
-# libssh2 probe
b38b0f
-min_libssh2_version=1.2.8
b38b0f
-if test "$libssh2" != "no" ; then
b38b0f
-  if $pkg_config --atleast-version=$min_libssh2_version libssh2; then
b38b0f
-    libssh2_cflags=$($pkg_config libssh2 --cflags)
b38b0f
-    libssh2_libs=$($pkg_config libssh2 --libs)
b38b0f
-    libssh2=yes
b38b0f
+# libssh probe
b38b0f
+if test "$libssh" != "no" ; then
b38b0f
+  if $pkg_config --exists libssh; then
b38b0f
+    libssh_cflags=$($pkg_config libssh --cflags)
b38b0f
+    libssh_libs=$($pkg_config libssh --libs)
b38b0f
+    libssh=yes
b38b0f
   else
b38b0f
-    if test "$libssh2" = "yes" ; then
b38b0f
-      error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
b38b0f
+    if test "$libssh" = "yes" ; then
b38b0f
+      error_exit "libssh required for --enable-libssh"
b38b0f
     fi
b38b0f
-    libssh2=no
b38b0f
+    libssh=no
b38b0f
   fi
b38b0f
 fi
b38b0f
 
b38b0f
 ##########################################
b38b0f
-# libssh2_sftp_fsync probe
b38b0f
+# Check for libssh 0.8
b38b0f
+# This is done like this instead of using the LIBSSH_VERSION_* and
b38b0f
+# SSH_VERSION_* macros because some distributions in the past shipped
b38b0f
+# snapshots of the future 0.8 from Git, and those snapshots did not
b38b0f
+# have updated version numbers (still referring to 0.7.0).
b38b0f
 
b38b0f
-if test "$libssh2" = "yes"; then
b38b0f
+if test "$libssh" = "yes"; then
b38b0f
   cat > $TMPC <
b38b0f
-#include <stdio.h>
b38b0f
-#include <libssh2.h>
b38b0f
-#include <libssh2_sftp.h>
b38b0f
-int main(void) {
b38b0f
-    LIBSSH2_SESSION *session;
b38b0f
-    LIBSSH2_SFTP *sftp;
b38b0f
-    LIBSSH2_SFTP_HANDLE *sftp_handle;
b38b0f
-    session = libssh2_session_init ();
b38b0f
-    sftp = libssh2_sftp_init (session);
b38b0f
-    sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
b38b0f
-    libssh2_sftp_fsync (sftp_handle);
b38b0f
-    return 0;
b38b0f
-}
b38b0f
+#include <libssh/libssh.h>
b38b0f
+int main(void) { return ssh_get_server_publickey(NULL, NULL); }
b38b0f
 EOF
b38b0f
-  # libssh2_cflags/libssh2_libs defined in previous test.
b38b0f
-  if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
b38b0f
-    QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
b38b0f
+  if compile_prog "$libssh_cflags" "$libssh_libs"; then
b38b0f
+    libssh_cflags="-DHAVE_LIBSSH_0_8 $libssh_cflags"
b38b0f
   fi
b38b0f
 fi
b38b0f
 
b38b0f
@@ -5998,7 +5989,7 @@ echo "GlusterFS support $glusterfs"
b38b0f
 echo "gcov              $gcov_tool"
b38b0f
 echo "gcov enabled      $gcov"
b38b0f
 echo "TPM support       $tpm"
b38b0f
-echo "libssh2 support   $libssh2"
b38b0f
+echo "libssh support    $libssh"
b38b0f
 echo "TPM passthrough   $tpm_passthrough"
b38b0f
 echo "TPM emulator      $tpm_emulator"
b38b0f
 echo "QOM debugging     $qom_cast_debug"
b38b0f
@@ -6664,10 +6655,10 @@ if test "$glusterfs_iocb_has_stat" = "yes" ; then
b38b0f
   echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
b38b0f
 fi
b38b0f
 
b38b0f
-if test "$libssh2" = "yes" ; then
b38b0f
-  echo "CONFIG_LIBSSH2=m" >> $config_host_mak
b38b0f
-  echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
b38b0f
-  echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
b38b0f
+if test "$libssh" = "yes" ; then
b38b0f
+  echo "CONFIG_LIBSSH=m" >> $config_host_mak
b38b0f
+  echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak
b38b0f
+  echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak
b38b0f
 fi
b38b0f
 
b38b0f
 if test "$live_block_migration" = "yes" ; then
b38b0f
diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi
b38b0f
index e0d752a..5004dec 100644
b38b0f
--- a/docs/qemu-block-drivers.texi
b38b0f
+++ b/docs/qemu-block-drivers.texi
b38b0f
@@ -782,7 +782,7 @@ print a warning when @code{fsync} is not supported:
b38b0f
 
b38b0f
 warning: ssh server @code{ssh.example.com:22} does not support fsync
b38b0f
 
b38b0f
-With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is
b38b0f
+With sufficiently new versions of libssh and OpenSSH, @code{fsync} is
b38b0f
 supported.
b38b0f
 
b38b0f
 @node disk_images_nvme
b38b0f
diff --git a/tests/docker/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker
b38b0f
index dd021f2..0a4970c 100644
b38b0f
--- a/tests/docker/dockerfiles/debian-win32-cross.docker
b38b0f
+++ b/tests/docker/dockerfiles/debian-win32-cross.docker
b38b0f
@@ -15,7 +15,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-curl \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-glib \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-libgcrypt \
b38b0f
-        mxe-$TARGET-w64-mingw32.shared-libssh2 \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-libusb1 \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-lzo \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-nettle \
b38b0f
diff --git a/tests/docker/dockerfiles/debian-win64-cross.docker b/tests/docker/dockerfiles/debian-win64-cross.docker
b38b0f
index 4542bcc..b27985b 100644
b38b0f
--- a/tests/docker/dockerfiles/debian-win64-cross.docker
b38b0f
+++ b/tests/docker/dockerfiles/debian-win64-cross.docker
b38b0f
@@ -15,7 +15,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-curl \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-glib \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-libgcrypt \
b38b0f
-        mxe-$TARGET-w64-mingw32.shared-libssh2 \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-libusb1 \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-lzo \
b38b0f
         mxe-$TARGET-w64-mingw32.shared-nettle \
b38b0f
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
b38b0f
index b706f42..2dd598d 100644
b38b0f
--- a/tests/docker/dockerfiles/fedora.docker
b38b0f
+++ b/tests/docker/dockerfiles/fedora.docker
b38b0f
@@ -6,18 +6,18 @@ ENV PACKAGES \
b38b0f
     bluez-libs-devel brlapi-devel bzip2-devel \
b38b0f
     device-mapper-multipath-devel glusterfs-api-devel gnutls-devel \
b38b0f
     gtk3-devel libattr-devel libcap-devel libcap-ng-devel libcurl-devel \
b38b0f
-    libjpeg-devel libpng-devel librbd-devel libssh2-devel libusbx-devel \
b38b0f
+    libjpeg-devel libpng-devel librbd-devel libssh-devel libusbx-devel \
b38b0f
     libxml2-devel lzo-devel ncurses-devel nettle-devel nss-devel \
b38b0f
     numactl-devel SDL2-devel snappy-devel spice-server-devel \
b38b0f
     systemtap-sdt-devel usbredir-devel virglrenderer-devel vte3-devel \
b38b0f
     xen-devel \
b38b0f
     mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config \
b38b0f
     mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \
b38b0f
-    mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2 \
b38b0f
+    mingw32-libjpeg-turbo mingw32-libpng mingw32-curl \
b38b0f
     mingw32-bzip2 \
b38b0f
     mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL mingw64-pkg-config \
b38b0f
     mingw64-gtk2 mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \
b38b0f
-    mingw64-libjpeg-turbo mingw64-libpng mingw64-curl mingw64-libssh2 \
b38b0f
+    mingw64-libjpeg-turbo mingw64-libpng mingw64-curl \
b38b0f
     mingw64-bzip2
b38b0f
 ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3
b38b0f
 
b38b0f
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
b38b0f
index dabbf2a..be37ff1 100644
b38b0f
--- a/tests/docker/dockerfiles/ubuntu.docker
b38b0f
+++ b/tests/docker/dockerfiles/ubuntu.docker
b38b0f
@@ -4,7 +4,7 @@ RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \
b38b0f
 RUN apt-get update
b38b0f
 ENV PACKAGES flex bison \
b38b0f
     libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \
b38b0f
-    libseccomp-dev libgnutls-dev libssh2-1-dev  libspice-server-dev \
b38b0f
+    libseccomp-dev libgnutls-dev libssh-dev  libspice-server-dev \
b38b0f
     libspice-protocol-dev libnss3-dev libfdt-dev \
b38b0f
     libgtk-3-dev libvte-2.91-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \
b38b0f
     libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \
b38b0f
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
b38b0f
index aaad656..219f1a6 100755
b38b0f
--- a/tests/qemu-iotests/207
b38b0f
+++ b/tests/qemu-iotests/207
b38b0f
@@ -106,12 +106,49 @@ with iotests.FilePath('t.img') as disk_path, \
b38b0f
 
b38b0f
     iotests.img_info_log(remote_path)
b38b0f
 
b38b0f
-    md5_key = subprocess.check_output(
b38b0f
-        'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
b38b0f
-        'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1',
b38b0f
-        shell=True).rstrip().decode('ascii')
b38b0f
+    keys = subprocess.check_output(
b38b0f
+        'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
b38b0f
+        'cut -d" " -f3',
b38b0f
+        shell=True).rstrip().decode('ascii').split('\n')
b38b0f
+
b38b0f
+    # Mappings of base64 representations to digests
b38b0f
+    md5_keys = {}
b38b0f
+    sha1_keys = {}
b38b0f
+
b38b0f
+    for key in keys:
b38b0f
+        md5_keys[key] = subprocess.check_output(
b38b0f
+            'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
b38b0f
+            shell=True).rstrip().decode('ascii')
b38b0f
+
b38b0f
+        sha1_keys[key] = subprocess.check_output(
b38b0f
+            'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
b38b0f
+            shell=True).rstrip().decode('ascii')
b38b0f
 
b38b0f
     vm.launch()
b38b0f
+
b38b0f
+    # Find correct key first
b38b0f
+    matching_key = None
b38b0f
+    for key in keys:
b38b0f
+        result = vm.qmp('blockdev-add',
b38b0f
+                        driver='ssh', node_name='node0', path=disk_path,
b38b0f
+                        server={
b38b0f
+                             'host': '127.0.0.1',
b38b0f
+                             'port': '22',
b38b0f
+                        }, host_key_check={
b38b0f
+                             'mode': 'hash',
b38b0f
+                             'type': 'md5',
b38b0f
+                             'hash': md5_keys[key],
b38b0f
+                        })
b38b0f
+
b38b0f
+        if 'error' not in result:
b38b0f
+            vm.qmp('blockdev-del', node_name='node0')
b38b0f
+            matching_key = key
b38b0f
+            break
b38b0f
+
b38b0f
+    if matching_key is None:
b38b0f
+        vm.shutdown()
b38b0f
+        iotests.notrun('Did not find a key that fits 127.0.0.1')
b38b0f
+
b38b0f
     blockdev_create(vm, { 'driver': 'ssh',
b38b0f
                           'location': {
b38b0f
                               'path': disk_path,
b38b0f
@@ -136,7 +173,7 @@ with iotests.FilePath('t.img') as disk_path, \
b38b0f
                               'host-key-check': {
b38b0f
                                   'mode': 'hash',
b38b0f
                                   'type': 'md5',
b38b0f
-                                  'hash': md5_key,
b38b0f
+                                  'hash': md5_keys[matching_key],
b38b0f
                               }
b38b0f
                           },
b38b0f
                           'size': 8388608 })
b38b0f
@@ -144,11 +181,6 @@ with iotests.FilePath('t.img') as disk_path, \
b38b0f
 
b38b0f
     iotests.img_info_log(remote_path)
b38b0f
 
b38b0f
-    sha1_key = subprocess.check_output(
b38b0f
-        'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
b38b0f
-        'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1',
b38b0f
-        shell=True).rstrip().decode('ascii')
b38b0f
-
b38b0f
     vm.launch()
b38b0f
     blockdev_create(vm, { 'driver': 'ssh',
b38b0f
                           'location': {
b38b0f
@@ -174,7 +206,7 @@ with iotests.FilePath('t.img') as disk_path, \
b38b0f
                               'host-key-check': {
b38b0f
                                   'mode': 'hash',
b38b0f
                                   'type': 'sha1',
b38b0f
-                                  'hash': sha1_key,
b38b0f
+                                  'hash': sha1_keys[matching_key],
b38b0f
                               }
b38b0f
                           },
b38b0f
                           'size': 4194304 })
b38b0f
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
b38b0f
index 789b465..48318ce 100644
b38b0f
--- a/tests/qemu-iotests/207.out
b38b0f
+++ b/tests/qemu-iotests/207.out
b38b0f
@@ -68,7 +68,7 @@ virtual size: 4.0M (4194304 bytes)
b38b0f
 
b38b0f
 {"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
b38b0f
 {"return": {}}
b38b0f
-Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
b38b0f
+Job failed: failed to open remote file '/this/is/not/an/existing/path': SFTP server: No such file (libssh error code: 1, sftp error code: 2)
b38b0f
 {"execute": "job-dismiss", "arguments": {"id": "job0"}}
b38b0f
 {"return": {}}
b38b0f
 
b38b0f
-- 
b38b0f
1.8.3.1
b38b0f