mirror of https://github.com/xemu-project/xemu.git
ssh: QAPIfy host-key-check option
This makes the host-key-check option available in blockdev-add. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
16e4bdb1bf
commit
ec2f54182c
92
block/ssh.c
92
block/ssh.c
|
@ -431,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
static int check_host_key(BDRVSSHState *s, const char *host, int port,
|
||||||
const char *host_key_check, Error **errp)
|
SshHostKeyCheck *hkc, Error **errp)
|
||||||
{
|
{
|
||||||
/* host_key_check=no */
|
SshHostKeyCheckMode mode;
|
||||||
if (strcmp(host_key_check, "no") == 0) {
|
|
||||||
|
if (hkc) {
|
||||||
|
mode = hkc->mode;
|
||||||
|
} else {
|
||||||
|
mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SSH_HOST_KEY_CHECK_MODE_NONE:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
case SSH_HOST_KEY_CHECK_MODE_HASH:
|
||||||
|
if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
|
||||||
/* host_key_check=md5:xx:yy:zz:... */
|
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||||
if (strncmp(host_key_check, "md5:", 4) == 0) {
|
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
||||||
return check_host_key_hash(s, &host_key_check[4],
|
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
|
||||||
LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
|
return check_host_key_hash(s, hkc->u.hash.hash,
|
||||||
}
|
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
||||||
|
}
|
||||||
/* host_key_check=sha1:xx:yy:zz:... */
|
g_assert_not_reached();
|
||||||
if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
break;
|
||||||
return check_host_key_hash(s, &host_key_check[5],
|
case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
|
||||||
LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* host_key_check=yes */
|
|
||||||
if (strcmp(host_key_check, "yes") == 0) {
|
|
||||||
return check_host_key_knownhosts(s, host, port, errp);
|
return check_host_key_knownhosts(s, host, port, errp);
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,16 +548,22 @@ static QemuOptsList ssh_runtime_opts = {
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
.help = "Port to connect to",
|
.help = "Port to connect to",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "host_key_check",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Defines how and what to check the host key against",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool ssh_process_legacy_socket_options(QDict *output_opts,
|
static bool ssh_process_legacy_options(QDict *output_opts,
|
||||||
QemuOpts *legacy_opts,
|
QemuOpts *legacy_opts,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
const char *host = qemu_opt_get(legacy_opts, "host");
|
const char *host = qemu_opt_get(legacy_opts, "host");
|
||||||
const char *port = qemu_opt_get(legacy_opts, "port");
|
const char *port = qemu_opt_get(legacy_opts, "port");
|
||||||
|
const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check");
|
||||||
|
|
||||||
if (!host && port) {
|
if (!host && port) {
|
||||||
error_setg(errp, "port may not be used without host");
|
error_setg(errp, "port may not be used without host");
|
||||||
|
@ -565,6 +575,28 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts,
|
||||||
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
|
qdict_put_str(output_opts, "server.port", port ?: stringify(22));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host_key_check) {
|
||||||
|
if (strcmp(host_key_check, "no") == 0) {
|
||||||
|
qdict_put_str(output_opts, "host-key-check.mode", "none");
|
||||||
|
} else if (strncmp(host_key_check, "md5:", 4) == 0) {
|
||||||
|
qdict_put_str(output_opts, "host-key-check.mode", "hash");
|
||||||
|
qdict_put_str(output_opts, "host-key-check.type", "md5");
|
||||||
|
qdict_put_str(output_opts, "host-key-check.hash",
|
||||||
|
&host_key_check[4]);
|
||||||
|
} else if (strncmp(host_key_check, "sha1:", 5) == 0) {
|
||||||
|
qdict_put_str(output_opts, "host-key-check.mode", "hash");
|
||||||
|
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 (strcmp(host_key_check, "yes") == 0) {
|
||||||
|
qdict_put_str(output_opts, "host-key-check.mode", "known_hosts");
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "unknown host_key_check setting (%s)",
|
||||||
|
host_key_check);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +617,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ssh_process_legacy_socket_options(options, opts, errp)) {
|
if (!ssh_process_legacy_options(options, opts, errp)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -629,16 +661,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||||
{
|
{
|
||||||
BlockdevOptionsSsh *opts;
|
BlockdevOptionsSsh *opts;
|
||||||
int r, ret;
|
int r, ret;
|
||||||
const char *user, *host_key_check;
|
const char *user;
|
||||||
long port = 0;
|
long port = 0;
|
||||||
|
|
||||||
host_key_check = qdict_get_try_str(options, "host_key_check");
|
|
||||||
if (!host_key_check) {
|
|
||||||
host_key_check = "yes";
|
|
||||||
} else {
|
|
||||||
qdict_del(options, "host_key_check");
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = ssh_parse_options(options, errp);
|
opts = ssh_parse_options(options, errp);
|
||||||
if (opts == NULL) {
|
if (opts == NULL) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -692,8 +717,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the remote host's key against known_hosts. */
|
/* Check the remote host's key against known_hosts. */
|
||||||
ret = check_host_key(s, s->inet->host, port, host_key_check,
|
ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
|
||||||
errp);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2552,6 +2552,63 @@
|
||||||
'*cache-clean-interval': 'int',
|
'*cache-clean-interval': 'int',
|
||||||
'*encrypt': 'BlockdevQcow2Encryption' } }
|
'*encrypt': 'BlockdevQcow2Encryption' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @SshHostKeyCheckMode:
|
||||||
|
#
|
||||||
|
# @none Don't check the host key at all
|
||||||
|
# @hash Compare the host key with a given hash
|
||||||
|
# @known_hosts Check the host key against the known_hosts file
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'enum': 'SshHostKeyCheckMode',
|
||||||
|
'data': [ 'none', 'hash', 'known_hosts' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @SshHostKeyCheckHashType:
|
||||||
|
#
|
||||||
|
# @md5 The given hash is an md5 hash
|
||||||
|
# @sha1 The given hash is an sha1 hash
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'enum': 'SshHostKeyCheckHashType',
|
||||||
|
'data': [ 'md5', 'sha1' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @SshHostKeyHash:
|
||||||
|
#
|
||||||
|
# @type The hash algorithm used for the hash
|
||||||
|
# @hash The expected hash value
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'struct': 'SshHostKeyHash',
|
||||||
|
'data': { 'type': 'SshHostKeyCheckHashType',
|
||||||
|
'hash': 'str' }}
|
||||||
|
|
||||||
|
##
|
||||||
|
# @SshHostKeyDummy:
|
||||||
|
#
|
||||||
|
# For those union branches that don't need additional fields.
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'struct': 'SshHostKeyDummy',
|
||||||
|
'data': {} }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @SshHostKeyCheck:
|
||||||
|
#
|
||||||
|
# Since: 2.12
|
||||||
|
##
|
||||||
|
{ 'union': 'SshHostKeyCheck',
|
||||||
|
'base': { 'mode': 'SshHostKeyCheckMode' },
|
||||||
|
'discriminator': 'mode',
|
||||||
|
'data': { 'none': 'SshHostKeyDummy',
|
||||||
|
'hash': 'SshHostKeyHash',
|
||||||
|
'known_hosts': 'SshHostKeyDummy' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptionsSsh:
|
# @BlockdevOptionsSsh:
|
||||||
#
|
#
|
||||||
|
@ -2562,14 +2619,16 @@
|
||||||
# @user: user as which to connect, defaults to current
|
# @user: user as which to connect, defaults to current
|
||||||
# local user name
|
# local user name
|
||||||
#
|
#
|
||||||
# TODO: Expose the host_key_check option in QMP
|
# @host-key-check: Defines how and what to check the host key against
|
||||||
|
# (default: known_hosts)
|
||||||
#
|
#
|
||||||
# Since: 2.9
|
# Since: 2.9
|
||||||
##
|
##
|
||||||
{ 'struct': 'BlockdevOptionsSsh',
|
{ 'struct': 'BlockdevOptionsSsh',
|
||||||
'data': { 'server': 'InetSocketAddress',
|
'data': { 'server': 'InetSocketAddress',
|
||||||
'path': 'str',
|
'path': 'str',
|
||||||
'*user': 'str' } }
|
'*user': 'str',
|
||||||
|
'*host-key-check': 'SshHostKeyCheck' } }
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
Loading…
Reference in New Issue