#include <libssh/libssh.h>
#include <libssh/sftp.h>
+#include "block/block-io.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h"
}
}
-static void GCC_FMT_ATTR(3, 4)
+static void G_GNUC_PRINTF(3, 4)
session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
{
va_list args;
g_free(msg);
}
-static void GCC_FMT_ATTR(3, 4)
+static void G_GNUC_PRINTF(3, 4)
sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
{
va_list args;
return 0;
err:
- if (uri) {
- uri_free(uri);
- }
+ uri_free(uri);
return -EINVAL;
}
static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
{
int ret;
-#ifdef HAVE_LIBSSH_0_8
enum ssh_known_hosts_e state;
int r;
ssh_key pubkey;
error_setg(errp, "error while checking for known server (%d)", state);
goto out;
}
-#else /* !HAVE_LIBSSH_0_8 */
- int state;
-
- state = ssh_is_server_known(s->session);
- trace_ssh_server_status(state);
-
- switch (state) {
- case SSH_SERVER_KNOWN_OK:
- /* OK */
- trace_ssh_check_host_key_knownhosts();
- break;
- case SSH_SERVER_KNOWN_CHANGED:
- ret = -EINVAL;
- error_setg(errp,
- "host key does not match the one in known_hosts; this "
- "may be a possible attack");
- goto out;
- case SSH_SERVER_FOUND_OTHER:
- ret = -EINVAL;
- error_setg(errp,
- "host key for this server not found, another type exists");
- goto out;
- case SSH_SERVER_FILE_NOT_FOUND:
- ret = -ENOENT;
- error_setg(errp, "known_hosts file not found");
- goto out;
- case SSH_SERVER_NOT_KNOWN:
- ret = -EINVAL;
- error_setg(errp, "no host key was found in known_hosts");
- goto out;
- case SSH_SERVER_ERROR:
- ret = -EINVAL;
- error_setg(errp, "server error");
- goto out;
- default:
- ret = -EINVAL;
- error_setg(errp, "error while checking for known server (%d)", state);
- goto out;
- }
-#endif /* !HAVE_LIBSSH_0_8 */
/* known_hosts checking successful. */
ret = 0;
return *host_key_check - '\0';
}
+static char *format_fingerprint(const unsigned char *fingerprint, size_t len)
+{
+ static const char *hex = "0123456789abcdef";
+ char *ret = g_new0(char, (len * 2) + 1);
+ for (size_t i = 0; i < len; i++) {
+ ret[i * 2] = hex[((fingerprint[i] >> 4) & 0xf)];
+ ret[(i * 2) + 1] = hex[(fingerprint[i] & 0xf)];
+ }
+ ret[len * 2] = '\0';
+ return ret;
+}
+
static int
check_host_key_hash(BDRVSSHState *s, const char *hash,
- enum ssh_publickey_hash_type type, Error **errp)
+ enum ssh_publickey_hash_type type, const char *typestr,
+ Error **errp)
{
int r;
ssh_key pubkey;
unsigned char *server_hash;
size_t server_hash_len;
+ const char *keytype;
-#ifdef HAVE_LIBSSH_0_8
r = ssh_get_server_publickey(s->session, &pubkey);
-#else
- r = ssh_get_publickey(s->session, &pubkey);
-#endif
if (r != SSH_OK) {
session_error_setg(errp, s, "failed to read remote host key");
return -EINVAL;
}
+ keytype = ssh_key_type_to_char(ssh_key_type(pubkey));
+
r = ssh_get_publickey_hash(pubkey, type, &server_hash, &server_hash_len);
ssh_key_free(pubkey);
if (r != 0) {
}
r = compare_fingerprint(server_hash, server_hash_len, hash);
- ssh_clean_pubkey_hash(&server_hash);
if (r != 0) {
- error_setg(errp, "remote host key does not match host_key_check '%s'",
- hash);
+ g_autofree char *server_fp = format_fingerprint(server_hash,
+ server_hash_len);
+ error_setg(errp, "remote host %s key fingerprint '%s:%s' "
+ "does not match host_key_check '%s:%s'",
+ keytype, typestr, server_fp, typestr, hash);
+ ssh_clean_pubkey_hash(&server_hash);
return -EPERM;
}
+ ssh_clean_pubkey_hash(&server_hash);
return 0;
}
case SSH_HOST_KEY_CHECK_MODE_HASH:
if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
return check_host_key_hash(s, hkc->u.hash.hash,
- SSH_PUBLICKEY_HASH_MD5, errp);
+ SSH_PUBLICKEY_HASH_MD5, "md5",
+ errp);
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
return check_host_key_hash(s, hkc->u.hash.hash,
- SSH_PUBLICKEY_HASH_SHA1, errp);
+ SSH_PUBLICKEY_HASH_SHA1, "sha1",
+ errp);
+ } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256) {
+ return check_host_key_hash(s, hkc->u.hash.hash,
+ SSH_PUBLICKEY_HASH_SHA256, "sha256",
+ errp);
}
g_assert_not_reached();
break;
qdict_put_str(output_opts, "host-key-check.type", "sha1");
qdict_put_str(output_opts, "host-key-check.hash",
&host_key_check[5]);
+ } else if (strncmp(host_key_check, "sha256:", 7) == 0) {
+ qdict_put_str(output_opts, "host-key-check.mode", "hash");
+ qdict_put_str(output_opts, "host-key-check.type", "sha256");
+ qdict_put_str(output_opts, "host-key-check.hash",
+ &host_key_check[7]);
} else if (strcmp(host_key_check, "yes") == 0) {
qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
} else {
{
BlockdevOptionsSsh *result = NULL;
QemuOpts *opts = NULL;
- Error *local_err = NULL;
const QDictEntry *e;
Visitor *v;
/* Translate legacy options */
opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
- qemu_opts_absorb_qdict(opts, options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_opts_absorb_qdict(opts, options, errp)) {
goto fail;
}
goto fail;
}
- visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
+ visit_type_BlockdevOptionsSsh(v, NULL, &result, errp);
visit_free(v);
-
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!result) {
goto fail;
}
unsigned int port = 0;
int new_sock = -1;
- if (opts->has_user) {
+ if (opts->user) {
s->user = g_strdup(opts->user);
} else {
s->user = g_strdup(g_get_user_name());
/* Go non-blocking. */
ssh_set_blocking(s->session, 0);
+ if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) {
+ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE;
+ }
+
qapi_free_BlockdevOptionsSsh(opts);
return 0;
return ret;
}
-static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
+static int coroutine_fn ssh_co_create_opts(BlockDriver *drv,
+ const char *filename,
+ QemuOpts *opts,
Error **errp)
{
BlockdevCreateOptions *create_options;
AioContext *ctx = bdrv_get_aio_context(bs);
trace_ssh_restart_coroutine(restart->co);
- aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL);
+ aio_set_fd_handler(ctx, s->sock, NULL, NULL, NULL, NULL, NULL);
aio_co_wake(restart->co);
}
trace_ssh_co_yield(s->sock, rd_handler, wr_handler);
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
- false, rd_handler, wr_handler, NULL, &restart);
+ rd_handler, wr_handler, NULL, NULL, &restart);
qemu_coroutine_yield();
trace_ssh_co_yield_back(s->sock);
}
return ret;
}
-static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
- int64_t offset, size_t size,
- QEMUIOVector *qiov)
+static coroutine_fn int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
+ int64_t offset, size_t size,
+ QEMUIOVector *qiov)
{
ssize_t r;
size_t written;
BDRVSSHState *s = bs->opaque;
int ret;
- assert(!flags);
qemu_co_mutex_lock(&s->lock);
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE, qiov);
}
}
-#ifdef HAVE_LIBSSH_0_8
-
static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
{
int r;
return ret;
}
-#else /* !HAVE_LIBSSH_0_8 */
-
-static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
-{
- BDRVSSHState *s = bs->opaque;
-
- unsafe_flush_warning(s, "libssh >= 0.8.0");
- return 0;
-}
-
-#endif /* !HAVE_LIBSSH_0_8 */
-
-static int64_t ssh_getlength(BlockDriverState *bs)
+static int64_t coroutine_fn ssh_co_getlength(BlockDriverState *bs)
{
BDRVSSHState *s = bs->opaque;
int64_t length;
}
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
- PreallocMode prealloc, Error **errp)
+ bool exact, PreallocMode prealloc,
+ BdrvRequestFlags flags, Error **errp)
{
BDRVSSHState *s = bs->opaque;
.bdrv_co_create_opts = ssh_co_create_opts,
.bdrv_close = ssh_close,
.bdrv_has_zero_init = ssh_has_zero_init,
- .bdrv_has_zero_init_truncate = ssh_has_zero_init,
.bdrv_co_readv = ssh_co_readv,
.bdrv_co_writev = ssh_co_writev,
- .bdrv_getlength = ssh_getlength,
+ .bdrv_co_getlength = ssh_co_getlength,
.bdrv_co_truncate = ssh_co_truncate,
.bdrv_co_flush_to_disk = ssh_co_flush,
.bdrv_refresh_filename = ssh_refresh_filename,