[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 2/2] ssh: implement private key authentication
From: |
Pino Toscano |
Subject: |
[Qemu-devel] [PATCH 2/2] ssh: implement private key authentication |
Date: |
Fri, 26 Jul 2019 16:09:54 +0200 |
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 <address@hidden>
---
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, SshHostKeyCheck
*hkc, Error **errp)
return -EINVAL;
}
+static int authenticate_privkey(BDRVSSHState *s, BlockdevOptionsSsh *opts,
+ Error **errp)
+{
+ int err;
+ int ret;
+ char *pubkey_file = NULL;
+ ssh_key public_key = NULL;
+ ssh_key private_key = NULL;
+ char *passphrase;
+
+ pubkey_file = 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 = qcrypto_secret_lookup_as_utf8(opts->private_key_secret, errp);
+ if (!passphrase) {
+ err = SSH_AUTH_ERROR;
+ goto error;
+ }
+ ret = ssh_pki_import_privkey_file(opts->private_key, passphrase,
+ NULL, NULL, &private_key);
+ g_free(passphrase);
+ if (ret == SSH_EOF) {
+ error_setg(errp, "Cannot read private key '%s'", opts->private_key);
+ err = SSH_AUTH_ERROR;
+ goto error;
+ } else if (ret == SSH_ERROR) {
+ error_setg(errp,
+ "Cannot open private key '%s', maybe the passphrase is "
+ "wrong",
+ opts->private_key);
+ err = SSH_AUTH_ERROR;
+ goto error;
+ }
+
+ /* try to open the public part of the private key */
+ ret = ssh_pki_import_pubkey_file(pubkey_file, &public_key);
+ if (ret == SSH_ERROR) {
+ error_setg(errp, "Cannot read public key '%s'", pubkey_file);
+ err = SSH_AUTH_ERROR;
+ goto error;
+ } else if (ret == SSH_EOF) {
+ /* create the public key from the private key */
+ ret = ssh_pki_export_privkey_to_pubkey(private_key, &public_key);
+ if (ret == SSH_ERROR) {
+ error_setg(errp,
+ "Cannot export the public key from the private key "
+ "'%s'",
+ opts->private_key);
+ err = SSH_AUTH_ERROR;
+ goto error;
+ }
+ }
+
+ ret = ssh_userauth_try_publickey(s->session, NULL, public_key);
+ if (ret != SSH_AUTH_SUCCESS) {
+ err = SSH_AUTH_DENIED;
+ goto error;
+ }
+
+ ret = ssh_userauth_publickey(s->session, NULL, private_key);
+ if (ret != SSH_AUTH_SUCCESS) {
+ err = 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,
BlockdevOptionsSsh *opts,
ret = 0;
goto out;
}
+
+ /*
+ * Try to authenticate with private key, if available.
+ */
+ if (opts->has_private_key && opts->has_private_key_secret) {
+ r = authenticate_privkey(s, opts, errp);
+ if (r == SSH_AUTH_ERROR) {
+ ret = -EINVAL;
+ goto out;
+ } else if (r == SSH_AUTH_SUCCESS) {
+ /* Authenticated! */
+ ret = 0;
+ goto out;
+ }
+ }
}
/*
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=%" PRIi64
ssh_auth_methods(int methods) "auth methods=0x%x"
ssh_server_status(int status) "server status=%d"
ssh_option_secret_object(const char *path) "using password from object %s"
+ssh_auth_key_passphrase(const char *path, const char *key) "using passphrase
from object %s for private key %s"
# 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.
-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.
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 passphrase
+# 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' } }
##
--
2.21.0
- [Qemu-devel] [PATCH 0/2] ssh: add password and privkey auth methods, Pino Toscano, 2019/07/26
- [Qemu-devel] [PATCH 1/2] ssh: implement password authentication, Pino Toscano, 2019/07/26
- [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication,
Pino Toscano <=
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Eric Blake, 2019/07/26
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Richard W.M. Jones, 2019/07/26
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Pino Toscano, 2019/07/29
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Markus Armbruster, 2019/07/29
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Pino Toscano, 2019/07/29
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Markus Armbruster, 2019/07/29
- Re: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication, Kevin Wolf, 2019/07/29
Re: [Qemu-devel] [PATCH 0/2] ssh: add password and privkey auth methods, Richard W.M. Jones, 2019/07/26