ssh: switch from libssh2 to libssh

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

Use APIs/features available in libssh 0.8 conditionally, to support
older versions (which are not recommended though).

Adjust the iotest 207 according to the different error message, and to
find the default key type for localhost (to properly compare the
fingerprint with).
Contributed-by: Max Reitz <mreitz@redhat.com>

Adjust the various Docker/Travis scripts to use libssh when available
instead of libssh2. The mingw/mxe testing is dropped for now, as there
are no packages for it.

Signed-off-by: Pino Toscano <ptoscano@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20190620200840.17655-1-ptoscano@redhat.com
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-id: 5873173.t2JhDm7DL7@lindworm.usersys.redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Pino Toscano 2019-06-20 22:08:40 +02:00 committed by Max Reitz
parent 98eb9733f4
commit b10d49d761
13 changed files with 454 additions and 365 deletions

View File

@ -31,7 +31,7 @@ addons:
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh2-1-dev - libssh-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvte-2.91-dev - libvte-2.91-dev
@ -270,7 +270,7 @@ matrix:
- libseccomp-dev - libseccomp-dev
- libspice-protocol-dev - libspice-protocol-dev
- libspice-server-dev - libspice-server-dev
- libssh2-1-dev - libssh-dev
- liburcu-dev - liburcu-dev
- libusb-1.0-0-dev - libusb-1.0-0-dev
- libvte-2.91-dev - libvte-2.91-dev

View File

@ -31,7 +31,7 @@ block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o
block-obj-$(CONFIG_VXHS) += vxhs.o block-obj-$(CONFIG_VXHS) += vxhs.o
block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-$(CONFIG_LIBSSH) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o block-obj-y += write-threshold.o
block-obj-y += backup.o block-obj-y += backup.o
@ -52,8 +52,8 @@ rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags := $(GLUSTERFS_CFLAGS) gluster.o-cflags := $(GLUSTERFS_CFLAGS)
gluster.o-libs := $(GLUSTERFS_LIBS) gluster.o-libs := $(GLUSTERFS_LIBS)
vxhs.o-libs := $(VXHS_LIBS) vxhs.o-libs := $(VXHS_LIBS)
ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-cflags := $(LIBSSH_CFLAGS)
ssh.o-libs := $(LIBSSH2_LIBS) ssh.o-libs := $(LIBSSH_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
dmg-bz2.o-libs := $(BZIP2_LIBS) dmg-bz2.o-libs := $(BZIP2_LIBS)

File diff suppressed because it is too large Load Diff

View File

@ -171,19 +171,21 @@ nbd_client_connect_success(const char *export_name) "export '%s'"
# ssh.c # ssh.c
ssh_restart_coroutine(void *co) "co=%p" ssh_restart_coroutine(void *co) "co=%p"
ssh_flush(void) "fsync" ssh_flush(void) "fsync"
ssh_check_host_key_knownhosts(const char *key) "host key OK: %s" ssh_check_host_key_knownhosts(void) "host key OK"
ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o" ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o"
ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p" ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p"
ssh_co_yield_back(int sock) "s->sock=%d - back" ssh_co_yield_back(int sock) "s->sock=%d - back"
ssh_getlength(int64_t length) "length=%" PRIi64 ssh_getlength(int64_t length) "length=%" PRIi64
ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64 ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64
ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu" ssh_read_buf(void *buf, size_t size, size_t actual_size) "sftp_read buf=%p size=%zu (actual size=%zu)"
ssh_read_return(ssize_t ret) "sftp_read returned %zd" ssh_read_return(ssize_t ret, int sftp_err) "sftp_read returned %zd (sftp error=%d)"
ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu" ssh_write_buf(void *buf, size_t size, size_t actual_size) "sftp_write buf=%p size=%zu (actual size=%zu)"
ssh_write_return(ssize_t ret) "sftp_write returned %zd" ssh_write_return(ssize_t ret, int sftp_err) "sftp_write returned %zd (sftp error=%d)"
ssh_seek(int64_t offset) "seeking to offset=%" PRIi64 ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
ssh_auth_methods(int methods) "auth methods=0x%x"
ssh_server_status(int status) "server status=%d"
# curl.c # curl.c
curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld"
@ -216,4 +218,4 @@ sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32 sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32
# ssh.c # ssh.c
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)" 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)"

