qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Daniel P. Berrange" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>,
	Amit Shah <amit.shah@redhat.com>,
	Juan Quintela <quintela@redhat.com>,
	"Daniel P. Berrange" <berrange@redhat.com>
Subject: [Qemu-devel] [PATCH v6 for-2.7 26/28] migration: add support for encrypting data with TLS
Date: Wed, 27 Apr 2016 11:05:16 +0100	[thread overview]
Message-ID: <1461751518-12128-27-git-send-email-berrange@redhat.com> (raw)
In-Reply-To: <1461751518-12128-1-git-send-email-berrange@redhat.com>

This extends the migration_set_incoming_channel and
migration_set_outgoing_channel methods so that they
will automatically wrap the QIOChannel in a
QIOChannelTLS instance if TLS credentials are configured
in the migration parameters.

This allows TLS to work for tcp, unix, fd and exec
migration protocols. It does not (currently) work for
RDMA since it does not use these APIs, but it is
unlikely that TLS would be desired with RDMA anyway
since it would degrade the performance to that seen
with TCP defeating the purpose of using RDMA.

On the target host, QEMU would be launched with a set
of TLS credentials for a server endpoint

 $ qemu-system-x86_64 -monitor stdio -incoming defer \
    -object tls-creds-x509,dir=/home/berrange/security/qemutls,endpoint=server,id=tls0 \
    ...other args...

To enable incoming TLS migration 2 monitor commands are
then used

  (qemu) migrate_set_str_parameter tls-creds tls0
  (qemu) migrate_incoming tcp:myhostname:9000

On the source host, QEMU is launched in a similar
manner but using client endpoint credentials

 $ qemu-system-x86_64 -monitor stdio \
    -object tls-creds-x509,dir=/home/berrange/security/qemutls,endpoint=client,id=tls0 \
    ...other args...

To enable outgoing TLS migration 2 monitor commands are
then used

  (qemu) migrate_set_str_parameter tls-creds tls0
  (qemu) migrate tcp:otherhostname:9000

Thanks to earlier improvements to error reporting,
TLS errors can be seen 'info migrate' when doing a
detached migration. For example:

  (qemu) info migrate
  capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
  Migration status: failed
  total time: 0 milliseconds
  error description: TLS handshake failed: The TLS connection was non-properly terminated.

Or

  (qemu) info migrate
  capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
  Migration status: failed
  total time: 0 milliseconds
  error description: Certificate does not match the hostname localhost

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/migration.h |  12 +++-
 migration/Makefile.objs       |   1 +
 migration/exec.c              |   2 +-
 migration/fd.c                |   2 +-
 migration/migration.c         |  40 +++++++++--
 migration/socket.c            |  34 +++++++--
 migration/tls.c               | 161 ++++++++++++++++++++++++++++++++++++++++++
 trace-events                  |  12 +++-
 8 files changed, 247 insertions(+), 17 deletions(-)
 create mode 100644 migration/tls.c

diff --git a/include/migration/migration.h b/include/migration/migration.h
index 9c5a002..332b198 100644
--- a/include/migration/migration.h
+++ b/include/migration/migration.h
@@ -188,8 +188,18 @@ void qemu_start_incoming_migration(const char *uri, Error **errp);
 void migration_set_incoming_channel(MigrationState *s,
                                     QIOChannel *ioc);
 
+void migration_tls_set_incoming_channel(MigrationState *s,
+                                        QIOChannel *ioc,
+                                        Error **errp);
+
 void migration_set_outgoing_channel(MigrationState *s,
-                                    QIOChannel *ioc);
+                                    QIOChannel *ioc,
+                                    const char *hostname);
+
+void migration_tls_set_outgoing_channel(MigrationState *s,
+                                        QIOChannel *ioc,
+                                        const char *hostname,
+                                        Error **errp);
 
 uint64_t migrate_max_downtime(void);
 
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 7b9051c..e68b54d 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,4 +1,5 @@
 common-obj-y += migration.o socket.o fd.o exec.o
+common-obj-y += tls.o
 common-obj-y += vmstate.o
 common-obj-y += qemu-file.o
 common-obj-y += qemu-file-channel.o
diff --git a/migration/exec.c b/migration/exec.c
index c825e27..1515cc3 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -38,7 +38,7 @@ void exec_start_outgoing_migration(MigrationState *s, const char *command, Error
         return;
     }
 
-    migration_set_outgoing_channel(s, ioc);
+    migration_set_outgoing_channel(s, ioc, NULL);
     object_unref(OBJECT(ioc));
 }
 
