From nobody Sat Feb 7 07:31:14 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.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 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1564150239; cv=none; d=zoho.com; s=zohoarc; b=abk6dkFjMNb90cW6LJpwKXaLFE/tFMb9ghM8pWO8LWQJNuCYQ7vY5FzPIB211rhJQlzXp5jKijw76IvajCQxFtLP6YabWKyYQX7n1jWL7T4JK3D82x01qiYmWzGellZUrPP/xNeRDZY2TmaadjIOVgpddrsW1FlK/rMaKb3mSco= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1564150239; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=+27yXPOQ12VOVNe9PskLLIm0E1/VECnK52ZuQ2OAft4=; b=kAqZcYhfQ4KbQwj5xDClWhn4ewy1/8gC2WqJPbbtz3eVZ7SoHJOOkIfx+7slqw6ofA4gj5YqmsCrb5HhEvYyZVD3r4s1iDkJ7jVIDm7i0lSlNci2uu40D/hbi05iD6CJnUSRVT/inNb1sOfc/oLQh28KjldEfuZhJTVS49xGFpE= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1564150239833752.5027303333821; Fri, 26 Jul 2019 07:10:39 -0700 (PDT) Received: from localhost ([::1]:40398 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hr0vc-0005O5-4t for importer@patchew.org; Fri, 26 Jul 2019 10:10:36 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:37237) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hr0v8-0003rN-Ty for qemu-devel@nongnu.org; Fri, 26 Jul 2019 10:10:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hr0v7-0007iQ-J8 for qemu-devel@nongnu.org; Fri, 26 Jul 2019 10:10:06 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46350) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hr0v4-0007df-BD; Fri, 26 Jul 2019 10:10:02 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8093E30B259D; Fri, 26 Jul 2019 14:10:01 +0000 (UTC) Received: from lindworm.usersys.redhat.com (unknown [10.43.2.5]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 074756012D; Fri, 26 Jul 2019 14:09:59 +0000 (UTC) From: Pino Toscano To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 26 Jul 2019 16:09:53 +0200 Message-Id: <20190726140954.31921-2-ptoscano@redhat.com> In-Reply-To: <20190726140954.31921-1-ptoscano@redhat.com> References: <20190726140954.31921-1-ptoscano@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Fri, 26 Jul 2019 14:10:01 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 1/2] ssh: implement password authentication X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, ptoscano@redhat.com, pkrempa@redhat.com, rjones@redhat.com, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a 'password-secret' option which represents the name of an object with the password of the user. Signed-off-by: Pino Toscano Tested-by: Richard W.M. Jones --- block/ssh.c | 35 ++++++++++++++++++++++++++++++++--- block/trace-events | 1 + docs/qemu-block-drivers.texi | 7 +++++-- qapi/block-core.json | 6 +++++- tests/qemu-iotests/207.out | 2 +- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/block/ssh.c b/block/ssh.c index 501933b855..04ae223282 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -43,6 +43,7 @@ #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" +#include "crypto/secret.h" #include "trace.h" =20 /* @@ -499,7 +500,8 @@ static int check_host_key(BDRVSSHState *s, SshHostKeyCh= eck *hkc, Error **errp) return -EINVAL; } =20 -static int authenticate(BDRVSSHState *s, Error **errp) +static int authenticate(BDRVSSHState *s, BlockdevOptionsSsh *opts, + Error **errp) { int r, ret; int method; @@ -538,9 +540,35 @@ static int authenticate(BDRVSSHState *s, Error **errp) } } =20 + /* + * Try to authenticate with password, if available. + */ + if (method & SSH_AUTH_METHOD_PASSWORD && opts->has_password_secret) { + char *password; + + trace_ssh_option_secret_object(opts->password_secret); + password =3D qcrypto_secret_lookup_as_utf8(opts->password_secret, = errp); + if (!password) { + ret =3D -EINVAL; + goto out; + } + r =3D ssh_userauth_password(s->session, NULL, password); + g_free(password); + if (r =3D=3D SSH_AUTH_ERROR) { + ret =3D -EINVAL; + session_error_setg(errp, s, "failed to authenticate using " + "password authentication"); + goto out; + } else if (r =3D=3D SSH_AUTH_SUCCESS) { + /* Authenticated! */ + ret =3D 0; + goto out; + } + } + ret =3D -EPERM; error_setg(errp, "failed to authenticate using publickey authenticatio= n " - "and the identities held by your ssh-agent"); + "and the identities held by your ssh-agent, or using passwo= rd"); =20 out: return ret; @@ -785,7 +813,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOpti= onsSsh *opts, } =20 /* Authenticate. */ - ret =3D authenticate(s, errp); + ret =3D authenticate(s, opts, errp); if (ret < 0) { goto err; } @@ -1376,6 +1404,7 @@ static const char *const ssh_strong_runtime_opts[] = =3D { "user", "host_key_check", "server.", + "password-secret", =20 NULL }; diff --git a/block/trace-events b/block/trace-events index d724df0117..391aae03e6 100644 --- a/block/trace-events +++ b/block/trace-events @@ -186,6 +186,7 @@ ssh_write_return(ssize_t ret, int sftp_err) "sftp_write= returned %zd (sftp error ssh_seek(int64_t offset) "seeking to offset=3D%" PRIi64 ssh_auth_methods(int methods) "auth methods=3D0x%x" ssh_server_status(int status) "server status=3D%d" +ssh_option_secret_object(const char *path) "using password from object %s" =20 # curl.c curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index 91ab0eceae..c77ef2dd69 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -771,8 +771,11 @@ matches a specific fingerprint: (@code{sha1:} can also be used as a prefix, but note that OpenSSH tools only use MD5 to print fingerprints). =20 -Currently authentication must be done using ssh-agent. Other -authentication methods may be supported in future. +The optional @var{password-secret} parameter provides the ID of a +@code{secret} object that contains the password for authenticating. + +Currently authentication must be done using ssh-agent, or providing a +password. Other authentication methods may be supported in future. =20 Note: Many ssh servers do not support an @code{fsync}-style operation. The ssh driver cannot guarantee that disk flush requests are diff --git a/qapi/block-core.json b/qapi/block-core.json index 0d43d4f37c..1244562c7b 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3223,13 +3223,17 @@ # @host-key-check: Defines how and what to check the host key against # (default: known_hosts) # +# @password-secret: ID of a QCryptoSecret object providing a password +# for authentication (since 4.2) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsSsh', 'data': { 'server': 'InetSocketAddress', 'path': 'str', '*user': 'str', - '*host-key-check': 'SshHostKeyCheck' } } + '*host-key-check': 'SshHostKeyCheck', + '*password-secret': 'str' } } =20 =20 ## diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index 1239d9d648..5bfdf626b9 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -74,7 +74,7 @@ Job failed: failed to open remote file '/this/is/not/an/e= xisting/path': SFTP ser =20 {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": = {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": = "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user"= : "invalid user"}, "size": 4194304}}} {"return": {}} -Job failed: failed to authenticate using publickey authentication and the = identities held by your ssh-agent +Job failed: failed to authenticate using publickey authentication and the = identities held by your ssh-agent, or using password {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} =20 --=20 2.21.0 From nobody Sat Feb 7 07:31:14 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.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 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1564150245; cv=none; d=zoho.com; s=zohoarc; b=I7TzMr7sNmnFNkme8WTUo+nQ0cM7g+bKyBHpMyMWd0Pgs5O2GsGt2EJsSmArwCD8st/GIKwWuOrzq0J2bc7/wnQbdNvEh8uWihTzaW1cKekrh/8ozkVKMwhXA6men+YBO5DrO4GBqmsUJKNN7TYcrTSyrrTK8MBk/dV2/UHTi0k= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1564150245; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=YkRl9dlRGXe4CRo12tL/qnHQn1VJ/GtK0SCyrolYQEk=; b=BE1Boyv789n1GvudeJIdWg08NjLxkVKCYaco27Qrja3/sLVpB6AmW8zDeWmWOspVI2oQHhjzictsEoKiW+4S+iXYNdRt9ohF7p/DCnjlUNxIwdYSlWE3sBaw+lRF1+qd58TnXvj+mnKrzPZlWSmMUYAeI845c4nNSvIZzvA3Oxk= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 156415024591729.041270011414554; Fri, 26 Jul 2019 07:10:45 -0700 (PDT) Received: from localhost ([::1]:40400 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hr0vf-0005bu-Tk for importer@patchew.org; Fri, 26 Jul 2019 10:10:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:37307) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hr0vA-0003zV-Kb for qemu-devel@nongnu.org; Fri, 26 Jul 2019 10:10:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hr0v9-0007kr-7V for qemu-devel@nongnu.org; Fri, 26 Jul 2019 10:10:08 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60290) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hr0v6-0007gS-0w; Fri, 26 Jul 2019 10:10:04 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4952C30BC590; Fri, 26 Jul 2019 14:10:03 +0000 (UTC) Received: from lindworm.usersys.redhat.com (unknown [10.43.2.5]) by smtp.corp.redhat.com (Postfix) with ESMTPS id BEA785C6E0; Fri, 26 Jul 2019 14:10:01 +0000 (UTC) From: Pino Toscano To: qemu-devel@nongnu.org, qemu-block@nongnu.org Date: Fri, 26 Jul 2019 16:09:54 +0200 Message-Id: <20190726140954.31921-3-ptoscano@redhat.com> In-Reply-To: <20190726140954.31921-1-ptoscano@redhat.com> References: <20190726140954.31921-1-ptoscano@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Fri, 26 Jul 2019 14:10:03 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, ptoscano@redhat.com, pkrempa@redhat.com, rjones@redhat.com, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a 'private-key' option which represents the path of a private key to use for authentication, and 'private-key-secret' as the name of an object with its passphrase. Signed-off-by: Pino Toscano Tested-by: Richard W.M. Jones --- block/ssh.c | 98 ++++++++++++++++++++++++++++++++++++ block/trace-events | 1 + docs/qemu-block-drivers.texi | 12 ++++- qapi/block-core.json | 9 +++- 4 files changed, 117 insertions(+), 3 deletions(-) diff --git a/block/ssh.c b/block/ssh.c index 04ae223282..1b7c1f4108 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -500,6 +500,89 @@ static int check_host_key(BDRVSSHState *s, SshHostKeyC= heck *hkc, Error **errp) return -EINVAL; } =20 +static int authenticate_privkey(BDRVSSHState *s, BlockdevOptionsSsh *opts, + Error **errp) +{ + int err; + int ret; + char *pubkey_file =3D NULL; + ssh_key public_key =3D NULL; + ssh_key private_key =3D NULL; + char *passphrase; + + pubkey_file =3D g_strdup_printf("%s.pub", opts->private_key); + + /* load the private key */ + trace_ssh_auth_key_passphrase(opts->private_key_secret, opts->private_= key); + passphrase =3D qcrypto_secret_lookup_as_utf8(opts->private_key_secret,= errp); + if (!passphrase) { + err =3D SSH_AUTH_ERROR; + goto error; + } + ret =3D ssh_pki_import_privkey_file(opts->private_key, passphrase, + NULL, NULL, &private_key); + g_free(passphrase); + if (ret =3D=3D SSH_EOF) { + error_setg(errp, "Cannot read private key '%s'", opts->private_key= ); + err =3D SSH_AUTH_ERROR; + goto error; + } else if (ret =3D=3D SSH_ERROR) { + error_setg(errp, + "Cannot open private key '%s', maybe the passphrase is " + "wrong", + opts->private_key); + err =3D SSH_AUTH_ERROR; + goto error; + } + + /* try to open the public part of the private key */ + ret =3D ssh_pki_import_pubkey_file(pubkey_file, &public_key); + if (ret =3D=3D SSH_ERROR) { + error_setg(errp, "Cannot read public key '%s'", pubkey_file); + err =3D SSH_AUTH_ERROR; + goto error; + } else if (ret =3D=3D SSH_EOF) { + /* create the public key from the private key */ + ret =3D ssh_pki_export_privkey_to_pubkey(private_key, &public_key); + if (ret =3D=3D SSH_ERROR) { + error_setg(errp, + "Cannot export the public key from the private key " + "'%s'", + opts->private_key); + err =3D SSH_AUTH_ERROR; + goto error; + } + } + + ret =3D ssh_userauth_try_publickey(s->session, NULL, public_key); + if (ret !=3D SSH_AUTH_SUCCESS) { + err =3D SSH_AUTH_DENIED; + goto error; + } + + ret =3D ssh_userauth_publickey(s->session, NULL, private_key); + if (ret !=3D SSH_AUTH_SUCCESS) { + err =3D SSH_AUTH_DENIED; + goto error; + } + + ssh_key_free(private_key); + ssh_key_free(public_key); + g_free(pubkey_file); + + return SSH_AUTH_SUCCESS; + + error: + if (private_key) { + ssh_key_free(private_key); + } + if (public_key) { + ssh_key_free(public_key); + } + g_free(pubkey_file); + return err; +} + static int authenticate(BDRVSSHState *s, BlockdevOptionsSsh *opts, Error **errp) { @@ -538,6 +621,21 @@ static int authenticate(BDRVSSHState *s, BlockdevOptio= nsSsh *opts, ret =3D 0; goto out; } + + /* + * Try to authenticate with private key, if available. + */ + if (opts->has_private_key && opts->has_private_key_secret) { + r =3D authenticate_privkey(s, opts, errp); + if (r =3D=3D SSH_AUTH_ERROR) { + ret =3D -EINVAL; + goto out; + } else if (r =3D=3D SSH_AUTH_SUCCESS) { + /* Authenticated! */ + ret =3D 0; + goto out; + } + } } =20 /* diff --git a/block/trace-events b/block/trace-events index 391aae03e6..ccb51b9992 100644 --- a/block/trace-events +++ b/block/trace-events @@ -187,6 +187,7 @@ ssh_seek(int64_t offset) "seeking to offset=3D%" PRIi64 ssh_auth_methods(int methods) "auth methods=3D0x%x" ssh_server_status(int status) "server status=3D%d" ssh_option_secret_object(const char *path) "using password from object %s" +ssh_auth_key_passphrase(const char *path, const char *key) "using passphra= se from object %s for private key %s" =20 # curl.c curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index c77ef2dd69..5513bf261c 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -774,8 +774,16 @@ tools only use MD5 to print fingerprints). The optional @var{password-secret} parameter provides the ID of a @code{secret} object that contains the password for authenticating. =20 -Currently authentication must be done using ssh-agent, or providing a -password. Other authentication methods may be supported in future. +The optional @var{private-key} parameter provides the path to the +private key for authenticating. + +The optional @var{private-key-secret} parameter provides the ID of a +@code{secret} object that contains the passphrase of the private key +specified as @var{private-key} for authenticating. + +Currently authentication must be done using ssh-agent, providing a +private key with its passphrase, or providing a password. +Other authentication methods may be supported in future. =20 Note: Many ssh servers do not support an @code{fsync}-style operation. The ssh driver cannot guarantee that disk flush requests are diff --git a/qapi/block-core.json b/qapi/block-core.json index 1244562c7b..e873f8934d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3226,6 +3226,11 @@ # @password-secret: ID of a QCryptoSecret object providing a password # for authentication (since 4.2) # +# @private-key: path to the private key (since 4.2) +# +# @private-key-secret: ID of a QCryptoSecret object providing the passphr= ase +# for 'private-key' (since 4.2) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsSsh', @@ -3233,7 +3238,9 @@ 'path': 'str', '*user': 'str', '*host-key-check': 'SshHostKeyCheck', - '*password-secret': 'str' } } + '*password-secret': 'str', + '*private-key': 'str', + '*private-key-secret': 'str' } } =20 =20 ## --=20 2.21.0