65
configure vendored
View File

@ -472,7 +472,7 @@ auth_pam=""
vte="" vte=""
virglrenderer="" virglrenderer=""
tpm="" tpm=""
libssh2="" libssh=""
live_block_migration="yes" live_block_migration="yes"
numa="" numa=""
tcmalloc="no" tcmalloc="no"
@ -1439,9 +1439,9 @@ for opt do
;; ;;
--enable-tpm) tpm="yes" --enable-tpm) tpm="yes"
;; ;;
--disable-libssh2) libssh2="no" --disable-libssh) libssh="no"
;; ;;
--enable-libssh2) libssh2="yes" --enable-libssh) libssh="yes"
;; ;;
--disable-live-block-migration) live_block_migration="no" --disable-live-block-migration) live_block_migration="no"
;; ;;
@ -1810,7 +1810,7 @@ disabled with --disable-FEATURE, default is enabled if available:
coroutine-pool coroutine freelist (better performance) coroutine-pool coroutine freelist (better performance)
glusterfs GlusterFS backend glusterfs GlusterFS backend
tpm TPM support tpm TPM support
libssh2 ssh block device support libssh ssh block device support
numa libnuma support numa libnuma support
libxml2 for Parallels image format libxml2 for Parallels image format
tcmalloc tcmalloc support tcmalloc tcmalloc support
@ -3914,43 +3914,34 @@ EOF
fi fi
########################################## ##########################################
# libssh2 probe # libssh probe
min_libssh2_version=1.2.8 if test "$libssh" != "no" ; then
if test "$libssh2" != "no" ; then if $pkg_config --exists libssh; then
if $pkg_config --atleast-version=$min_libssh2_version libssh2; then libssh_cflags=$($pkg_config libssh --cflags)
libssh2_cflags=$($pkg_config libssh2 --cflags) libssh_libs=$($pkg_config libssh --libs)
libssh2_libs=$($pkg_config libssh2 --libs) libssh=yes
libssh2=yes
else else
if test "$libssh2" = "yes" ; then if test "$libssh" = "yes" ; then
error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2" error_exit "libssh required for --enable-libssh"
fi fi
libssh2=no libssh=no
fi fi
fi fi
########################################## ##########################################
# libssh2_sftp_fsync probe # Check for libssh 0.8
# This is done like this instead of using the LIBSSH_VERSION_* and
# SSH_VERSION_* macros because some distributions in the past shipped
# snapshots of the future 0.8 from Git, and those snapshots did not
# have updated version numbers (still referring to 0.7.0).
if test "$libssh2" = "yes"; then if test "$libssh" = "yes"; then
cat > $TMPC <<EOF cat > $TMPC <<EOF
#include <stdio.h> #include <libssh/libssh.h>
#include <libssh2.h> int main(void) { return ssh_get_server_publickey(NULL, NULL); }
#include <libssh2_sftp.h>
int main(void) {
LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftp;
LIBSSH2_SFTP_HANDLE *sftp_handle;
session = libssh2_session_init ();
sftp = libssh2_sftp_init (session);
sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
libssh2_sftp_fsync (sftp_handle);
return 0;
}
EOF EOF
# libssh2_cflags/libssh2_libs defined in previous test. if compile_prog "$libssh_cflags" "$libssh_libs"; then
if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then libssh_cflags="-DHAVE_LIBSSH_0_8 $libssh_cflags"
QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
fi fi
fi fi
@ -6451,7 +6442,7 @@ echo "GlusterFS support $glusterfs"
echo "gcov $gcov_tool" echo "gcov $gcov_tool"
echo "gcov enabled $gcov" echo "gcov enabled $gcov"
echo "TPM support $tpm" echo "TPM support $tpm"
echo "libssh2 support $libssh2" echo "libssh support $libssh"
echo "QOM debugging $qom_cast_debug" echo "QOM debugging $qom_cast_debug"
echo "Live block migration $live_block_migration" echo "Live block migration $live_block_migration"
echo "lzo support $lzo" echo "lzo support $lzo"
@ -7144,10 +7135,10 @@ if test "$glusterfs_iocb_has_stat" = "yes" ; then
echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
fi fi
if test "$libssh2" = "yes" ; then if test "$libssh" = "yes" ; then
echo "CONFIG_LIBSSH2=m" >> $config_host_mak echo "CONFIG_LIBSSH=m" >> $config_host_mak
echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak
echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak
fi fi
if test "$live_block_migration" = "yes" ; then if test "$live_block_migration" = "yes" ; then