diff --git a/migration/fd.c b/migration/fd.c
index 60a75b8..fc5c9ee 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -38,7 +38,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **
         return;
     }
 
-    migration_set_outgoing_channel(s, ioc);
+    migration_set_outgoing_channel(s, ioc, NULL);
     object_unref(OBJECT(ioc));
 }
 
diff --git a/migration/migration.c b/migration/migration.c
index c946946..fde3641 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -35,6 +35,7 @@
 #include "exec/memory.h"
 #include "exec/address-spaces.h"
 #include "io/channel-buffer.h"
+#include "io/channel-tls.h"
 
 #define MAX_THROTTLE  (32 << 20)      /* Migration transfer speed throttling */
 
@@ -429,20 +430,47 @@ void process_incoming_migration(QEMUFile *f)
 void migration_set_incoming_channel(MigrationState *s,
                                     QIOChannel *ioc)
 {
-    QEMUFile *f = qemu_fopen_channel_input(ioc);
+    trace_migration_set_incoming_channel(
+        ioc, object_get_typename(OBJECT(ioc)));
 
-    process_incoming_migration(f);
+    if (s->parameters.tls_creds &&
+        !object_dynamic_cast(OBJECT(ioc),
+                             TYPE_QIO_CHANNEL_TLS)) {
+        Error *local_err = NULL;
+        migration_tls_set_incoming_channel(s, ioc, &local_err);
+        if (local_err) {
+            error_report_err(local_err);
+        }
+    } else {
+        QEMUFile *f = qemu_fopen_channel_input(ioc);
+        process_incoming_migration(f);
+    }
 }
 
 
 void migration_set_outgoing_channel(MigrationState *s,
-                                    QIOChannel *ioc)
+                                    QIOChannel *ioc,
+                                    const char *hostname)
 {
-    QEMUFile *f = qemu_fopen_channel_output(ioc);
+    trace_migration_set_outgoing_channel(
+        ioc, object_get_typename(OBJECT(ioc)), hostname);
 
-    s->to_dst_file = f;
+    if (s->parameters.tls_creds &&
+        !object_dynamic_cast(OBJECT(ioc),
+                             TYPE_QIO_CHANNEL_TLS)) {
+        Error *local_err = NULL;
+        migration_tls_set_outgoing_channel(s, ioc, hostname, &local_err);
+        if (local_err) {
+            migrate_fd_error(s, local_err);
+            error_free(local_err);
+        }
+    } else {
+        QEMUFile *f = qemu_fopen_channel_output(ioc);
 
-    migrate_fd_connect(s);
+        s->to_dst_file = f;
+
+        migrate_fd_connect(s);
+    }
 }
 
 
diff --git a/migration/socket.c b/migration/socket.c
index 25457ea..977a8d3 100644
--- a/migration/socket.c
+++ b/migration/socket.c
@@ -55,20 +55,35 @@ static SocketAddress *unix_build_address(const char *path)
 }
 
 
+struct SocketConnectData {
+    MigrationState *s;
+    char *hostname;
+};
+
+static void socket_connect_data_free(void *opaque)
+{
+    struct SocketConnectData *data = opaque;
+    if (!data) {
+        return;
+    }
+    g_free(data->hostname);
+    g_free(data);
+}
+
 static void socket_outgoing_migration(Object *src,
                                       Error *err,
                                       gpointer opaque)
 {
-    MigrationState *s = opaque;
+    struct SocketConnectData *data = opaque;
     QIOChannel *sioc = QIO_CHANNEL(src);
 
     if (err) {
         trace_migration_socket_outgoing_error(error_get_pretty(err));
-        s->to_dst_file = NULL;
-        migrate_fd_error(s, err);
+        data->s->to_dst_file = NULL;
+        migrate_fd_error(data->s, err);
     } else {
-        trace_migration_socket_outgoing_connected();
-        migration_set_outgoing_channel(s, sioc);
+        trace_migration_socket_outgoing_connected(data->hostname);
+        migration_set_outgoing_channel(data->s, sioc, data->hostname);
     }
     object_unref(src);
 }
