cryptospore / rpms / qemu-kvm

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