qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Pino Toscano <ptoscano@redhat.com>
To: qemu-devel@nongnu.org, qemu-block@nongnu.org
Cc: kwolf@redhat.com, ptoscano@redhat.com, pkrempa@redhat.com,
	rjones@redhat.com, mreitz@redhat.com
Subject: [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication
Date: Fri, 26 Jul 2019 16:09:54 +0200	[thread overview]
Message-ID: <20190726140954.31921-3-ptoscano@redhat.com> (raw)
In-Reply-To: <20190726140954.31921-1-ptoscano@redhat.com>

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 <ptoscano@redhat.com>
---
 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



  parent reply	other threads:[~2019-07-26 14:10 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-26 14:09 [Qemu-devel] [PATCH 0/2] ssh: add password and privkey auth methods Pino Toscano
2019-07-26 14:09 ` [Qemu-devel] [PATCH 1/2] ssh: implement password authentication Pino Toscano
2019-07-26 14:09 ` Pino Toscano [this message]
2019-07-26 14:24   ` [Qemu-devel] [PATCH 2/2] ssh: implement private key authentication Eric Blake
2019-07-26 14:29     ` Richard W.M. Jones
2019-07-29  8:00     ` Pino Toscano
2019-07-29 10:57       ` Markus Armbruster
2019-07-29 11:21         ` Pino Toscano
2019-07-29 15:10           ` Markus Armbruster
2019-07-29 11:08     ` Kevin Wolf
2019-08-12 21:08       ` Max Reitz
2019-08-12 21:22       ` Eric Blake
2019-07-26 14:27 ` [Qemu-devel] [PATCH 0/2] ssh: add password and privkey auth methods Richard W.M. Jones
2019-07-26 14:45   ` Pino Toscano
2019-07-26 14:50     ` Richard W.M. Jones
2019-07-26 15:06     ` Eric Blake
2019-07-26 15:35       ` Richard W.M. Jones
2019-07-26 15:43         ` Daniel P. Berrangé

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190726140954.31921-3-ptoscano@redhat.com \
    --to=ptoscano@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=pkrempa@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=rjones@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).