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