View File

@ -782,7 +782,7 @@ print a warning when @code{fsync} is not supported:
warning: ssh server @code{ssh.example.com:22} does not support fsync warning: ssh server @code{ssh.example.com:22} does not support fsync
With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is With sufficiently new versions of libssh and OpenSSH, @code{fsync} is
supported. supported.
@node disk_images_nvme @node disk_images_nvme

View File

@ -15,7 +15,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
mxe-$TARGET-w64-mingw32.shared-curl \ mxe-$TARGET-w64-mingw32.shared-curl \
mxe-$TARGET-w64-mingw32.shared-glib \ mxe-$TARGET-w64-mingw32.shared-glib \
mxe-$TARGET-w64-mingw32.shared-libgcrypt \ mxe-$TARGET-w64-mingw32.shared-libgcrypt \
mxe-$TARGET-w64-mingw32.shared-libssh2 \
mxe-$TARGET-w64-mingw32.shared-libusb1 \ mxe-$TARGET-w64-mingw32.shared-libusb1 \
mxe-$TARGET-w64-mingw32.shared-lzo \ mxe-$TARGET-w64-mingw32.shared-lzo \
mxe-$TARGET-w64-mingw32.shared-nettle \ mxe-$TARGET-w64-mingw32.shared-nettle \

View File

@ -15,7 +15,6 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
mxe-$TARGET-w64-mingw32.shared-curl \ mxe-$TARGET-w64-mingw32.shared-curl \
mxe-$TARGET-w64-mingw32.shared-glib \ mxe-$TARGET-w64-mingw32.shared-glib \
mxe-$TARGET-w64-mingw32.shared-libgcrypt \ mxe-$TARGET-w64-mingw32.shared-libgcrypt \
mxe-$TARGET-w64-mingw32.shared-libssh2 \
mxe-$TARGET-w64-mingw32.shared-libusb1 \ mxe-$TARGET-w64-mingw32.shared-libusb1 \
mxe-$TARGET-w64-mingw32.shared-lzo \ mxe-$TARGET-w64-mingw32.shared-lzo \
mxe-$TARGET-w64-mingw32.shared-nettle \ mxe-$TARGET-w64-mingw32.shared-nettle \

View File

@ -35,7 +35,7 @@ ENV PACKAGES \
libpng-devel \ libpng-devel \
librbd-devel \ librbd-devel \
libseccomp-devel \ libseccomp-devel \
libssh2-devel \ libssh-devel \
libubsan \ libubsan \
libusbx-devel \ libusbx-devel \
libxml2-devel \ libxml2-devel \
@ -50,7 +50,6 @@ ENV PACKAGES \
mingw32-gtk3 \ mingw32-gtk3 \
mingw32-libjpeg-turbo \ mingw32-libjpeg-turbo \
mingw32-libpng \ mingw32-libpng \
mingw32-libssh2 \
mingw32-libtasn1 \ mingw32-libtasn1 \
mingw32-nettle \ mingw32-nettle \
mingw32-pixman \ mingw32-pixman \
@ -64,7 +63,6 @@ ENV PACKAGES \
mingw64-gtk3 \ mingw64-gtk3 \
mingw64-libjpeg-turbo \ mingw64-libjpeg-turbo \
mingw64-libpng \ mingw64-libpng \
mingw64-libssh2 \
mingw64-libtasn1 \ mingw64-libtasn1 \
mingw64-nettle \ mingw64-nettle \
mingw64-pixman \ mingw64-pixman \

