xref: /openbmc/qemu/crypto/tlscredspsk.c (revision 731d58b545ef66072d38b428fe0dcd1d691e364c)
1e1a6dc91SRichard W.M. Jones /*
2e1a6dc91SRichard W.M. Jones  * QEMU crypto TLS Pre-Shared Keys (PSK) support
3e1a6dc91SRichard W.M. Jones  *
4e1a6dc91SRichard W.M. Jones  * Copyright (c) 2018 Red Hat, Inc.
5e1a6dc91SRichard W.M. Jones  *
6e1a6dc91SRichard W.M. Jones  * This library is free software; you can redistribute it and/or
7e1a6dc91SRichard W.M. Jones  * modify it under the terms of the GNU Lesser General Public
8e1a6dc91SRichard W.M. Jones  * License as published by the Free Software Foundation; either
9b7cbb874SThomas Huth  * version 2.1 of the License, or (at your option) any later version.
10e1a6dc91SRichard W.M. Jones  *
11e1a6dc91SRichard W.M. Jones  * This library is distributed in the hope that it will be useful,
12e1a6dc91SRichard W.M. Jones  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13e1a6dc91SRichard W.M. Jones  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14e1a6dc91SRichard W.M. Jones  * Lesser General Public License for more details.
15e1a6dc91SRichard W.M. Jones  *
16e1a6dc91SRichard W.M. Jones  * You should have received a copy of the GNU Lesser General Public
17e1a6dc91SRichard W.M. Jones  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18e1a6dc91SRichard W.M. Jones  *
19e1a6dc91SRichard W.M. Jones  */
20e1a6dc91SRichard W.M. Jones 
21e1a6dc91SRichard W.M. Jones #include "qemu/osdep.h"
22e1a6dc91SRichard W.M. Jones #include "crypto/tlscredspsk.h"
23e1a6dc91SRichard W.M. Jones #include "tlscredspriv.h"
24e1a6dc91SRichard W.M. Jones #include "qapi/error.h"
250b8fa32fSMarkus Armbruster #include "qemu/module.h"
26e1a6dc91SRichard W.M. Jones #include "qom/object_interfaces.h"
27e1a6dc91SRichard W.M. Jones #include "trace.h"
28e1a6dc91SRichard W.M. Jones 
29e1a6dc91SRichard W.M. Jones 
30e1a6dc91SRichard W.M. Jones #ifdef CONFIG_GNUTLS
31e1a6dc91SRichard W.M. Jones 
32678bcc3cSPhilippe Mathieu-Daudé #include <gnutls/gnutls.h>
33678bcc3cSPhilippe Mathieu-Daudé 
34e1a6dc91SRichard W.M. Jones static int
lookup_key(const char * pskfile,const char * username,gnutls_datum_t * key,Error ** errp)35e1a6dc91SRichard W.M. Jones lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key,
36e1a6dc91SRichard W.M. Jones            Error **errp)
37e1a6dc91SRichard W.M. Jones {
38e1a6dc91SRichard W.M. Jones     const size_t ulen = strlen(username);
39e1a6dc91SRichard W.M. Jones     GError *gerr = NULL;
40e1a6dc91SRichard W.M. Jones     char *content = NULL;
41e1a6dc91SRichard W.M. Jones     char **lines = NULL;
42e1a6dc91SRichard W.M. Jones     size_t clen = 0, i;
43e1a6dc91SRichard W.M. Jones     int ret = -1;
44e1a6dc91SRichard W.M. Jones 
45e1a6dc91SRichard W.M. Jones     if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) {
46e1a6dc91SRichard W.M. Jones         error_setg(errp, "Cannot read PSK file %s: %s",
47e1a6dc91SRichard W.M. Jones                    pskfile, gerr->message);
48e1a6dc91SRichard W.M. Jones         g_error_free(gerr);
49e1a6dc91SRichard W.M. Jones         return -1;
50e1a6dc91SRichard W.M. Jones     }
51e1a6dc91SRichard W.M. Jones 
52e1a6dc91SRichard W.M. Jones     lines = g_strsplit(content, "\n", -1);
53e1a6dc91SRichard W.M. Jones     for (i = 0; lines[i] != NULL; ++i) {
54e1a6dc91SRichard W.M. Jones         if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') {
55e1a6dc91SRichard W.M. Jones             key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]);
56e1a6dc91SRichard W.M. Jones             key->size = strlen(lines[i]) - ulen - 1;
57e1a6dc91SRichard W.M. Jones             ret = 0;
58e1a6dc91SRichard W.M. Jones             goto out;
59e1a6dc91SRichard W.M. Jones         }
60e1a6dc91SRichard W.M. Jones     }
61e1a6dc91SRichard W.M. Jones     error_setg(errp, "Username %s not found in PSK file %s",
62e1a6dc91SRichard W.M. Jones                username, pskfile);
63e1a6dc91SRichard W.M. Jones 
64e1a6dc91SRichard W.M. Jones  out:
65e1a6dc91SRichard W.M. Jones     free(content);
66e1a6dc91SRichard W.M. Jones     g_strfreev(lines);
67e1a6dc91SRichard W.M. Jones     return ret;
68e1a6dc91SRichard W.M. Jones }
69e1a6dc91SRichard W.M. Jones 
70e1a6dc91SRichard W.M. Jones static int
qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK * creds,Error ** errp)71e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds,
72e1a6dc91SRichard W.M. Jones                            Error **errp)
73e1a6dc91SRichard W.M. Jones {
7457b9f113SDaniel P. Berrangé     g_autofree char *pskfile = NULL;
7557b9f113SDaniel P. Berrangé     g_autofree char *dhparams = NULL;
76e1a6dc91SRichard W.M. Jones     const char *username;
77e1a6dc91SRichard W.M. Jones     int ret;
78e1a6dc91SRichard W.M. Jones     int rv = -1;
79e1a6dc91SRichard W.M. Jones     gnutls_datum_t key = { .data = NULL };
80e1a6dc91SRichard W.M. Jones 
81e1a6dc91SRichard W.M. Jones     trace_qcrypto_tls_creds_psk_load(creds,
82e1a6dc91SRichard W.M. Jones             creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
83e1a6dc91SRichard W.M. Jones 
84e1a6dc91SRichard W.M. Jones     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
85e1a6dc91SRichard W.M. Jones         if (creds->username) {
86e1a6dc91SRichard W.M. Jones             error_setg(errp, "username should not be set when endpoint=server");
87e1a6dc91SRichard W.M. Jones             goto cleanup;
88e1a6dc91SRichard W.M. Jones         }
89e1a6dc91SRichard W.M. Jones 
90e1a6dc91SRichard W.M. Jones         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
91e1a6dc91SRichard W.M. Jones                                        QCRYPTO_TLS_CREDS_DH_PARAMS,
92e1a6dc91SRichard W.M. Jones                                        false, &dhparams, errp) < 0 ||
93e1a6dc91SRichard W.M. Jones             qcrypto_tls_creds_get_path(&creds->parent_obj,
94e1a6dc91SRichard W.M. Jones                                        QCRYPTO_TLS_CREDS_PSKFILE,
95e1a6dc91SRichard W.M. Jones                                        true, &pskfile, errp) < 0) {
96e1a6dc91SRichard W.M. Jones             goto cleanup;
97e1a6dc91SRichard W.M. Jones         }
98e1a6dc91SRichard W.M. Jones 
99e1a6dc91SRichard W.M. Jones         ret = gnutls_psk_allocate_server_credentials(&creds->data.server);
100e1a6dc91SRichard W.M. Jones         if (ret < 0) {
101e1a6dc91SRichard W.M. Jones             error_setg(errp, "Cannot allocate credentials: %s",
102e1a6dc91SRichard W.M. Jones                        gnutls_strerror(ret));
103e1a6dc91SRichard W.M. Jones             goto cleanup;
104e1a6dc91SRichard W.M. Jones         }
105e1a6dc91SRichard W.M. Jones 
106e1a6dc91SRichard W.M. Jones         if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams,
107e1a6dc91SRichard W.M. Jones                                                  &creds->parent_obj.dh_params,
108e1a6dc91SRichard W.M. Jones                                                  errp) < 0) {
109e1a6dc91SRichard W.M. Jones             goto cleanup;
110e1a6dc91SRichard W.M. Jones         }
111e1a6dc91SRichard W.M. Jones 
1123983bf1bSDaniel P. Berrangé         ret = gnutls_psk_set_server_credentials_file(creds->data.server, pskfile);
1133983bf1bSDaniel P. Berrangé         if (ret < 0) {
1143983bf1bSDaniel P. Berrangé             error_setg(errp, "Cannot set PSK server credentials: %s",
1153983bf1bSDaniel P. Berrangé                        gnutls_strerror(ret));
1163983bf1bSDaniel P. Berrangé             goto cleanup;
1173983bf1bSDaniel P. Berrangé         }
118e1a6dc91SRichard W.M. Jones         gnutls_psk_set_server_dh_params(creds->data.server,
119e1a6dc91SRichard W.M. Jones                                         creds->parent_obj.dh_params);
120e1a6dc91SRichard W.M. Jones     } else {
121e1a6dc91SRichard W.M. Jones         if (qcrypto_tls_creds_get_path(&creds->parent_obj,
122e1a6dc91SRichard W.M. Jones                                        QCRYPTO_TLS_CREDS_PSKFILE,
123e1a6dc91SRichard W.M. Jones                                        true, &pskfile, errp) < 0) {
124e1a6dc91SRichard W.M. Jones             goto cleanup;
125e1a6dc91SRichard W.M. Jones         }
126e1a6dc91SRichard W.M. Jones 
127e1a6dc91SRichard W.M. Jones         if (creds->username) {
128e1a6dc91SRichard W.M. Jones             username = creds->username;
129e1a6dc91SRichard W.M. Jones         } else {
130e1a6dc91SRichard W.M. Jones             username = "qemu";
131e1a6dc91SRichard W.M. Jones         }
132e1a6dc91SRichard W.M. Jones         if (lookup_key(pskfile, username, &key, errp) != 0) {
133e1a6dc91SRichard W.M. Jones             goto cleanup;
134e1a6dc91SRichard W.M. Jones         }
135e1a6dc91SRichard W.M. Jones 
136e1a6dc91SRichard W.M. Jones         ret = gnutls_psk_allocate_client_credentials(&creds->data.client);
137e1a6dc91SRichard W.M. Jones         if (ret < 0) {
138e1a6dc91SRichard W.M. Jones             error_setg(errp, "Cannot allocate credentials: %s",
139e1a6dc91SRichard W.M. Jones                        gnutls_strerror(ret));
140e1a6dc91SRichard W.M. Jones             goto cleanup;
141e1a6dc91SRichard W.M. Jones         }
142e1a6dc91SRichard W.M. Jones 
1433983bf1bSDaniel P. Berrangé         ret = gnutls_psk_set_client_credentials(creds->data.client,
144e1a6dc91SRichard W.M. Jones                                                 username, &key, GNUTLS_PSK_KEY_HEX);
1453983bf1bSDaniel P. Berrangé         if (ret < 0) {
1463983bf1bSDaniel P. Berrangé             error_setg(errp, "Cannot set PSK client credentials: %s",
1473983bf1bSDaniel P. Berrangé                        gnutls_strerror(ret));
1483983bf1bSDaniel P. Berrangé             goto cleanup;
1493983bf1bSDaniel P. Berrangé         }
150e1a6dc91SRichard W.M. Jones     }
151e1a6dc91SRichard W.M. Jones 
152e1a6dc91SRichard W.M. Jones     rv = 0;
153e1a6dc91SRichard W.M. Jones  cleanup:
154e1a6dc91SRichard W.M. Jones     g_free(key.data);
155e1a6dc91SRichard W.M. Jones     return rv;
156e1a6dc91SRichard W.M. Jones }
157e1a6dc91SRichard W.M. Jones 
158e1a6dc91SRichard W.M. Jones 
159e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK * creds)160e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds)
161e1a6dc91SRichard W.M. Jones {
162e1a6dc91SRichard W.M. Jones     if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
163e1a6dc91SRichard W.M. Jones         if (creds->data.client) {
164e1a6dc91SRichard W.M. Jones             gnutls_psk_free_client_credentials(creds->data.client);
165e1a6dc91SRichard W.M. Jones             creds->data.client = NULL;
166e1a6dc91SRichard W.M. Jones         }
167e1a6dc91SRichard W.M. Jones     } else {
168e1a6dc91SRichard W.M. Jones         if (creds->data.server) {
169e1a6dc91SRichard W.M. Jones             gnutls_psk_free_server_credentials(creds->data.server);
170e1a6dc91SRichard W.M. Jones             creds->data.server = NULL;
171e1a6dc91SRichard W.M. Jones         }
172e1a6dc91SRichard W.M. Jones     }
173e1a6dc91SRichard W.M. Jones     if (creds->parent_obj.dh_params) {
174e1a6dc91SRichard W.M. Jones         gnutls_dh_params_deinit(creds->parent_obj.dh_params);
175e1a6dc91SRichard W.M. Jones         creds->parent_obj.dh_params = NULL;
176e1a6dc91SRichard W.M. Jones     }
177e1a6dc91SRichard W.M. Jones }
178e1a6dc91SRichard W.M. Jones 
179e1a6dc91SRichard W.M. Jones #else /* ! CONFIG_GNUTLS */
180e1a6dc91SRichard W.M. Jones 
181e1a6dc91SRichard W.M. Jones 
182e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK * creds G_GNUC_UNUSED,Error ** errp)183e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED,
184e1a6dc91SRichard W.M. Jones                            Error **errp)
185e1a6dc91SRichard W.M. Jones {
186e1a6dc91SRichard W.M. Jones     error_setg(errp, "TLS credentials support requires GNUTLS");
187e1a6dc91SRichard W.M. Jones }
188e1a6dc91SRichard W.M. Jones 
189e1a6dc91SRichard W.M. Jones 
190e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK * creds G_GNUC_UNUSED)191e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED)
192e1a6dc91SRichard W.M. Jones {
193e1a6dc91SRichard W.M. Jones     /* nada */
194e1a6dc91SRichard W.M. Jones }
195e1a6dc91SRichard W.M. Jones 
196e1a6dc91SRichard W.M. Jones 
197e1a6dc91SRichard W.M. Jones #endif /* ! CONFIG_GNUTLS */
198e1a6dc91SRichard W.M. Jones 
199e1a6dc91SRichard W.M. Jones 
200e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_complete(UserCreatable * uc,Error ** errp)2010310641cSPaolo Bonzini qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp)
202e1a6dc91SRichard W.M. Jones {
2030310641cSPaolo Bonzini     QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(uc);
204e1a6dc91SRichard W.M. Jones 
205e1a6dc91SRichard W.M. Jones     qcrypto_tls_creds_psk_load(creds, errp);
206e1a6dc91SRichard W.M. Jones }
207e1a6dc91SRichard W.M. Jones 
208e1a6dc91SRichard W.M. Jones 
209e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_finalize(Object * obj)210e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_finalize(Object *obj)
211e1a6dc91SRichard W.M. Jones {
212e1a6dc91SRichard W.M. Jones     QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
213e1a6dc91SRichard W.M. Jones 
214e1a6dc91SRichard W.M. Jones     qcrypto_tls_creds_psk_unload(creds);
215*87e012f2SPeter Maydell     g_free(creds->username);
216e1a6dc91SRichard W.M. Jones }
217e1a6dc91SRichard W.M. Jones 
218e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_prop_set_username(Object * obj,const char * value,Error ** errp G_GNUC_UNUSED)219e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_prop_set_username(Object *obj,
220e1a6dc91SRichard W.M. Jones                                         const char *value,
221e1a6dc91SRichard W.M. Jones                                         Error **errp G_GNUC_UNUSED)
222e1a6dc91SRichard W.M. Jones {
223e1a6dc91SRichard W.M. Jones     QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
224e1a6dc91SRichard W.M. Jones 
225e1a6dc91SRichard W.M. Jones     creds->username = g_strdup(value);
226e1a6dc91SRichard W.M. Jones }
227e1a6dc91SRichard W.M. Jones 
228e1a6dc91SRichard W.M. Jones 
229e1a6dc91SRichard W.M. Jones static char *
qcrypto_tls_creds_psk_prop_get_username(Object * obj,Error ** errp G_GNUC_UNUSED)230e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_prop_get_username(Object *obj,
231e1a6dc91SRichard W.M. Jones                                         Error **errp G_GNUC_UNUSED)
232e1a6dc91SRichard W.M. Jones {
233e1a6dc91SRichard W.M. Jones     QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
234e1a6dc91SRichard W.M. Jones 
235e1a6dc91SRichard W.M. Jones     return g_strdup(creds->username);
236e1a6dc91SRichard W.M. Jones }
237e1a6dc91SRichard W.M. Jones 
238e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_class_init(ObjectClass * oc,void * data)239e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data)
240e1a6dc91SRichard W.M. Jones {
241e1a6dc91SRichard W.M. Jones     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
242e1a6dc91SRichard W.M. Jones 
243e1a6dc91SRichard W.M. Jones     ucc->complete = qcrypto_tls_creds_psk_complete;
244e1a6dc91SRichard W.M. Jones 
245e1a6dc91SRichard W.M. Jones     object_class_property_add_str(oc, "username",
246e1a6dc91SRichard W.M. Jones                                   qcrypto_tls_creds_psk_prop_get_username,
247d2623129SMarkus Armbruster                                   qcrypto_tls_creds_psk_prop_set_username);
248e1a6dc91SRichard W.M. Jones }
249e1a6dc91SRichard W.M. Jones 
250e1a6dc91SRichard W.M. Jones 
251e1a6dc91SRichard W.M. Jones static const TypeInfo qcrypto_tls_creds_psk_info = {
252e1a6dc91SRichard W.M. Jones     .parent = TYPE_QCRYPTO_TLS_CREDS,
253e1a6dc91SRichard W.M. Jones     .name = TYPE_QCRYPTO_TLS_CREDS_PSK,
254e1a6dc91SRichard W.M. Jones     .instance_size = sizeof(QCryptoTLSCredsPSK),
255e1a6dc91SRichard W.M. Jones     .instance_finalize = qcrypto_tls_creds_psk_finalize,
256e1a6dc91SRichard W.M. Jones     .class_size = sizeof(QCryptoTLSCredsPSKClass),
257e1a6dc91SRichard W.M. Jones     .class_init = qcrypto_tls_creds_psk_class_init,
258e1a6dc91SRichard W.M. Jones     .interfaces = (InterfaceInfo[]) {
259e1a6dc91SRichard W.M. Jones         { TYPE_USER_CREATABLE },
260e1a6dc91SRichard W.M. Jones         { }
261e1a6dc91SRichard W.M. Jones     }
262e1a6dc91SRichard W.M. Jones };
263e1a6dc91SRichard W.M. Jones 
264e1a6dc91SRichard W.M. Jones 
265e1a6dc91SRichard W.M. Jones static void
qcrypto_tls_creds_psk_register_types(void)266e1a6dc91SRichard W.M. Jones qcrypto_tls_creds_psk_register_types(void)
267e1a6dc91SRichard W.M. Jones {
268e1a6dc91SRichard W.M. Jones     type_register_static(&qcrypto_tls_creds_psk_info);
269e1a6dc91SRichard W.M. Jones }
270e1a6dc91SRichard W.M. Jones 
271e1a6dc91SRichard W.M. Jones 
272e1a6dc91SRichard W.M. Jones type_init(qcrypto_tls_creds_psk_register_types);
273