From nobody Fri Oct 24 09:45:23 2025 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; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1519415444485611.5887902460732; Fri, 23 Feb 2018 11:50:44 -0800 (PST) Received: from localhost ([::1]:46849 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epJMg-000595-Ob for importer@patchew.org; Fri, 23 Feb 2018 14:50:42 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50573) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1epIzm-0001QM-TI for qemu-devel@nongnu.org; Fri, 23 Feb 2018 14:27:04 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1epIzl-0005Oy-9J for qemu-devel@nongnu.org; Fri, 23 Feb 2018 14:27:02 -0500 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:50868 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1epIze-0005FP-AK; Fri, 23 Feb 2018 14:26:54 -0500 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DEEFD8A3DD; Fri, 23 Feb 2018 19:26:53 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-117-118.ams2.redhat.com [10.36.117.118]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4F2FC2024CAC; Fri, 23 Feb 2018 19:26:52 +0000 (UTC) From: Kevin Wolf To: qemu-block@nongnu.org Date: Fri, 23 Feb 2018 20:25:43 +0100 Message-Id: <20180223192549.26666-31-kwolf@redhat.com> In-Reply-To: <20180223192549.26666-1-kwolf@redhat.com> References: <20180223192549.26666-1-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 23 Feb 2018 19:26:53 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 23 Feb 2018 19:26:53 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'kwolf@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v3 30/36] ssh: QAPIfy host-key-check option 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, jdurgin@redhat.com, pkrempa@redhat.com, mitake.hitoshi@lab.ntt.co.jp, jcody@redhat.com, qemu-devel@nongnu.org, mreitz@redhat.com, namei.unix@gmail.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" This makes the host-key-check option available in blockdev-add. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- qapi/block-core.json | 63 +++++++++++++++++++++++++++++++++++-- block/ssh.c | 88 +++++++++++++++++++++++++++++++++---------------= ---- 2 files changed, 117 insertions(+), 34 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index f7679fce53..431d4a4fb2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2553,6 +2553,63 @@ '*encrypt': 'BlockdevQcow2Encryption' } } =20 ## +# @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: # # @server: host address @@ -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' } } =20 =20 ## diff --git a/block/ssh.c b/block/ssh.c index 9a89b7f350..dcf766c213 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -430,31 +430,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash, } =20 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=3Dno */ - if (strcmp(host_key_check, "no") =3D=3D 0) { - return 0; - } + SshHostKeyCheckMode mode; =20 - /* 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); - } - - /* 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); + if (hkc) { + mode =3D hkc->mode; + } else { + mode =3D SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS; } =20 - /* host_key_check=3Dyes */ - if (strcmp(host_key_check, "yes") =3D=3D 0) { + switch (mode) { + case SSH_HOST_KEY_CHECK_MODE_NONE: + return 0; + case SSH_HOST_KEY_CHECK_MODE_HASH: + if (hkc->u.hash.type =3D=3D 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 =3D=3D SSH_HOST_KEY_CHECK_HASH_TYPE_SH= A1) { + return check_host_key_hash(s, hkc->u.hash.hash, + LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp= ); + } + 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(); } =20 - error_setg(errp, "unknown host_key_check setting (%s)", host_key_check= ); return -EINVAL; } =20 @@ -543,16 +547,22 @@ static QemuOptsList ssh_runtime_opts =3D { .type =3D QEMU_OPT_NUMBER, .help =3D "Port to connect to", }, + { + .name =3D "host_key_check", + .type =3D QEMU_OPT_STRING, + .help =3D "Defines how and what to check the host key against", + }, { /* end of list */ } }, }; =20 -static bool ssh_process_legacy_socket_options(QDict *output_opts, - QemuOpts *legacy_opts, - Error **errp) +static bool ssh_process_legacy_options(QDict *output_opts, + QemuOpts *legacy_opts, + Error **errp) { const char *host =3D qemu_opt_get(legacy_opts, "host"); const char *port =3D qemu_opt_get(legacy_opts, "port"); + const char *host_key_check =3D qemu_opt_get(legacy_opts, "host_key_che= ck"); =20 if (!host && port) { error_setg(errp, "port may not be used without host"); @@ -564,6 +574,28 @@ static bool ssh_process_legacy_socket_options(QDict *o= utput_opts, qdict_put_str(output_opts, "server.port", port ?: stringify(22)); } =20 + if (host_key_check) { + if (strcmp(host_key_check, "no") =3D=3D 0) { + qdict_put_str(output_opts, "host-key-check.mode", "none"); + } else if (strncmp(host_key_check, "md5:", 4) =3D=3D 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) =3D=3D 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") =3D=3D 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; } =20 @@ -584,7 +616,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *opt= ions, Error **errp) goto fail; } =20 - if (!ssh_process_legacy_socket_options(options, opts, errp)) { + if (!ssh_process_legacy_options(options, opts, errp)) { goto fail; } =20 @@ -628,16 +660,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *opti= ons, { BlockdevOptionsSsh *opts; int r, ret; - const char *user, *host_key_check; + const char *user; long port =3D 0; =20 - host_key_check =3D qdict_get_try_str(options, "host_key_check"); - if (!host_key_check) { - host_key_check =3D "yes"; - } else { - qdict_del(options, "host_key_check"); - } - opts =3D ssh_parse_options(options, errp); if (opts =3D=3D NULL) { return -EINVAL; @@ -691,8 +716,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *optio= ns, } =20 /* Check the remote host's key against known_hosts. */ - ret =3D check_host_key(s, s->inet->host, port, host_key_check, - errp); + ret =3D check_host_key(s, s->inet->host, port, opts->host_key_check, e= rrp); if (ret < 0) { goto err; } --=20 2.13.6