View File

@ -53,7 +53,7 @@ ENV PACKAGES flex bison \
libsnappy-dev \ libsnappy-dev \
libspice-protocol-dev \ libspice-protocol-dev \
libspice-server-dev \ libspice-server-dev \
libssh2-1-dev \ libssh-dev \
libusb-1.0-0-dev \ libusb-1.0-0-dev \
libusbredirhost-dev \ libusbredirhost-dev \
libvdeplug-dev \ libvdeplug-dev \

View File

@ -40,7 +40,7 @@ ENV PACKAGES flex bison \
libsnappy-dev \ libsnappy-dev \
libspice-protocol-dev \ libspice-protocol-dev \
libspice-server-dev \ libspice-server-dev \
libssh2-1-dev \ libssh-dev \
libusb-1.0-0-dev \ libusb-1.0-0-dev \
libusbredirhost-dev \ libusbredirhost-dev \
libvdeplug-dev \ libvdeplug-dev \

View File

@ -110,12 +110,49 @@ with iotests.FilePath('t.img') as disk_path, \
iotests.img_info_log(remote_path) iotests.img_info_log(remote_path)
md5_key = subprocess.check_output( keys = subprocess.check_output(
'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', 'cut -d" " -f3',
shell=True).rstrip().decode('ascii') shell=True).rstrip().decode('ascii').split('\n')
# Mappings of base64 representations to digests
md5_keys = {}
sha1_keys = {}
for key in keys:
md5_keys[key] = subprocess.check_output(
'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
shell=True).rstrip().decode('ascii')
sha1_keys[key] = subprocess.check_output(
'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
shell=True).rstrip().decode('ascii')
vm.launch() vm.launch()
# Find correct key first
matching_key = None
for key in keys:
result = vm.qmp('blockdev-add',
driver='ssh', node_name='node0', path=disk_path,
server={
'host': '127.0.0.1',
'port': '22',
}, host_key_check={
'mode': 'hash',
'type': 'md5',
'hash': md5_keys[key],
})
if 'error' not in result:
vm.qmp('blockdev-del', node_name='node0')
matching_key = key
break
if matching_key is None:
vm.shutdown()
iotests.notrun('Did not find a key that fits 127.0.0.1')
blockdev_create(vm, { 'driver': 'ssh', blockdev_create(vm, { 'driver': 'ssh',
'location': { 'location': {
'path': disk_path, 'path': disk_path,
@ -140,7 +177,7 @@ with iotests.FilePath('t.img') as disk_path, \
'host-key-check': { 'host-key-check': {
'mode': 'hash', 'mode': 'hash',
'type': 'md5', 'type': 'md5',
'hash': md5_key, 'hash': md5_keys[matching_key],
} }
}, },
'size': 8388608 }) 'size': 8388608 })
@ -148,11 +185,6 @@ with iotests.FilePath('t.img') as disk_path, \
iotests.img_info_log(remote_path) iotests.img_info_log(remote_path)
sha1_key = subprocess.check_output(
'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1',
shell=True).rstrip().decode('ascii')
vm.launch() vm.launch()
blockdev_create(vm, { 'driver': 'ssh', blockdev_create(vm, { 'driver': 'ssh',
'location': { 'location': {
@ -178,7 +210,7 @@ with iotests.FilePath('t.img') as disk_path, \
'host-key-check': { 'host-key-check': {
'mode': 'hash', 'mode': 'hash',
'type': 'sha1', 'type': 'sha1',
'hash': sha1_key, 'hash': sha1_keys[matching_key],
} }
}, },
'size': 4194304 }) 'size': 4194304 })

View File

@ -68,7 +68,7 @@ virtual size: 4 MiB (4194304 bytes)
{"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}}} {"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}}}
{"return": {}} {"return": {}}
Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) 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)
{"execute": "job-dismiss", "arguments": {"id": "job0"}} {"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}} {"return": {}}