Commit ec2f5418 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

ssh: QAPIfy host-key-check option



This makes the host-key-check option available in blockdev-add.

Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
parent 16e4bdb1
Loading
Loading
Loading
Loading
+56 −32
Original line number Diff line number Diff line
@@ -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,
                          const char *host_key_check, Error **errp)
                          SshHostKeyCheck *hkc, Error **errp)
{
    /* host_key_check=no */
    if (strcmp(host_key_check, "no") == 0) {
        return 0;
    }
    SshHostKeyCheckMode mode;

    /* host_key_check=md5:xx:yy:zz:... */
    if (strncmp(host_key_check, "md5:", 4) == 0) {
        return check_host_key_hash(s, &host_key_check[4],
                                   LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
    if (hkc) {
        mode = hkc->mode;
    } else {
        mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS;
    }

    /* host_key_check=sha1:xx:yy:zz:... */
    if (strncmp(host_key_check, "sha1:", 5) == 0) {
        return check_host_key_hash(s, &host_key_check[5],
    switch (mode) {
    case SSH_HOST_KEY_CHECK_MODE_NONE:
        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,
                                       LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
        } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
            return check_host_key_hash(s, hkc->u.hash.hash,
                                       LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
        }

    /* host_key_check=yes */
    if (strcmp(host_key_check, "yes") == 0) {
        g_assert_not_reached();
        break;
    case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
        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;
}

@@ -544,16 +548,22 @@ static QemuOptsList ssh_runtime_opts = {
            .type = QEMU_OPT_NUMBER,
            .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 */ }
    },
};

static bool ssh_process_legacy_socket_options(QDict *output_opts,
static bool ssh_process_legacy_options(QDict *output_opts,
                                       QemuOpts *legacy_opts,
                                       Error **errp)
{
    const char *host = qemu_opt_get(legacy_opts, "host");
    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) {
        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));
    }

    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;
}

@@ -585,7 +617,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
        goto fail;
    }

    if (!ssh_process_legacy_socket_options(options, opts, errp)) {
    if (!ssh_process_legacy_options(options, opts, errp)) {
        goto fail;
    }

@@ -629,16 +661,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
{
    BlockdevOptionsSsh *opts;
    int r, ret;
    const char *user, *host_key_check;
    const char *user;
    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);
    if (opts == NULL) {
        return -EINVAL;
@@ -692,8 +717,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
    }

    /* Check the remote host's key against known_hosts. */
    ret = check_host_key(s, s->inet->host, port, host_key_check,
                         errp);
    ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
    if (ret < 0) {
        goto err;
    }
+61 −2
Original line number Diff line number Diff line
@@ -2552,6 +2552,63 @@
            '*cache-clean-interval': 'int',
            '*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:
#
@@ -2562,14 +2619,16 @@
# @user:                user as which to connect, defaults to current
#                       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
##
{ 'struct': 'BlockdevOptionsSsh',
  'data': { 'server': 'InetSocketAddress',
            'path': 'str',
            '*user': 'str' } }
            '*user': 'str',
            '*host-key-check': 'SshHostKeyCheck' } }


##