xref: /qemu/migration/tls.c (revision 92eecfff)
1 /*
2  * QEMU migration TLS support
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "qemu/osdep.h"
22 #include "channel.h"
23 #include "migration.h"
24 #include "tls.h"
25 #include "crypto/tlscreds.h"
26 #include "qemu/error-report.h"
27 #include "qapi/error.h"
28 #include "trace.h"
29 
30 static QCryptoTLSCreds *
31 migration_tls_get_creds(MigrationState *s,
32                         QCryptoTLSCredsEndpoint endpoint,
33                         Error **errp)
34 {
35     Object *creds;
36     QCryptoTLSCreds *ret;
37 
38     creds = object_resolve_path_component(
39         object_get_objects_root(), s->parameters.tls_creds);
40     if (!creds) {
41         error_setg(errp, "No TLS credentials with id '%s'",
42                    s->parameters.tls_creds);
43         return NULL;
44     }
45     ret = (QCryptoTLSCreds *)object_dynamic_cast(
46         creds, TYPE_QCRYPTO_TLS_CREDS);
47     if (!ret) {
48         error_setg(errp, "Object with id '%s' is not TLS credentials",
49                    s->parameters.tls_creds);
50         return NULL;
51     }
52     if (ret->endpoint != endpoint) {
53         error_setg(errp,
54                    "Expected TLS credentials for a %s endpoint",
55                    endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT ?
56                    "client" : "server");
57         return NULL;
58     }
59 
60     return ret;
61 }
62 
63 
64 static void migration_tls_incoming_handshake(QIOTask *task,
65                                              gpointer opaque)
66 {
67     QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
68     Error *err = NULL;
69 
70     if (qio_task_propagate_error(task, &err)) {
71         trace_migration_tls_incoming_handshake_error(error_get_pretty(err));
72         error_report_err(err);
73     } else {
74         trace_migration_tls_incoming_handshake_complete();
75         migration_channel_process_incoming(ioc);
76     }
77     object_unref(OBJECT(ioc));
78 }
79 
80 void migration_tls_channel_process_incoming(MigrationState *s,
81                                             QIOChannel *ioc,
82                                             Error **errp)
83 {
84     QCryptoTLSCreds *creds;
85     QIOChannelTLS *tioc;
86 
87     creds = migration_tls_get_creds(
88         s, QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp);
89     if (!creds) {
90         return;
91     }
92 
93     tioc = qio_channel_tls_new_server(
94         ioc, creds,
95         s->parameters.tls_authz,
96         errp);
97     if (!tioc) {
98         return;
99     }
100 
101     trace_migration_tls_incoming_handshake_start();
102     qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming");
103     qio_channel_tls_handshake(tioc,
104                               migration_tls_incoming_handshake,
105                               NULL,
106                               NULL,
107                               NULL);
108 }
109 
110 
111 static void migration_tls_outgoing_handshake(QIOTask *task,
112                                              gpointer opaque)
113 {
114     MigrationState *s = opaque;
115     QIOChannel *ioc = QIO_CHANNEL(qio_task_get_source(task));
116     Error *err = NULL;
117 
118     if (qio_task_propagate_error(task, &err)) {
119         trace_migration_tls_outgoing_handshake_error(error_get_pretty(err));
120     } else {
121         trace_migration_tls_outgoing_handshake_complete();
122     }
123     migration_channel_connect(s, ioc, NULL, err);
124     object_unref(OBJECT(ioc));
125 }
126 
127 QIOChannelTLS *migration_tls_client_create(MigrationState *s,
128                                            QIOChannel *ioc,
129                                            const char *hostname,
130                                            Error **errp)
131 {
132     QCryptoTLSCreds *creds;
133     QIOChannelTLS *tioc;
134 
135     creds = migration_tls_get_creds(
136         s, QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, errp);
137     if (!creds) {
138         return NULL;
139     }
140 
141     if (s->parameters.tls_hostname && *s->parameters.tls_hostname) {
142         hostname = s->parameters.tls_hostname;
143     }
144     if (!hostname) {
145         error_setg(errp, "No hostname available for TLS");
146         return NULL;
147     }
148 
149     tioc = qio_channel_tls_new_client(
150         ioc, creds, hostname, errp);
151 
152     return tioc;
153 }
154 
155 void migration_tls_channel_connect(MigrationState *s,
156                                    QIOChannel *ioc,
157                                    const char *hostname,
158                                    Error **errp)
159 {
160     QIOChannelTLS *tioc;
161 
162     tioc = migration_tls_client_create(s, ioc, hostname, errp);
163     if (!tioc) {
164         return;
165     }
166 
167     /* Save hostname into MigrationState for handshake */
168     s->hostname = g_strdup(hostname);
169     trace_migration_tls_outgoing_handshake_start(hostname);
170     qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing");
171     qio_channel_tls_handshake(tioc,
172                               migration_tls_outgoing_handshake,
173                               s,
174                               NULL,
175                               NULL);
176 }
177