From nobody Thu May 2 12:09:07 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1510763324813835.5271989772249; Wed, 15 Nov 2017 08:28:44 -0800 (PST) Received: from localhost ([::1]:36822 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eF0YG-0002Vm-7q for importer@patchew.org; Wed, 15 Nov 2017 11:28:36 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49055) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eF0Wr-0001jv-MW for qemu-devel@nongnu.org; Wed, 15 Nov 2017 11:27:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eF0Wo-0007Gc-4C for qemu-devel@nongnu.org; Wed, 15 Nov 2017 11:27:09 -0500 Received: from mx1.redhat.com ([209.132.183.28]:36356) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1eF0Wg-000780-1t; Wed, 15 Nov 2017 11:26:58 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5B6A662676; Wed, 15 Nov 2017 16:26:55 +0000 (UTC) Received: from thyrus.usersys.redhat.com (unknown [10.34.244.82]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1E2F911E7D7; Wed, 15 Nov 2017 16:26:52 +0000 (UTC) From: Pino Toscano To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Wed, 15 Nov 2017 17:26:48 +0100 Message-Id: <20171115162648.30135-1-ptoscano@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Wed, 15 Nov 2017 16:26:55 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v3] ssh: switch from libssh2 to libssh X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, jcody@redhat.com, ptoscano@redhat.com, rjones@redhat.com, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Rewrite the implementation of the ssh block driver to use libssh instead of libssh2. The libssh library has various advantages over libssh2: - easier API for authentication (for example for using ssh-agent) - easier API for known_hosts handling - supports newer types of keys in known_hosts Kerberos authentication can be enabled once the libssh bug for it [1] is fixed. The development version of libssh (i.e. the future 0.8.x) supports fsync, so reuse the build time check for this. [1] https://red.libssh.org/issues/242 Signed-off-by: Pino Toscano Tested-by: Richard W.M. Jones --- Changes from v2: - used again an own fd - fixed co_yield() implementation Changes from v1: - fixed jumbo packets writing - fixed missing 'err' assignment - fixed commit message block/Makefile.objs | 6 +- block/ssh.c | 494 ++++++++++++++++++++++++------------------------= ---- configure | 65 ++++--- 3 files changed, 259 insertions(+), 306 deletions(-) diff --git a/block/Makefile.objs b/block/Makefile.objs index 6eaf78a046..c0df21d0b4 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -20,7 +20,7 @@ block-obj-$(CONFIG_CURL) +=3D curl.o block-obj-$(CONFIG_RBD) +=3D rbd.o block-obj-$(CONFIG_GLUSTERFS) +=3D gluster.o block-obj-$(CONFIG_VXHS) +=3D vxhs.o -block-obj-$(CONFIG_LIBSSH2) +=3D ssh.o +block-obj-$(CONFIG_LIBSSH) +=3D ssh.o block-obj-y +=3D accounting.o dirty-bitmap.o block-obj-y +=3D write-threshold.o block-obj-y +=3D backup.o @@ -41,8 +41,8 @@ rbd.o-libs :=3D $(RBD_LIBS) gluster.o-cflags :=3D $(GLUSTERFS_CFLAGS) gluster.o-libs :=3D $(GLUSTERFS_LIBS) vxhs.o-libs :=3D $(VXHS_LIBS) -ssh.o-cflags :=3D $(LIBSSH2_CFLAGS) -ssh.o-libs :=3D $(LIBSSH2_LIBS) +ssh.o-cflags :=3D $(LIBSSH_CFLAGS) +ssh.o-libs :=3D $(LIBSSH_LIBS) block-obj-$(if $(CONFIG_BZIP2),m,n) +=3D dmg-bz2.o dmg-bz2.o-libs :=3D $(BZIP2_LIBS) qcow.o-libs :=3D -lz diff --git a/block/ssh.c b/block/ssh.c index b049a16eb9..9e96c9d684 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -24,8 +24,8 @@ =20 #include "qemu/osdep.h" =20 -#include -#include +#include +#include =20 #include "block/block_int.h" #include "qapi/error.h" @@ -41,14 +41,12 @@ /* DEBUG_SSH=3D1 enables the DPRINTF (debugging printf) statements in * this block driver code. * - * TRACE_LIBSSH2=3D enables tracing in libssh2 itself. Note - * that this requires that libssh2 was specially compiled with the - * `./configure --enable-debug' option, so most likely you will have - * to compile it yourself. The meaning of is described - * here: http://www.libssh2.org/libssh2_trace.html + * TRACE_LIBSSH=3D enables tracing in libssh itself. + * The meaning of is described here: + * http://api.libssh.org/master/group__libssh__log.html */ #define DEBUG_SSH 0 -#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ +#define TRACE_LIBSSH 0 /* see: SSH_LOG_* */ =20 #define DPRINTF(fmt, ...) \ do { \ @@ -64,18 +62,14 @@ typedef struct BDRVSSHState { =20 /* SSH connection. */ int sock; /* socket */ - LIBSSH2_SESSION *session; /* ssh session */ - LIBSSH2_SFTP *sftp; /* sftp session */ - LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */ + ssh_session session; /* ssh session */ + sftp_session sftp; /* sftp session */ + sftp_file sftp_handle; /* sftp remote file handle */ =20 - /* See ssh_seek() function below. */ - int64_t offset; - bool offset_op_read; - - /* File attributes at open. We try to keep the .filesize field + /* File attributes at open. We try to keep the .size field * updated if it changes (eg by writing at the end of the file). */ - LIBSSH2_SFTP_ATTRIBUTES attrs; + sftp_attributes attrs; =20 InetSocketAddress *inet; =20 @@ -87,26 +81,23 @@ static void ssh_state_init(BDRVSSHState *s) { memset(s, 0, sizeof *s); s->sock =3D -1; - s->offset =3D -1; qemu_co_mutex_init(&s->lock); } =20 static void ssh_state_free(BDRVSSHState *s) { + if (s->attrs) { + sftp_attributes_free(s->attrs); + } if (s->sftp_handle) { - libssh2_sftp_close(s->sftp_handle); + sftp_close(s->sftp_handle); } if (s->sftp) { - libssh2_sftp_shutdown(s->sftp); + sftp_free(s->sftp); } if (s->session) { - libssh2_session_disconnect(s->session, - "from qemu ssh client: " - "user closed the connection"); - libssh2_session_free(s->session); - } - if (s->sock >=3D 0) { - close(s->sock); + ssh_disconnect(s->session); + ssh_free(s->session); } } =20 @@ -121,13 +112,13 @@ session_error_setg(Error **errp, BDRVSSHState *s, con= st char *fs, ...) va_end(args); =20 if (s->session) { - char *ssh_err; + const char *ssh_err; int ssh_err_code; =20 - /* This is not an errno. See . */ - ssh_err_code =3D libssh2_session_last_error(s->session, - &ssh_err, NULL, 0); - error_setg(errp, "%s: %s (libssh2 error code: %d)", + /* This is not an errno. See . */ + ssh_err =3D ssh_get_error(s->session); + ssh_err_code =3D ssh_get_error_code(s->session); + error_setg(errp, "%s: %s (libssh error code: %d)", msg, ssh_err, ssh_err_code); } else { error_setg(errp, "%s", msg); @@ -146,19 +137,14 @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const = char *fs, ...) va_end(args); =20 if (s->sftp) { - char *ssh_err; - int ssh_err_code; - unsigned long sftp_err_code; + int sftp_err_code; =20 - /* This is not an errno. See . */ - ssh_err_code =3D libssh2_session_last_error(s->session, - &ssh_err, NULL, 0); - /* See . */ - sftp_err_code =3D libssh2_sftp_last_error((s)->sftp); + /* This is not an errno. See . */ + sftp_err_code =3D sftp_get_error(s->sftp); =20 error_setg(errp, - "%s: %s (libssh2 error code: %d, sftp error code: %lu)", - msg, ssh_err, ssh_err_code, sftp_err_code); + "%s (sftp error code: %d)", + msg, sftp_err_code); } else { error_setg(errp, "%s", msg); } @@ -174,18 +160,13 @@ sftp_error_report(BDRVSSHState *s, const char *fs, ..= .) error_vprintf(fs, args); =20 if ((s)->sftp) { - char *ssh_err; - int ssh_err_code; - unsigned long sftp_err_code; + int sftp_err_code; =20 - /* This is not an errno. See . */ - ssh_err_code =3D libssh2_session_last_error(s->session, - &ssh_err, NULL, 0); - /* See . */ - sftp_err_code =3D libssh2_sftp_last_error((s)->sftp); + /* This is not an errno. See . */ + sftp_err_code =3D sftp_get_error(s->sftp); =20 - error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)", - ssh_err, ssh_err_code, sftp_err_code); + error_printf(": (sftp error code: %d)", + sftp_err_code); } =20 va_end(args); @@ -291,68 +272,41 @@ static void ssh_parse_filename(const char *filename, = QDict *options, static int check_host_key_knownhosts(BDRVSSHState *s, const char *host, int port, Error **e= rrp) { - const char *home; - char *knh_file =3D NULL; - LIBSSH2_KNOWNHOSTS *knh =3D NULL; - struct libssh2_knownhost *found; - int ret, r; - const char *hostkey; - size_t len; - int type; + int ret; + int state; =20 - hostkey =3D libssh2_session_hostkey(s->session, &len, &type); - if (!hostkey) { - ret =3D -EINVAL; - session_error_setg(errp, s, "failed to read remote host key"); - goto out; - } + state =3D ssh_is_server_known(s->session); =20 - knh =3D libssh2_knownhost_init(s->session); - if (!knh) { - ret =3D -EINVAL; - session_error_setg(errp, s, - "failed to initialize known hosts support"); - goto out; - } - - home =3D getenv("HOME"); - if (home) { - knh_file =3D g_strdup_printf("%s/.ssh/known_hosts", home); - } else { - knh_file =3D g_strdup_printf("/root/.ssh/known_hosts"); - } - - /* Read all known hosts from OpenSSH-style known_hosts file. */ - libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENS= SH); - - r =3D libssh2_knownhost_checkp(knh, host, port, hostkey, len, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW, - &found); - switch (r) { - case LIBSSH2_KNOWNHOST_CHECK_MATCH: + switch (state) { + case SSH_SERVER_KNOWN_OK: /* OK */ - DPRINTF("host key OK: %s", found->key); break; - case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + case SSH_SERVER_KNOWN_CHANGED: ret =3D -EINVAL; session_error_setg(errp, s, - "host key does not match the one in known_hosts" - " (found key %s)", found->key); + "host key does not match the one in known_hosts"); goto out; - case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + case SSH_SERVER_FOUND_OTHER: + ret =3D -EINVAL; + session_error_setg(errp, s, + "host key for this server not found, another type " + "exists"); + goto out; + case SSH_SERVER_FILE_NOT_FOUND: + ret =3D -EINVAL; + session_error_setg(errp, s, "known_hosts file not found"); + goto out; + case SSH_SERVER_NOT_KNOWN: ret =3D -EINVAL; session_error_setg(errp, s, "no host key was found in known_hosts"= ); goto out; - case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + case SSH_SERVER_ERROR: ret =3D -EINVAL; - session_error_setg(errp, s, - "failure matching the host key with known_hosts"); + session_error_setg(errp, s, "server error"); goto out; default: ret =3D -EINVAL; - session_error_setg(errp, s, "unknown error matching the host key" - " with known_hosts (%d)", r); + session_error_setg(errp, s, "error while checking for known server= "); goto out; } =20 @@ -360,10 +314,6 @@ static int check_host_key_knownhosts(BDRVSSHState *s, ret =3D 0; =20 out: - if (knh !=3D NULL) { - libssh2_knownhost_free(knh); - } - g_free(knh_file); return ret; } =20 @@ -407,18 +357,31 @@ static int compare_fingerprint(const unsigned char *f= ingerprint, size_t len, =20 static int check_host_key_hash(BDRVSSHState *s, const char *hash, - int hash_type, size_t fingerprint_len, Error **errp) + enum ssh_publickey_hash_type type, size_t fingerprint_= len, + Error **errp) { - const char *fingerprint; + int r; + ssh_key pubkey; + unsigned char *server_hash; + size_t server_hash_len; =20 - fingerprint =3D libssh2_hostkey_hash(s->session, hash_type); - if (!fingerprint) { + r =3D ssh_get_publickey(s->session, &pubkey); + if (r < 0) { session_error_setg(errp, s, "failed to read remote host key"); return -EINVAL; } =20 - if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len, - hash) !=3D 0) { + r =3D ssh_get_publickey_hash(pubkey, type, &server_hash, &server_hash_= len); + ssh_key_free(pubkey); + if (r < 0) { + session_error_setg(errp, s, + "failed reading the hash of the server SSH key"= ); + return -EINVAL; + } + + r =3D compare_fingerprint(server_hash, server_hash_len, hash); + ssh_clean_pubkey_hash(&server_hash); + if (r !=3D 0) { error_setg(errp, "remote host key does not match host_key_check '%= s'", hash); return -EPERM; @@ -438,13 +401,13 @@ static int check_host_key(BDRVSSHState *s, const char= *host, int port, /* host_key_check=3Dmd5:xx:yy:zz:... */ if (strncmp(host_key_check, "md5:", 4) =3D=3D 0) { return check_host_key_hash(s, &host_key_check[4], - LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); + SSH_PUBLICKEY_HASH_MD5, 16, errp); } =20 /* host_key_check=3Dsha1:xx:yy:zz:... */ if (strncmp(host_key_check, "sha1:", 5) =3D=3D 0) { return check_host_key_hash(s, &host_key_check[5], - LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); + SSH_PUBLICKEY_HASH_SHA1, 20, errp); } =20 /* host_key_check=3Dyes */ @@ -459,57 +422,32 @@ static int check_host_key(BDRVSSHState *s, const char= *host, int port, static int authenticate(BDRVSSHState *s, const char *user, Error **errp) { int r, ret; - const char *userauthlist; - LIBSSH2_AGENT *agent =3D NULL; - struct libssh2_agent_publickey *identity; - struct libssh2_agent_publickey *prev_identity =3D NULL; + int method; =20 - userauthlist =3D libssh2_userauth_list(s->session, user, strlen(user)); - if (strstr(userauthlist, "publickey") =3D=3D NULL) { + r =3D ssh_userauth_none(s->session, NULL); + if (r =3D=3D SSH_AUTH_ERROR) { ret =3D -EPERM; - error_setg(errp, - "remote server does not support \"publickey\" authenticati= on"); + session_error_setg(errp, s, "failed to call ssh_userauth_none"); goto out; } =20 - /* Connect to ssh-agent and try each identity in turn. */ - agent =3D libssh2_agent_init(s->session); - if (!agent) { - ret =3D -EINVAL; - session_error_setg(errp, s, "failed to initialize ssh-agent suppor= t"); - goto out; - } - if (libssh2_agent_connect(agent)) { - ret =3D -ECONNREFUSED; - session_error_setg(errp, s, "failed to connect to ssh-agent"); - goto out; - } - if (libssh2_agent_list_identities(agent)) { - ret =3D -EINVAL; - session_error_setg(errp, s, - "failed requesting identities from ssh-agent"); - goto out; - } + method =3D ssh_userauth_list(s->session, NULL); =20 - for(;;) { - r =3D libssh2_agent_get_identity(agent, &identity, prev_identity); - if (r =3D=3D 1) { /* end of list */ - break; - } - if (r < 0) { + /* Try to authenticate with publickey, using the ssh-agent + * if available. + */ + if (method & SSH_AUTH_METHOD_PUBLICKEY) { + r =3D ssh_userauth_publickey_auto(s->session, NULL, NULL); + if (r =3D=3D SSH_AUTH_ERROR) { ret =3D -EINVAL; - session_error_setg(errp, s, - "failed to obtain identity from ssh-agent"); + error_setg(errp, "failed to authenticate using publickey " + "authentication"); goto out; - } - r =3D libssh2_agent_userauth(agent, user, identity); - if (r =3D=3D 0) { + } else if (r =3D=3D SSH_AUTH_SUCCESS) { /* Authenticated! */ ret =3D 0; goto out; } - /* Failed to authenticate with this identity, try the next one. */ - prev_identity =3D identity; } =20 ret =3D -EPERM; @@ -517,13 +455,6 @@ static int authenticate(BDRVSSHState *s, const char *u= ser, Error **errp) "and the identities held by your ssh-agent"); =20 out: - if (agent !=3D NULL) { - /* Note: libssh2 implementation implicitly calls - * libssh2_agent_disconnect if necessary. - */ - libssh2_agent_free(agent); - } - return ret; } =20 @@ -628,6 +559,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *optio= ns, Error *local_err =3D NULL; const char *user, *path, *host_key_check; long port =3D 0; + unsigned long portU =3D 0; =20 opts =3D qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); @@ -685,19 +617,64 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *opt= ions, } =20 /* Create SSH session. */ - s->session =3D libssh2_session_init(); + s->session =3D ssh_new(); if (!s->session) { ret =3D -EINVAL; - session_error_setg(errp, s, "failed to initialize libssh2 session"= ); + session_error_setg(errp, s, "failed to initialize libssh session"); goto err; } =20 -#if TRACE_LIBSSH2 !=3D 0 - libssh2_trace(s->session, TRACE_LIBSSH2); -#endif + /* Make sure we are in blocking mode during the connection and + * authentication phases. + */ + ssh_set_blocking(s->session, 1); =20 - r =3D libssh2_session_handshake(s->session, s->sock); - if (r !=3D 0) { + r =3D ssh_options_set(s->session, SSH_OPTIONS_USER, user); + if (r < 0) { + ret =3D -EINVAL; + session_error_setg(errp, s, + "failed to set the user in the libssh session"); + goto err; + } + + r =3D ssh_options_set(s->session, SSH_OPTIONS_HOST, s->inet->host); + if (r < 0) { + ret =3D -EINVAL; + session_error_setg(errp, s, + "failed to set the host in the libssh session"); + goto err; + } + + if (port > 0) { + portU =3D port; + r =3D ssh_options_set(s->session, SSH_OPTIONS_PORT, &portU); + if (r < 0) { + ret =3D -EINVAL; + session_error_setg(errp, s, + "failed to set the port in the libssh sessi= on"); + goto err; + } + } + + /* Read ~/.ssh/config. */ + r =3D ssh_options_parse_config(s->session, NULL); + if (r < 0) { + ret =3D -EINVAL; + session_error_setg(errp, s, "failed to parse ~/.ssh/config"); + goto err; + } + + r =3D ssh_options_set(s->session, SSH_OPTIONS_FD, &s->sock); + if (r < 0) { + ret =3D -EINVAL; + session_error_setg(errp, s, + "failed to set the socket in the libssh session= "); + goto err; + } + + /* Connect. */ + r =3D ssh_connect(s->session); + if (r < 0) { ret =3D -EINVAL; session_error_setg(errp, s, "failed to establish SSH session"); goto err; @@ -717,8 +694,15 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *opti= ons, } =20 /* Start SFTP. */ - s->sftp =3D libssh2_sftp_init(s->session); + s->sftp =3D sftp_new(s->session); if (!s->sftp) { + session_error_setg(errp, s, "failed to create sftp handle"); + ret =3D -EINVAL; + goto err; + } + + r =3D sftp_init(s->sftp); + if (r < 0) { session_error_setg(errp, s, "failed to initialize sftp handle"); ret =3D -EINVAL; goto err; @@ -727,17 +711,20 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *opt= ions, /* Open the remote file. */ DPRINTF("opening file %s flags=3D0x%x creat_mode=3D0%o", path, ssh_flags, creat_mode); - s->sftp_handle =3D libssh2_sftp_open(s->sftp, path, ssh_flags, creat_m= ode); + s->sftp_handle =3D sftp_open(s->sftp, path, ssh_flags, creat_mode); if (!s->sftp_handle) { session_error_setg(errp, s, "failed to open remote file '%s'", pat= h); ret =3D -EINVAL; goto err; } =20 + /* Make sure the SFTP file is handled in blocking mode. */ + sftp_file_set_blocking(s->sftp_handle); + qemu_opts_del(opts); =20 - r =3D libssh2_sftp_fstat(s->sftp_handle, &s->attrs); - if (r < 0) { + s->attrs =3D sftp_fstat(s->sftp_handle); + if (!s->attrs) { sftp_error_setg(errp, s, "failed to read file attributes"); return -EINVAL; } @@ -745,19 +732,21 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *opt= ions, return 0; =20 err: + if (s->attrs) { + sftp_attributes_free(s->attrs); + } + s->attrs =3D NULL; if (s->sftp_handle) { - libssh2_sftp_close(s->sftp_handle); + sftp_close(s->sftp_handle); } s->sftp_handle =3D NULL; if (s->sftp) { - libssh2_sftp_shutdown(s->sftp); + sftp_free(s->sftp); } s->sftp =3D NULL; if (s->session) { - libssh2_session_disconnect(s->session, - "from qemu ssh client: " - "error opening connection"); - libssh2_session_free(s->session); + ssh_disconnect(s->session); + ssh_free(s->session); } s->session =3D NULL; =20 @@ -775,9 +764,11 @@ static int ssh_file_open(BlockDriverState *bs, QDict *= options, int bdrv_flags, =20 ssh_state_init(s); =20 - ssh_flags =3D LIBSSH2_FXF_READ; + ssh_flags =3D 0; if (bdrv_flags & BDRV_O_RDWR) { - ssh_flags |=3D LIBSSH2_FXF_WRITE; + ssh_flags |=3D O_RDWR; + } else { + ssh_flags |=3D O_RDONLY; } =20 /* Start up SSH. */ @@ -787,7 +778,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *o= ptions, int bdrv_flags, } =20 /* Go non-blocking. */ - libssh2_session_set_blocking(s->session, 0); + ssh_set_blocking(s->session, 0); =20 return 0; =20 @@ -837,8 +828,7 @@ static int ssh_create(const char *filename, QemuOpts *o= pts, Error **errp) } =20 r =3D connect_to_ssh(&s, uri_options, - LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE| - LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, + O_RDWR | O_CREAT | O_TRUNC, 0644, errp); if (r < 0) { ret =3D r; @@ -846,14 +836,14 @@ static int ssh_create(const char *filename, QemuOpts = *opts, Error **errp) } =20 if (total_size > 0) { - libssh2_sftp_seek64(s.sftp_handle, total_size-1); - r2 =3D libssh2_sftp_write(s.sftp_handle, c, 1); + sftp_seek64(s.sftp_handle, total_size - 1); + r2 =3D sftp_write(s.sftp_handle, c, 1); if (r2 < 0) { sftp_error_setg(errp, &s, "truncate failed"); ret =3D -EINVAL; goto out; } - s.attrs.filesize =3D total_size; + s.attrs->size =3D total_size; } =20 ret =3D 0; @@ -879,10 +869,8 @@ static int ssh_has_zero_init(BlockDriverState *bs) /* Assume false, unless we can positively prove it's true. */ int has_zero_init =3D 0; =20 - if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { - if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) { - has_zero_init =3D 1; - } + if (s->attrs->type =3D=3D SSH_FILEXFER_TYPE_REGULAR) { + has_zero_init =3D 1; } =20 return has_zero_init; @@ -919,12 +907,12 @@ static coroutine_fn void co_yield(BDRVSSHState *s, Bl= ockDriverState *bs) .co =3D qemu_coroutine_self() }; =20 - r =3D libssh2_session_block_directions(s->session); + r =3D ssh_get_poll_flags(s->session); =20 - if (r & LIBSSH2_SESSION_BLOCK_INBOUND) { + if (r & SSH_READ_PENDING) { rd_handler =3D restart_coroutine; } - if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) { + if (r & SSH_WRITE_PENDING) { wr_handler =3D restart_coroutine; } =20 @@ -937,33 +925,6 @@ static coroutine_fn void co_yield(BDRVSSHState *s, Blo= ckDriverState *bs) DPRINTF("s->sock=3D%d - back", s->sock); } =20 -/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position - * in the remote file. Notice that it just updates a field in the - * sftp_handle structure, so there is no network traffic and it cannot - * fail. - * - * However, `libssh2_sftp_seek64' does have a catastrophic effect on - * performance since it causes the handle to throw away all in-flight - * reads and buffered readahead data. Therefore this function tries - * to be intelligent about when to call the underlying libssh2 function. - */ -#define SSH_SEEK_WRITE 0 -#define SSH_SEEK_READ 1 -#define SSH_SEEK_FORCE 2 - -static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) -{ - bool op_read =3D (flags & SSH_SEEK_READ) !=3D 0; - bool force =3D (flags & SSH_SEEK_FORCE) !=3D 0; - - if (force || op_read !=3D s->offset_op_read || offset !=3D s->offset) { - DPRINTF("seeking to offset=3D%" PRIi64, offset); - libssh2_sftp_seek64(s->sftp_handle, offset); - s->offset =3D offset; - s->offset_op_read =3D op_read; - } -} - static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, int64_t offset, size_t size, QEMUIOVector *qiov) @@ -975,7 +936,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, Block= DriverState *bs, =20 DPRINTF("offset=3D%" PRIi64 " size=3D%zu", offset, size); =20 - ssh_seek(s, offset, SSH_SEEK_READ); + sftp_seek64(s->sftp_handle, offset); =20 /* This keeps track of the current iovec element ('i'), where we * will write to next ('buf'), and the end of the current iovec @@ -985,35 +946,34 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, Blo= ckDriverState *bs, buf =3D i->iov_base; end_of_vec =3D i->iov_base + i->iov_len; =20 - /* libssh2 has a hard-coded limit of 2000 bytes per request, - * although it will also do readahead behind our backs. Therefore - * we may have to do repeated reads here until we have read 'size' - * bytes. - */ for (got =3D 0; got < size; ) { again: - DPRINTF("sftp_read buf=3D%p size=3D%zu", buf, end_of_vec - buf); - r =3D libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_read buf=3D%p size=3D%zu (actual size=3D%zu)", + buf, end_of_vec - buf, MIN(end_of_vec - buf, 16384)); + /* The size of SFTP packets is limited to 32K bytes, so limit + * the amount of data requested to 16K, as libssh currently + * does not handle multiple requests on its own: + * https://red.libssh.org/issues/58 + */ + r =3D sftp_read(s->sftp_handle, buf, MIN(end_of_vec - buf, 16384)); DPRINTF("sftp_read returned %zd", r); =20 - if (r =3D=3D LIBSSH2_ERROR_EAGAIN || r =3D=3D LIBSSH2_ERROR_TIMEOU= T) { + if (r =3D=3D SSH_AGAIN) { co_yield(s, bs); goto again; } - if (r < 0) { - sftp_error_report(s, "read failed"); - s->offset =3D -1; - return -EIO; - } - if (r =3D=3D 0) { + if (r =3D=3D SSH_EOF) { /* EOF: Short read so pad the buffer with zeroes and return it= . */ qemu_iovec_memset(qiov, got, 0, size - got); return 0; } + if (r < 0) { + sftp_error_report(s, "read failed"); + return -EIO; + } =20 got +=3D r; buf +=3D r; - s->offset +=3D r; if (buf >=3D end_of_vec && got < size) { i++; buf =3D i->iov_base; @@ -1050,7 +1010,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverStat= e *bs, =20 DPRINTF("offset=3D%" PRIi64 " size=3D%zu", offset, size); =20 - ssh_seek(s, offset, SSH_SEEK_WRITE); + sftp_seek64(s->sftp_handle, offset); =20 /* This keeps track of the current iovec element ('i'), where we * will read from next ('buf'), and the end of the current iovec @@ -1062,45 +1022,35 @@ static int ssh_write(BDRVSSHState *s, BlockDriverSt= ate *bs, =20 for (written =3D 0; written < size; ) { again: - DPRINTF("sftp_write buf=3D%p size=3D%zu", buf, end_of_vec - buf); - r =3D libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_write buf=3D%p size=3D%zu (actual size=3D%zu)", + buf, end_of_vec - buf, MIN(end_of_vec - buf, 131072)); + /* Avoid too large data packets, as libssh currently does not + * handle multiple requests on its own: + * https://red.libssh.org/issues/58 + */ + r =3D sftp_write(s->sftp_handle, buf, MIN(end_of_vec - buf, 131072= )); DPRINTF("sftp_write returned %zd", r); =20 - if (r =3D=3D LIBSSH2_ERROR_EAGAIN || r =3D=3D LIBSSH2_ERROR_TIMEOU= T) { + if (r =3D=3D SSH_AGAIN) { co_yield(s, bs); goto again; } if (r < 0) { sftp_error_report(s, "write failed"); - s->offset =3D -1; return -EIO; } - /* The libssh2 API is very unclear about this. A comment in - * the code says "nothing was acked, and no EAGAIN was - * received!" which apparently means that no data got sent - * out, and the underlying channel didn't return any EAGAIN - * indication. I think this is a bug in either libssh2 or - * OpenSSH (server-side). In any case, forcing a seek (to - * discard libssh2 internal buffers), and then trying again - * works for me. - */ - if (r =3D=3D 0) { - ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE); - co_yield(s, bs); - goto again; - } =20 written +=3D r; buf +=3D r; - s->offset +=3D r; if (buf >=3D end_of_vec && written < size) { i++; buf =3D i->iov_base; end_of_vec =3D i->iov_base + i->iov_len; } =20 - if (offset + written > s->attrs.filesize) - s->attrs.filesize =3D offset + written; + if (offset + written > s->attrs->size) { + s->attrs->size =3D offset + written; + } } =20 return 0; @@ -1133,24 +1083,24 @@ static void unsafe_flush_warning(BDRVSSHState *s, c= onst char *what) } } =20 -#ifdef HAS_LIBSSH2_SFTP_FSYNC +#ifdef HAS_LIBSSH_SFTP_FSYNC =20 static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) { int r; =20 DPRINTF("fsync"); + + if (!sftp_extension_supported(s->sftp, "fsync@openssh.com", "1")) { + unsafe_flush_warning(s, "OpenSSH >=3D 6.3"); + return 0; + } again: - r =3D libssh2_sftp_fsync(s->sftp_handle); - if (r =3D=3D LIBSSH2_ERROR_EAGAIN || r =3D=3D LIBSSH2_ERROR_TIMEOUT) { + r =3D sftp_fsync(s->sftp_handle); + if (r =3D=3D SSH_AGAIN) { co_yield(s, bs); goto again; } - if (r =3D=3D LIBSSH2_ERROR_SFTP_PROTOCOL && - libssh2_sftp_last_error(s->sftp) =3D=3D LIBSSH2_FX_OP_UNSUPPORTED)= { - unsafe_flush_warning(s, "OpenSSH >=3D 6.3"); - return 0; - } if (r < 0) { sftp_error_report(s, "fsync failed"); return -EIO; @@ -1171,25 +1121,25 @@ static coroutine_fn int ssh_co_flush(BlockDriverSta= te *bs) return ret; } =20 -#else /* !HAS_LIBSSH2_SFTP_FSYNC */ +#else /* !HAS_LIBSSH_SFTP_FSYNC */ =20 static coroutine_fn int ssh_co_flush(BlockDriverState *bs) { BDRVSSHState *s =3D bs->opaque; =20 - unsafe_flush_warning(s, "libssh2 >=3D 1.4.4"); + unsafe_flush_warning(s, "libssh >=3D 0.8.0"); return 0; } =20 -#endif /* !HAS_LIBSSH2_SFTP_FSYNC */ +#endif /* !HAS_LIBSSH_SFTP_FSYNC */ =20 static int64_t ssh_getlength(BlockDriverState *bs) { BDRVSSHState *s =3D bs->opaque; int64_t length; =20 - /* Note we cannot make a libssh2 call here. */ - length =3D (int64_t) s->attrs.filesize; + /* Note we cannot make a libssh call here. */ + length =3D (int64_t) s->attrs->size; DPRINTF("length=3D%" PRIi64, length); =20 return length; @@ -1215,12 +1165,16 @@ static void bdrv_ssh_init(void) { int r; =20 - r =3D libssh2_init(0); + r =3D ssh_init(); if (r !=3D 0) { - fprintf(stderr, "libssh2 initialization failed, %d\n", r); + fprintf(stderr, "libssh initialization failed, %d\n", r); exit(EXIT_FAILURE); } =20 +#if TRACE_LIBSSH !=3D 0 + ssh_set_log_level(TRACE_LIBSSH); +#endif + bdrv_register(&bdrv_ssh); } =20 diff --git a/configure b/configure index 0e856bbc04..9b004ea122 100755 --- a/configure +++ b/configure @@ -415,7 +415,7 @@ gcrypt_kdf=3D"no" vte=3D"" virglrenderer=3D"" tpm=3D"yes" -libssh2=3D"" +libssh=3D"" live_block_migration=3D"yes" numa=3D"" tcmalloc=3D"no" @@ -1263,9 +1263,9 @@ for opt do ;; --enable-tpm) tpm=3D"yes" ;; - --disable-libssh2) libssh2=3D"no" + --disable-libssh) libssh=3D"no" ;; - --enable-libssh2) libssh2=3D"yes" + --enable-libssh) libssh=3D"yes" ;; --disable-live-block-migration) live_block_migration=3D"no" ;; @@ -1546,7 +1546,7 @@ disabled with --disable-FEATURE, default is enabled i= f available: coroutine-pool coroutine freelist (better performance) glusterfs GlusterFS backend tpm TPM support - libssh2 ssh block device support + libssh ssh block device support numa libnuma support tcmalloc tcmalloc support jemalloc jemalloc support @@ -3490,43 +3490,42 @@ EOF fi =20 ########################################## -# libssh2 probe -min_libssh2_version=3D1.2.8 -if test "$libssh2" !=3D "no" ; then - if $pkg_config --atleast-version=3D$min_libssh2_version libssh2; then - libssh2_cflags=3D$($pkg_config libssh2 --cflags) - libssh2_libs=3D$($pkg_config libssh2 --libs) - libssh2=3Dyes +# libssh probe +if test "$libssh" !=3D "no" ; then + if $pkg_config --exists libssh; then + libssh_cflags=3D$($pkg_config libssh --cflags) + libssh_libs=3D$($pkg_config libssh --libs) + libssh=3Dyes else - if test "$libssh2" =3D "yes" ; then - error_exit "libssh2 >=3D $min_libssh2_version required for --enable-= libssh2" + if test "$libssh" =3D "yes" ; then + error_exit "libssh required for --enable-libssh" fi - libssh2=3Dno + libssh=3Dno fi fi =20 ########################################## -# libssh2_sftp_fsync probe +# libssh sftp_fsync probe =20 -if test "$libssh2" =3D "yes"; then +if test "$libssh" =3D "yes"; then cat > $TMPC < -#include -#include +#include +#include int main(void) { - LIBSSH2_SESSION *session; - LIBSSH2_SFTP *sftp; - LIBSSH2_SFTP_HANDLE *sftp_handle; - session =3D libssh2_session_init (); - sftp =3D libssh2_sftp_init (session); - sftp_handle =3D libssh2_sftp_open (sftp, "/", 0, 0); - libssh2_sftp_fsync (sftp_handle); + ssh_session session; + sftp_session sftp; + sftp_file sftp_handle; + session =3D ssh_new(); + sftp =3D sftp_new(session); + sftp_handle =3D sftp_open(sftp, "/", 0, 0); + sftp_fsync(sftp_handle); return 0; } EOF - # libssh2_cflags/libssh2_libs defined in previous test. - if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then - QEMU_CFLAGS=3D"-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS" + # libssh_cflags/libssh_libs defined in previous test. + if compile_prog "$libssh_cflags" "$libssh_libs" ; then + QEMU_CFLAGS=3D"-DHAS_LIBSSH_SFTP_FSYNC $QEMU_CFLAGS" fi fi =20 @@ -5540,7 +5539,7 @@ echo "GlusterFS support $glusterfs" echo "gcov $gcov_tool" echo "gcov enabled $gcov" echo "TPM support $tpm" -echo "libssh2 support $libssh2" +echo "libssh support $libssh" echo "TPM passthrough $tpm_passthrough" echo "TPM emulator $tpm_emulator" echo "QOM debugging $qom_cast_debug" @@ -6140,10 +6139,10 @@ if test "$glusterfs_zerofill" =3D "yes" ; then echo "CONFIG_GLUSTERFS_ZEROFILL=3Dy" >> $config_host_mak fi =20 -if test "$libssh2" =3D "yes" ; then - echo "CONFIG_LIBSSH2=3Dm" >> $config_host_mak - echo "LIBSSH2_CFLAGS=3D$libssh2_cflags" >> $config_host_mak - echo "LIBSSH2_LIBS=3D$libssh2_libs" >> $config_host_mak +if test "$libssh" =3D "yes" ; then + echo "CONFIG_LIBSSH=3Dm" >> $config_host_mak + echo "LIBSSH_CFLAGS=3D$libssh_cflags" >> $config_host_mak + echo "LIBSSH_LIBS=3D$libssh_libs" >> $config_host_mak fi =20 if test "$live_block_migration" =3D "yes" ; then --=20 2.13.6