@@ -78,11 +93,16 @@ static void socket_start_outgoing_migration(MigrationState *s,
                                             Error **errp)
 {
     QIOChannelSocket *sioc = qio_channel_socket_new();
+    struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
+    data->s = s;
+    if (saddr->type == SOCKET_ADDRESS_KIND_INET) {
+        data->hostname = g_strdup(saddr->u.inet.data->host);
+    }
     qio_channel_socket_connect_async(sioc,
                                      saddr,
                                      socket_outgoing_migration,
-                                     s,
-                                     NULL);
+                                     data,
+                                     socket_connect_data_free);
     qapi_free_SocketAddress(saddr);
 }
 
diff --git a/migration/tls.c b/migration/tls.c
new file mode 100644
index 0000000..75f959f
--- /dev/null
+++ b/migration/tls.c
@@ -0,0 +1,161 @@
+/*
+ * QEMU migration TLS support
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "migration/migration.h"
+#include "io/channel-tls.h"
+#include "crypto/tlscreds.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+static QCryptoTLSCreds *
+migration_tls_get_creds(MigrationState *s,
+                        QCryptoTLSCredsEndpoint endpoint,
+                        Error **errp)
+{
+    Object *creds;
+    QCryptoTLSCreds *ret;
+
+    creds = object_resolve_path_component(
+        object_get_objects_root(), s->parameters.tls_creds);
+    if (!creds) {
+        error_setg(errp, "No TLS credentials with id '%s'",
+                   s->parameters.tls_creds);
+        return NULL;
+    }
+    ret = (QCryptoTLSCreds *)object_dynamic_cast(
+        creds, TYPE_QCRYPTO_TLS_CREDS);
+    if (!ret) {
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
+                   s->parameters.tls_creds);
+        return NULL;
+    }
+    if (ret->endpoint != endpoint) {
+        error_setg(errp,
+                   "Expected TLS credentials for a %s endpoint",
+                   endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT ?
+                   "client" : "server");
+        return NULL;
+    }
+
+    object_ref(OBJECT(ret));
+    return ret;
+}
+
+
+static void migration_tls_incoming_handshake(Object *src,
+                                             Error *err,
+                                             gpointer opaque)
+{
+    QIOChannel *ioc = QIO_CHANNEL(src);
+
+    if (err) {
+        trace_migration_tls_incoming_handshake_error(error_get_pretty(err));
+        error_report("%s", error_get_pretty(err));
+    } else {
+        trace_migration_tls_incoming_handshake_complete();
+        migration_set_incoming_channel(migrate_get_current(), ioc);
+    }
+    object_unref(OBJECT(ioc));
+}
+
+void migration_tls_set_incoming_channel(MigrationState *s,
+                                        QIOChannel *ioc,
+                                        Error **errp)
+{
+    QCryptoTLSCreds *creds;
+    QIOChannelTLS *tioc;
+
+    creds = migration_tls_get_creds(
+        s, QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp);
+    if (!creds) {
+        return;
+    }
+
+    tioc = qio_channel_tls_new_server(
+        ioc, creds,
+        NULL, /* XXX pass ACL name */
+        errp);
+    if (!tioc) {
+        return;
+    }
+
+    trace_migration_tls_incoming_handshake_start();
+    qio_channel_tls_handshake(tioc,
+                              migration_tls_incoming_handshake,
+                              NULL,
+                              NULL);
+}
+
+
+static void migration_tls_outgoing_handshake(Object *src,
+                                             Error *err,
+                                             gpointer opaque)
+{
+    MigrationState *s = opaque;
+    QIOChannel *ioc = QIO_CHANNEL(src);
+
+    if (err) {
+        trace_migration_tls_outgoing_handshake_error(error_get_pretty(err));
+        s->to_dst_file = NULL;
+        migrate_fd_error(s, err);
+    } else {
+        trace_migration_tls_outgoing_handshake_complete();
+        migration_set_outgoing_channel(s, ioc, NULL);
+    }
+    object_unref(OBJECT(ioc));
+}
+
+
+void migration_tls_set_outgoing_channel(MigrationState *s,
+                                        QIOChannel *ioc,
+                                        const char *hostname,
+                                        Error **errp)
+{
+    QCryptoTLSCreds *creds;
+    QIOChannelTLS *tioc;
+
+    creds = migration_tls_get_creds(
+        s, QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, errp);
+    if (!creds) {
+        return;
+    }
+
+    if (s->parameters.tls_hostname) {
+        hostname = s->parameters.tls_hostname;
+    }
+    if (!hostname) {
+        error_setg(errp, "No hostname available for TLS");
+        return;
+    }
+
+    tioc = qio_channel_tls_new_client(
+        ioc, creds, hostname, errp);
+    if (!tioc) {
+        return;
+    }
+
+    trace_migration_tls_outgoing_handshake_start(hostname);
+    qio_channel_tls_handshake(tioc,
+                              migration_tls_outgoing_handshake,
+                              s,
+                              NULL);
+}
diff --git a/trace-events b/trace-events
index fb4d961..e9249d3 100644
--- a/trace-events
+++ b/trace-events
@@ -1514,6 +1514,8 @@ migrate_state_too_big(void) ""
 migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64
 process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d"
 process_incoming_migration_co_postcopy_end_main(void) ""
+migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s"
+migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname)  "ioc=%p ioctype=%s hostname=%s"
 
 # migration/rdma.c
 qemu_rdma_accept_incoming_migration(void) ""
@@ -1608,9 +1610,17 @@ migration_fd_incoming(int fd) "fd=%d"
 
 # migration/socket.c
 migration_socket_incoming_accepted(void) ""
-migration_socket_outgoing_connected(void) ""
+migration_socket_outgoing_connected(const char *hostname) "hostname=%s"
 migration_socket_outgoing_error(const char *err) "error=%s"
 
+# migration/tls.c
+migration_tls_outgoing_handshake_start(const char *hostname) "hostname=%s"
+migration_tls_outgoing_handshake_error(const char *err) "err=%s"
+migration_tls_outgoing_handshake_complete(void) ""
+migration_tls_incoming_handshake_start(void) ""
+migration_tls_incoming_handshake_error(const char *err) "err=%s"
+migration_tls_incoming_handshake_complete(void) ""
+
 # kvm-all.c
 kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
 kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
-- 
2.5.5

  parent reply	other threads:[~2016-04-27 10:06 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-27 10:04 [Qemu-devel] [PATCH v6 for-2.7 00/28] Convert migration to QIOChannel & support Daniel P. Berrange
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 01/28] s390: use FILE instead of QEMUFile for creating text file Daniel P. Berrange
2016-05-04 10:43   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 02/28] io: avoid double-free when closing QIOChannelBuffer Daniel P. Berrange
2016-05-04 10:43   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 03/28] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
2016-05-04 10:45   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 04/28] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
2016-05-04 10:46   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 05/28] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
2016-05-04 10:48   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 06/28] migration: introduce set_blocking function in QEMUFileOps Daniel P. Berrange
2016-05-04 10:49   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 07/28] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
2016-05-04 10:49   ` Juan Quintela
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 08/28] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
2016-04-27 10:04 ` [Qemu-devel] [PATCH v6 for-2.7 09/28] migration: add helpers for creating QEMUFile from a QIOChannel Daniel P. Berrange
2016-05-04 10:56   ` Juan Quintela
2016-05-04 11:02   ` Juan Quintela
2016-05-24  6:01     ` Amit Shah
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 10/28] migration: add reporting of errors for outgoing migration Daniel P. Berrange
2016-05-04 10:53   ` Juan Quintela
2016-05-26 12:37   ` Eric Blake
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 11/28] migration: convert post-copy to use QIOChannelBuffer Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 12/28] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 13/28] migration: rename unix.c to socket.c Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 14/28] migration: convert tcp socket protocol to use QIOChannel Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 15/28] migration: convert fd " Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 16/28] migration: convert exec " Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 17/28] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 18/28] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 19/28] migration: delete QEMUFile buffer implementation Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 20/28] migration: delete QEMUSizedBuffer struct Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 21/28] migration: delete QEMUFile sockets implementation Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 22/28] migration: delete QEMUFile stdio implementation Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 23/28] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 24/28] migration: don't use an array for storing migrate parameters Daniel P. Berrange
2016-05-25 11:10   ` Amit Shah
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 25/28] migration: define 'tls-creds' and 'tls-hostname' migration parameters Daniel P. Berrange
2016-05-25 11:53   ` Amit Shah
2016-04-27 10:05 ` Daniel P. Berrange [this message]
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 27/28] migration: remove support for non-iovec based write handlers Daniel P. Berrange
2016-04-27 10:05 ` [Qemu-devel] [PATCH v6 for-2.7 28/28] migration: remove qemu_get_fd method from QEMUFile Daniel P. Berrange
2016-05-26  6:17 ` [Qemu-devel] [PATCH v6 for-2.7 00/28] Convert migration to QIOChannel & support Amit Shah
2016-05-31  9:21   ` Daniel P. Berrange

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=1461751518-12128-27-git-send-email-berrange@redhat.com \
    --to=berrange@redhat.com \
    --cc=amit.shah@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@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).