master
c 264 lines 7.64 KB
Raw
1 /*
2 * QEMU crypto TLS Pre-Shared Keys (PSK) support
3 *
4 * Copyright (c) 2018 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.1 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 "crypto/tlscredspsk.h"
23 #include "tlscredspriv.h"
24 #include "qapi/error.h"
25 #include "qemu/module.h"
26 #include "qom/object_interfaces.h"
27 #include "trace.h"
28
29
30 struct QCryptoTLSCredsPSK {
31 QCryptoTLSCreds parent_obj;
32 char *username;
33 };
34
35 #ifdef CONFIG_GNUTLS
36
37 #include <gnutls/gnutls.h>
38
39 static int
40 lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key,
41 Error **errp)
42 {
43 const size_t ulen = strlen(username);
44 GError *gerr = NULL;
45 char *content = NULL;
46 char **lines = NULL;
47 size_t clen = 0, i;
48 int ret = -1;
49
50 if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) {
51 error_setg(errp, "Cannot read PSK file %s: %s",
52 pskfile, gerr->message);
53 g_error_free(gerr);
54 return -1;
55 }
56
57 lines = g_strsplit(content, "\n", -1);
58 for (i = 0; lines[i] != NULL; ++i) {
59 if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') {
60 key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]);
61 key->size = strlen(lines[i]) - ulen - 1;
62 ret = 0;
63 goto out;
64 }
65 }
66 error_setg(errp, "Username %s not found in PSK file %s",
67 username, pskfile);
68
69 out:
70 free(content);
71 g_strfreev(lines);
72 return ret;
73 }
74
75 static int
76 qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
77 Error **errp)
78 {
79 g_autoptr(QCryptoTLSCredsBox) box = NULL;
80 g_autofree char *pskfile = NULL;
81 g_autofree char *dhparams = NULL;
82 const char *username;
83 int ret;
84 int rv = -1;
85 gnutls_datum_t key = { .data = NULL };
86
87 trace_qcrypto_tls_creds_psk_load(creds,
88 creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
89
90 if (!creds->parent_obj.dir) {
91 error_setg(errp, "Missing 'dir' property value");
92 goto cleanup;
93 }
94
95 if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
96 box = qcrypto_tls_creds_box_new_server(GNUTLS_CRD_PSK);
97
98 if (creds->username) {
99 error_setg(errp, "username should not be set when endpoint=server");
100 goto cleanup;
101 }
102
103 if (qcrypto_tls_creds_get_path(&creds->parent_obj,
104 QCRYPTO_TLS_CREDS_DH_PARAMS,
105 false, &dhparams, errp) < 0 ||
106 qcrypto_tls_creds_get_path(&creds->parent_obj,
107 QCRYPTO_TLS_CREDS_PSKFILE,
108 true, &pskfile, errp) < 0) {
109 goto cleanup;
110 }
111
112 ret = gnutls_psk_allocate_server_credentials(&box->data.pskserver);
113 if (ret < 0) {
114 error_setg(errp, "Cannot allocate credentials: %s",
115 gnutls_strerror(ret));
116 goto cleanup;
117 }
118
119 if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
120 &box->dh_params,
121 errp) < 0) {
122 goto cleanup;
123 }
124
125 ret = gnutls_psk_set_server_credentials_file(box->data.pskserver,
126 pskfile);
127 if (ret < 0) {
128 error_setg(errp, "Cannot set PSK server credentials: %s",
129 gnutls_strerror(ret));
130 goto cleanup;
131 }
132 if (box->dh_params) {
133 gnutls_psk_set_server_dh_params(box->data.pskserver,
134 box->dh_params);
135 }
136 } else {
137 box = qcrypto_tls_creds_box_new_client(GNUTLS_CRD_PSK);
138
139 if (qcrypto_tls_creds_get_path(&creds->parent_obj,
140 QCRYPTO_TLS_CREDS_PSKFILE,
141 true, &pskfile, errp) < 0) {
142 goto cleanup;
143 }
144
145 if (creds->username) {
146 username = creds->username;
147 } else {
148 username = "qemu";
149 }
150 if (lookup_key(pskfile, username, &key, errp) != 0) {
151 goto cleanup;
152 }
153
154 ret = gnutls_psk_allocate_client_credentials(&box->data.pskclient);
155 if (ret < 0) {
156 error_setg(errp, "Cannot allocate credentials: %s",
157 gnutls_strerror(ret));
158 goto cleanup;
159 }
160
161 ret = gnutls_psk_set_client_credentials(box->data.pskclient,
162 username, &key, GNUTLS_PSK_KEY_HEX);
163 if (ret < 0) {
164 error_setg(errp, "Cannot set PSK client credentials: %s",
165 gnutls_strerror(ret));
166 goto cleanup;
167 }
168 }
169 creds->parent_obj.box = g_steal_pointer(&box);
170
171 rv = 0;
172 cleanup:
173 g_free(key.data);
174 return rv;
175 }
176
177 #else /* ! CONFIG_GNUTLS */
178
179
180 static void
181 qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED,
182 Error **errp)
183 {
184 error_setg(errp, "TLS credentials support requires GNUTLS");
185 }
186
187
188 #endif /* ! CONFIG_GNUTLS */
189
190
191 static void
192 qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp)
193 {
194 QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(uc);
195
196 qcrypto_tls_creds_psk_load(creds, errp);
197 }
198
199
200 static void
201 qcrypto_tls_creds_psk_finalize(Object *obj)
202 {
203 QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
204
205 g_free(creds->username);
206 }
207
208 static void
209 qcrypto_tls_creds_psk_prop_set_username(Object *obj,
210 const char *value,
211 Error **errp G_GNUC_UNUSED)
212 {
213 QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
214
215 creds->username = g_strdup(value);
216 }
217
218
219 static char *
220 qcrypto_tls_creds_psk_prop_get_username(Object *obj,
221 Error **errp G_GNUC_UNUSED)
222 {
223 QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
224
225 return g_strdup(creds->username);
226 }
227
228 static void
229 qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data)
230 {
231 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
232 QCryptoTLSCredsClass *tcc = QCRYPTO_TLS_CREDS_CLASS(oc);
233
234 ucc->complete = qcrypto_tls_creds_psk_complete;
235 tcc->prioritySuffix = "+ECDHE-PSK:+DHE-PSK:+PSK";
236
237 object_class_property_add_str(oc, "username",
238 qcrypto_tls_creds_psk_prop_get_username,
239 qcrypto_tls_creds_psk_prop_set_username);
240 }
241
242
243 static const TypeInfo qcrypto_tls_creds_psk_info = {
244 .parent = TYPE_QCRYPTO_TLS_CREDS,
245 .name = TYPE_QCRYPTO_TLS_CREDS_PSK,
246 .instance_size = sizeof(QCryptoTLSCredsPSK),
247 .instance_finalize = qcrypto_tls_creds_psk_finalize,
248 .class_size = sizeof(QCryptoTLSCredsPSKClass),
249 .class_init = qcrypto_tls_creds_psk_class_init,
250 .interfaces = (const InterfaceInfo[]) {
251 { TYPE_USER_CREATABLE },
252 { }
253 }
254 };
255
256
257 static void
258 qcrypto_tls_creds_psk_register_types(void)
259 {
260 type_register_static(&qcrypto_tls_creds_psk_info);
261 }
262
263
264 type_init(qcrypto_tls_creds_psk_register_types);