]>
Commit | Line | Data |
---|---|---|
85bcbc78 DB |
1 | /* |
2 | * QEMU crypto TLS x509 credential 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 | ||
42f7a448 | 21 | #include "qemu/osdep.h" |
85bcbc78 DB |
22 | #include "crypto/tlscredsx509.h" |
23 | #include "crypto/tlscredspriv.h" | |
1d7b5b4a | 24 | #include "crypto/secret.h" |
85bcbc78 DB |
25 | #include "qom/object_interfaces.h" |
26 | #include "trace.h" | |
27 | ||
28 | ||
29 | #ifdef CONFIG_GNUTLS | |
30 | ||
9a2fd434 DB |
31 | #include <gnutls/x509.h> |
32 | ||
33 | ||
34 | static int | |
35 | qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert, | |
36 | const char *certFile, | |
37 | bool isServer, | |
38 | bool isCA, | |
39 | Error **errp) | |
40 | { | |
41 | time_t now = time(NULL); | |
42 | ||
43 | if (now == ((time_t)-1)) { | |
44 | error_setg_errno(errp, errno, "cannot get current time"); | |
45 | return -1; | |
46 | } | |
47 | ||
48 | if (gnutls_x509_crt_get_expiration_time(cert) < now) { | |
49 | error_setg(errp, | |
50 | (isCA ? | |
51 | "The CA certificate %s has expired" : | |
52 | (isServer ? | |
53 | "The server certificate %s has expired" : | |
54 | "The client certificate %s has expired")), | |
55 | certFile); | |
56 | return -1; | |
57 | } | |
58 | ||
59 | if (gnutls_x509_crt_get_activation_time(cert) > now) { | |
60 | error_setg(errp, | |
61 | (isCA ? | |
62 | "The CA certificate %s is not yet active" : | |
63 | (isServer ? | |
64 | "The server certificate %s is not yet active" : | |
65 | "The client certificate %s is not yet active")), | |
66 | certFile); | |
67 | return -1; | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | ||
74 | #if LIBGNUTLS_VERSION_NUMBER >= 2 | |
75 | /* | |
76 | * The gnutls_x509_crt_get_basic_constraints function isn't | |
77 | * available in GNUTLS 1.0.x branches. This isn't critical | |
78 | * though, since gnutls_certificate_verify_peers2 will do | |
79 | * pretty much the same check at runtime, so we can just | |
80 | * disable this code | |
81 | */ | |
82 | static int | |
83 | qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, | |
84 | gnutls_x509_crt_t cert, | |
85 | const char *certFile, | |
86 | bool isServer, | |
87 | bool isCA, | |
88 | Error **errp) | |
89 | { | |
90 | int status; | |
91 | ||
92 | status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); | |
93 | trace_qcrypto_tls_creds_x509_check_basic_constraints( | |
94 | creds, certFile, status); | |
95 | ||
96 | if (status > 0) { /* It is a CA cert */ | |
97 | if (!isCA) { | |
98 | error_setg(errp, isServer ? | |
99 | "The certificate %s basic constraints show a CA, " | |
100 | "but we need one for a server" : | |
101 | "The certificate %s basic constraints show a CA, " | |
102 | "but we need one for a client", | |
103 | certFile); | |
104 | return -1; | |
105 | } | |
106 | } else if (status == 0) { /* It is not a CA cert */ | |
107 | if (isCA) { | |
108 | error_setg(errp, | |
109 | "The certificate %s basic constraints do not " | |
110 | "show a CA", | |
111 | certFile); | |
112 | return -1; | |
113 | } | |
114 | } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { | |
115 | /* Missing basicConstraints */ | |
116 | if (isCA) { | |
117 | error_setg(errp, | |
118 | "The certificate %s is missing basic constraints " | |
119 | "for a CA", | |
120 | certFile); | |
121 | return -1; | |
122 | } | |
123 | } else { /* General error */ | |
124 | error_setg(errp, | |
125 | "Unable to query certificate %s basic constraints: %s", | |
126 | certFile, gnutls_strerror(status)); | |
127 | return -1; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | #endif | |
133 | ||
134 | ||
135 | static int | |
136 | qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds, | |
137 | gnutls_x509_crt_t cert, | |
138 | const char *certFile, | |
139 | bool isCA, | |
140 | Error **errp) | |
141 | { | |
142 | int status; | |
143 | unsigned int usage = 0; | |
144 | unsigned int critical = 0; | |
145 | ||
146 | status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); | |
147 | trace_qcrypto_tls_creds_x509_check_key_usage( | |
148 | creds, certFile, status, usage, critical); | |
149 | ||
150 | if (status < 0) { | |
151 | if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { | |
152 | usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : | |
153 | GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; | |
154 | } else { | |
155 | error_setg(errp, | |
156 | "Unable to query certificate %s key usage: %s", | |
157 | certFile, gnutls_strerror(status)); | |
158 | return -1; | |
159 | } | |
160 | } | |
161 | ||
162 | if (isCA) { | |
163 | if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { | |
164 | if (critical) { | |
165 | error_setg(errp, | |
166 | "Certificate %s usage does not permit " | |
167 | "certificate signing", certFile); | |
168 | return -1; | |
169 | } | |
170 | } | |
171 | } else { | |
172 | if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { | |
173 | if (critical) { | |
174 | error_setg(errp, | |
175 | "Certificate %s usage does not permit digital " | |
176 | "signature", certFile); | |
177 | return -1; | |
178 | } | |
179 | } | |
180 | if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { | |
181 | if (critical) { | |
182 | error_setg(errp, | |
183 | "Certificate %s usage does not permit key " | |
184 | "encipherment", certFile); | |
185 | return -1; | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | ||
194 | static int | |
195 | qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds, | |
196 | gnutls_x509_crt_t cert, | |
197 | const char *certFile, | |
198 | bool isServer, | |
199 | Error **errp) | |
200 | { | |
201 | int status; | |
202 | size_t i; | |
203 | unsigned int purposeCritical; | |
204 | unsigned int critical; | |
205 | char *buffer = NULL; | |
206 | size_t size; | |
207 | bool allowClient = false, allowServer = false; | |
208 | ||
209 | critical = 0; | |
210 | for (i = 0; ; i++) { | |
211 | size = 0; | |
212 | status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, | |
213 | &size, NULL); | |
214 | ||
215 | if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { | |
216 | ||
217 | /* If there is no data at all, then we must allow | |
218 | client/server to pass */ | |
219 | if (i == 0) { | |
220 | allowServer = allowClient = true; | |
221 | } | |
222 | break; | |
223 | } | |
224 | if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { | |
225 | error_setg(errp, | |
226 | "Unable to query certificate %s key purpose: %s", | |
227 | certFile, gnutls_strerror(status)); | |
228 | return -1; | |
229 | } | |
230 | ||
231 | buffer = g_new0(char, size); | |
232 | ||
233 | status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, | |
234 | &size, &purposeCritical); | |
235 | ||
236 | if (status < 0) { | |
237 | trace_qcrypto_tls_creds_x509_check_key_purpose( | |
238 | creds, certFile, status, "<none>", purposeCritical); | |
239 | g_free(buffer); | |
240 | error_setg(errp, | |
241 | "Unable to query certificate %s key purpose: %s", | |
242 | certFile, gnutls_strerror(status)); | |
243 | return -1; | |
244 | } | |
245 | trace_qcrypto_tls_creds_x509_check_key_purpose( | |
246 | creds, certFile, status, buffer, purposeCritical); | |
247 | if (purposeCritical) { | |
248 | critical = true; | |
249 | } | |
250 | ||
251 | if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { | |
252 | allowServer = true; | |
253 | } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { | |
254 | allowClient = true; | |
255 | } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) { | |
256 | allowServer = allowClient = true; | |
257 | } | |
258 | ||
259 | g_free(buffer); | |
0e1d0245 | 260 | buffer = NULL; |
9a2fd434 DB |
261 | } |
262 | ||
263 | if (isServer) { | |
264 | if (!allowServer) { | |
265 | if (critical) { | |
266 | error_setg(errp, | |
267 | "Certificate %s purpose does not allow " | |
268 | "use with a TLS server", certFile); | |
269 | return -1; | |
270 | } | |
271 | } | |
272 | } else { | |
273 | if (!allowClient) { | |
274 | if (critical) { | |
275 | error_setg(errp, | |
276 | "Certificate %s purpose does not allow use " | |
277 | "with a TLS client", certFile); | |
278 | return -1; | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | ||
287 | static int | |
288 | qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, | |
289 | gnutls_x509_crt_t cert, | |
290 | const char *certFile, | |
291 | bool isServer, | |
292 | bool isCA, | |
293 | Error **errp) | |
294 | { | |
295 | if (qcrypto_tls_creds_check_cert_times(cert, certFile, | |
296 | isServer, isCA, | |
297 | errp) < 0) { | |
298 | return -1; | |
299 | } | |
300 | ||
301 | #if LIBGNUTLS_VERSION_NUMBER >= 2 | |
302 | if (qcrypto_tls_creds_check_cert_basic_constraints(creds, | |
303 | cert, certFile, | |
304 | isServer, isCA, | |
305 | errp) < 0) { | |
306 | return -1; | |
307 | } | |
308 | #endif | |
309 | ||
310 | if (qcrypto_tls_creds_check_cert_key_usage(creds, | |
311 | cert, certFile, | |
312 | isCA, errp) < 0) { | |
313 | return -1; | |
314 | } | |
315 | ||
316 | if (!isCA && | |
317 | qcrypto_tls_creds_check_cert_key_purpose(creds, | |
318 | cert, certFile, | |
319 | isServer, errp) < 0) { | |
320 | return -1; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | ||
327 | static int | |
328 | qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert, | |
329 | const char *certFile, | |
330 | gnutls_x509_crt_t *cacerts, | |
331 | size_t ncacerts, | |
332 | const char *cacertFile, | |
333 | bool isServer, | |
334 | Error **errp) | |
335 | { | |
336 | unsigned int status; | |
337 | ||
338 | if (gnutls_x509_crt_list_verify(&cert, 1, | |
339 | cacerts, ncacerts, | |
340 | NULL, 0, | |
341 | 0, &status) < 0) { | |
342 | error_setg(errp, isServer ? | |
343 | "Unable to verify server certificate %s against " | |
344 | "CA certificate %s" : | |
345 | "Unable to verify client certificate %s against " | |
346 | "CA certificate %s", | |
347 | certFile, cacertFile); | |
348 | return -1; | |
349 | } | |
350 | ||
351 | if (status != 0) { | |
352 | const char *reason = "Invalid certificate"; | |
353 | ||
354 | if (status & GNUTLS_CERT_INVALID) { | |
355 | reason = "The certificate is not trusted"; | |
356 | } | |
357 | ||
358 | if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { | |
359 | reason = "The certificate hasn't got a known issuer"; | |
360 | } | |
361 | ||
362 | if (status & GNUTLS_CERT_REVOKED) { | |
363 | reason = "The certificate has been revoked"; | |
364 | } | |
365 | ||
366 | #ifndef GNUTLS_1_0_COMPAT | |
367 | if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { | |
368 | reason = "The certificate uses an insecure algorithm"; | |
369 | } | |
370 | #endif | |
371 | ||
372 | error_setg(errp, | |
373 | "Our own certificate %s failed validation against %s: %s", | |
374 | certFile, cacertFile, reason); | |
375 | return -1; | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | ||
382 | static gnutls_x509_crt_t | |
383 | qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, | |
384 | const char *certFile, | |
385 | bool isServer, | |
386 | Error **errp) | |
387 | { | |
388 | gnutls_datum_t data; | |
389 | gnutls_x509_crt_t cert = NULL; | |
390 | char *buf = NULL; | |
391 | gsize buflen; | |
392 | GError *gerr; | |
393 | int ret = -1; | |
394 | ||
395 | trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile); | |
396 | ||
397 | if (gnutls_x509_crt_init(&cert) < 0) { | |
398 | error_setg(errp, "Unable to initialize certificate"); | |
399 | goto cleanup; | |
400 | } | |
401 | ||
402 | if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { | |
403 | error_setg(errp, "Cannot load CA cert list %s: %s", | |
404 | certFile, gerr->message); | |
405 | g_error_free(gerr); | |
406 | goto cleanup; | |
407 | } | |
408 | ||
409 | data.data = (unsigned char *)buf; | |
410 | data.size = strlen(buf); | |
411 | ||
412 | if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { | |
413 | error_setg(errp, isServer ? | |
414 | "Unable to import server certificate %s" : | |
415 | "Unable to import client certificate %s", | |
416 | certFile); | |
417 | goto cleanup; | |
418 | } | |
419 | ||
420 | ret = 0; | |
421 | ||
422 | cleanup: | |
423 | if (ret != 0) { | |
424 | gnutls_x509_crt_deinit(cert); | |
425 | cert = NULL; | |
426 | } | |
427 | g_free(buf); | |
428 | return cert; | |
429 | } | |
430 | ||
431 | ||
432 | static int | |
433 | qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, | |
434 | const char *certFile, | |
435 | gnutls_x509_crt_t *certs, | |
436 | unsigned int certMax, | |
437 | size_t *ncerts, | |
438 | Error **errp) | |
439 | { | |
440 | gnutls_datum_t data; | |
441 | char *buf = NULL; | |
442 | gsize buflen; | |
443 | int ret = -1; | |
444 | GError *gerr = NULL; | |
445 | ||
446 | *ncerts = 0; | |
447 | trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile); | |
448 | ||
449 | if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { | |
450 | error_setg(errp, "Cannot load CA cert list %s: %s", | |
451 | certFile, gerr->message); | |
452 | g_error_free(gerr); | |
453 | goto cleanup; | |
454 | } | |
455 | ||
456 | data.data = (unsigned char *)buf; | |
457 | data.size = strlen(buf); | |
458 | ||
459 | if (gnutls_x509_crt_list_import(certs, &certMax, &data, | |
460 | GNUTLS_X509_FMT_PEM, 0) < 0) { | |
461 | error_setg(errp, | |
462 | "Unable to import CA certificate list %s", | |
463 | certFile); | |
464 | goto cleanup; | |
465 | } | |
466 | *ncerts = certMax; | |
467 | ||
468 | ret = 0; | |
469 | ||
470 | cleanup: | |
471 | g_free(buf); | |
472 | return ret; | |
473 | } | |
474 | ||
475 | ||
476 | #define MAX_CERTS 16 | |
477 | static int | |
478 | qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, | |
479 | bool isServer, | |
480 | const char *cacertFile, | |
481 | const char *certFile, | |
482 | Error **errp) | |
483 | { | |
484 | gnutls_x509_crt_t cert = NULL; | |
485 | gnutls_x509_crt_t cacerts[MAX_CERTS]; | |
486 | size_t ncacerts = 0; | |
487 | size_t i; | |
488 | int ret = -1; | |
489 | ||
490 | memset(cacerts, 0, sizeof(cacerts)); | |
08cb175a DB |
491 | if (certFile && |
492 | access(certFile, R_OK) == 0) { | |
9a2fd434 DB |
493 | cert = qcrypto_tls_creds_load_cert(creds, |
494 | certFile, isServer, | |
495 | errp); | |
496 | if (!cert) { | |
497 | goto cleanup; | |
498 | } | |
499 | } | |
500 | if (access(cacertFile, R_OK) == 0) { | |
501 | if (qcrypto_tls_creds_load_ca_cert_list(creds, | |
502 | cacertFile, cacerts, | |
503 | MAX_CERTS, &ncacerts, | |
504 | errp) < 0) { | |
505 | goto cleanup; | |
506 | } | |
507 | } | |
508 | ||
509 | if (cert && | |
510 | qcrypto_tls_creds_check_cert(creds, | |
511 | cert, certFile, isServer, | |
512 | false, errp) < 0) { | |
513 | goto cleanup; | |
514 | } | |
515 | ||
516 | for (i = 0; i < ncacerts; i++) { | |
517 | if (qcrypto_tls_creds_check_cert(creds, | |
518 | cacerts[i], cacertFile, | |
519 | isServer, true, errp) < 0) { | |
520 | goto cleanup; | |
521 | } | |
522 | } | |
523 | ||
524 | if (cert && ncacerts && | |
525 | qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts, | |
526 | ncacerts, cacertFile, | |
527 | isServer, errp) < 0) { | |
528 | goto cleanup; | |
529 | } | |
530 | ||
531 | ret = 0; | |
532 | ||
533 | cleanup: | |
534 | if (cert) { | |
535 | gnutls_x509_crt_deinit(cert); | |
536 | } | |
537 | for (i = 0; i < ncacerts; i++) { | |
538 | gnutls_x509_crt_deinit(cacerts[i]); | |
539 | } | |
540 | return ret; | |
541 | } | |
542 | ||
85bcbc78 DB |
543 | |
544 | static int | |
545 | qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, | |
546 | Error **errp) | |
547 | { | |
548 | char *cacert = NULL, *cacrl = NULL, *cert = NULL, | |
549 | *key = NULL, *dhparams = NULL; | |
550 | int ret; | |
551 | int rv = -1; | |
552 | ||
553 | trace_qcrypto_tls_creds_x509_load(creds, | |
554 | creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); | |
555 | ||
556 | if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { | |
557 | if (qcrypto_tls_creds_get_path(&creds->parent_obj, | |
558 | QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
559 | true, &cacert, errp) < 0 || | |
560 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
561 | QCRYPTO_TLS_CREDS_X509_CA_CRL, | |
562 | false, &cacrl, errp) < 0 || | |
563 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
564 | QCRYPTO_TLS_CREDS_X509_SERVER_CERT, | |
565 | true, &cert, errp) < 0 || | |
566 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
567 | QCRYPTO_TLS_CREDS_X509_SERVER_KEY, | |
568 | true, &key, errp) < 0 || | |
569 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
570 | QCRYPTO_TLS_CREDS_DH_PARAMS, | |
571 | false, &dhparams, errp) < 0) { | |
572 | goto cleanup; | |
573 | } | |
574 | } else { | |
575 | if (qcrypto_tls_creds_get_path(&creds->parent_obj, | |
576 | QCRYPTO_TLS_CREDS_X509_CA_CERT, | |
577 | true, &cacert, errp) < 0 || | |
578 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
579 | QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, | |
580 | false, &cert, errp) < 0 || | |
581 | qcrypto_tls_creds_get_path(&creds->parent_obj, | |
582 | QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, | |
583 | false, &key, errp) < 0) { | |
584 | goto cleanup; | |
585 | } | |
586 | } | |
587 | ||
9a2fd434 DB |
588 | if (creds->sanityCheck && |
589 | qcrypto_tls_creds_x509_sanity_check(creds, | |
590 | creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, | |
591 | cacert, cert, errp) < 0) { | |
592 | goto cleanup; | |
593 | } | |
594 | ||
85bcbc78 DB |
595 | ret = gnutls_certificate_allocate_credentials(&creds->data); |
596 | if (ret < 0) { | |
597 | error_setg(errp, "Cannot allocate credentials: '%s'", | |
598 | gnutls_strerror(ret)); | |
599 | goto cleanup; | |
600 | } | |
601 | ||
602 | ret = gnutls_certificate_set_x509_trust_file(creds->data, | |
603 | cacert, | |
604 | GNUTLS_X509_FMT_PEM); | |
605 | if (ret < 0) { | |
606 | error_setg(errp, "Cannot load CA certificate '%s': %s", | |
607 | cacert, gnutls_strerror(ret)); | |
608 | goto cleanup; | |
609 | } | |
610 | ||
611 | if (cert != NULL && key != NULL) { | |
1d7b5b4a DB |
612 | #if GNUTLS_VERSION_NUMBER >= 0x030111 |
613 | char *password = NULL; | |
614 | if (creds->passwordid) { | |
615 | password = qcrypto_secret_lookup_as_utf8(creds->passwordid, | |
616 | errp); | |
617 | if (!password) { | |
618 | goto cleanup; | |
619 | } | |
620 | } | |
621 | ret = gnutls_certificate_set_x509_key_file2(creds->data, | |
622 | cert, key, | |
623 | GNUTLS_X509_FMT_PEM, | |
624 | password, | |
625 | 0); | |
626 | g_free(password); | |
627 | #else /* GNUTLS_VERSION_NUMBER < 0x030111 */ | |
628 | if (creds->passwordid) { | |
629 | error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11"); | |
630 | goto cleanup; | |
631 | } | |
85bcbc78 DB |
632 | ret = gnutls_certificate_set_x509_key_file(creds->data, |
633 | cert, key, | |
634 | GNUTLS_X509_FMT_PEM); | |
1d7b5b4a | 635 | #endif /* GNUTLS_VERSION_NUMBER < 0x030111 */ |
85bcbc78 DB |
636 | if (ret < 0) { |
637 | error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", | |
638 | cert, key, gnutls_strerror(ret)); | |
639 | goto cleanup; | |
640 | } | |
641 | } | |
642 | ||
643 | if (cacrl != NULL) { | |
644 | ret = gnutls_certificate_set_x509_crl_file(creds->data, | |
645 | cacrl, | |
646 | GNUTLS_X509_FMT_PEM); | |
647 | if (ret < 0) { | |
648 | error_setg(errp, "Cannot load CRL '%s': %s", | |
649 | cacrl, gnutls_strerror(ret)); | |
650 | goto cleanup; | |
651 | } | |
652 | } | |
653 | ||
654 | if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { | |
655 | if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, | |
656 | &creds->parent_obj.dh_params, | |
657 | errp) < 0) { | |
658 | goto cleanup; | |
659 | } | |
660 | gnutls_certificate_set_dh_params(creds->data, | |
661 | creds->parent_obj.dh_params); | |
662 | } | |
663 | ||
664 | rv = 0; | |
665 | cleanup: | |
666 | g_free(cacert); | |
667 | g_free(cacrl); | |
668 | g_free(cert); | |
669 | g_free(key); | |
670 | g_free(dhparams); | |
671 | return rv; | |
672 | } | |
673 | ||
674 | ||
675 | static void | |
676 | qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds) | |
677 | { | |
678 | if (creds->data) { | |
679 | gnutls_certificate_free_credentials(creds->data); | |
680 | creds->data = NULL; | |
681 | } | |
61b9251a DB |
682 | if (creds->parent_obj.dh_params) { |
683 | gnutls_dh_params_deinit(creds->parent_obj.dh_params); | |
684 | creds->parent_obj.dh_params = NULL; | |
685 | } | |
85bcbc78 DB |
686 | } |
687 | ||
688 | ||
689 | #else /* ! CONFIG_GNUTLS */ | |
690 | ||
691 | ||
692 | static void | |
693 | qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED, | |
694 | Error **errp) | |
695 | { | |
696 | error_setg(errp, "TLS credentials support requires GNUTLS"); | |
697 | } | |
698 | ||
699 | ||
700 | static void | |
701 | qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED) | |
702 | { | |
703 | /* nada */ | |
704 | } | |
705 | ||
706 | ||
707 | #endif /* ! CONFIG_GNUTLS */ | |
708 | ||
709 | ||
710 | static void | |
711 | qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, | |
712 | bool value, | |
713 | Error **errp) | |
714 | { | |
715 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
716 | ||
717 | if (value) { | |
718 | qcrypto_tls_creds_x509_load(creds, errp); | |
719 | } else { | |
720 | qcrypto_tls_creds_x509_unload(creds); | |
721 | } | |
722 | } | |
723 | ||
724 | ||
725 | #ifdef CONFIG_GNUTLS | |
726 | ||
727 | ||
728 | static bool | |
729 | qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, | |
730 | Error **errp G_GNUC_UNUSED) | |
731 | { | |
732 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
733 | ||
734 | return creds->data != NULL; | |
735 | } | |
736 | ||
737 | ||
738 | #else /* ! CONFIG_GNUTLS */ | |
739 | ||
740 | ||
741 | static bool | |
742 | qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, | |
743 | Error **errp G_GNUC_UNUSED) | |
744 | { | |
745 | return false; | |
746 | } | |
747 | ||
748 | ||
749 | #endif /* ! CONFIG_GNUTLS */ | |
750 | ||
751 | ||
9a2fd434 DB |
752 | static void |
753 | qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, | |
754 | bool value, | |
755 | Error **errp G_GNUC_UNUSED) | |
756 | { | |
757 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
758 | ||
759 | creds->sanityCheck = value; | |
760 | } | |
761 | ||
762 | ||
1d7b5b4a DB |
763 | static void |
764 | qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj, | |
765 | const char *value, | |
766 | Error **errp G_GNUC_UNUSED) | |
767 | { | |
768 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
769 | ||
770 | creds->passwordid = g_strdup(value); | |
771 | } | |
772 | ||
773 | ||
774 | static char * | |
775 | qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj, | |
776 | Error **errp G_GNUC_UNUSED) | |
777 | { | |
778 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
779 | ||
780 | return g_strdup(creds->passwordid); | |
781 | } | |
782 | ||
783 | ||
9a2fd434 DB |
784 | static bool |
785 | qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, | |
786 | Error **errp G_GNUC_UNUSED) | |
787 | { | |
788 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
789 | ||
790 | return creds->sanityCheck; | |
791 | } | |
792 | ||
793 | ||
85bcbc78 DB |
794 | static void |
795 | qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) | |
796 | { | |
797 | object_property_set_bool(OBJECT(uc), true, "loaded", errp); | |
798 | } | |
799 | ||
800 | ||
801 | static void | |
802 | qcrypto_tls_creds_x509_init(Object *obj) | |
803 | { | |
9a2fd434 DB |
804 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); |
805 | ||
806 | creds->sanityCheck = true; | |
85bcbc78 DB |
807 | } |
808 | ||
809 | ||
810 | static void | |
811 | qcrypto_tls_creds_x509_finalize(Object *obj) | |
812 | { | |
813 | QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); | |
814 | ||
1d7b5b4a | 815 | g_free(creds->passwordid); |
85bcbc78 DB |
816 | qcrypto_tls_creds_x509_unload(creds); |
817 | } | |
818 | ||
819 | ||
820 | static void | |
821 | qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) | |
822 | { | |
823 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | |
824 | ||
825 | ucc->complete = qcrypto_tls_creds_x509_complete; | |
9884abee DB |
826 | |
827 | object_class_property_add_bool(oc, "loaded", | |
828 | qcrypto_tls_creds_x509_prop_get_loaded, | |
829 | qcrypto_tls_creds_x509_prop_set_loaded, | |
830 | NULL); | |
831 | object_class_property_add_bool(oc, "sanity-check", | |
832 | qcrypto_tls_creds_x509_prop_get_sanity, | |
833 | qcrypto_tls_creds_x509_prop_set_sanity, | |
834 | NULL); | |
835 | object_class_property_add_str(oc, "passwordid", | |
836 | qcrypto_tls_creds_x509_prop_get_passwordid, | |
837 | qcrypto_tls_creds_x509_prop_set_passwordid, | |
838 | NULL); | |
85bcbc78 DB |
839 | } |
840 | ||
841 | ||
842 | static const TypeInfo qcrypto_tls_creds_x509_info = { | |
843 | .parent = TYPE_QCRYPTO_TLS_CREDS, | |
844 | .name = TYPE_QCRYPTO_TLS_CREDS_X509, | |
845 | .instance_size = sizeof(QCryptoTLSCredsX509), | |
846 | .instance_init = qcrypto_tls_creds_x509_init, | |
847 | .instance_finalize = qcrypto_tls_creds_x509_finalize, | |
848 | .class_size = sizeof(QCryptoTLSCredsX509Class), | |
849 | .class_init = qcrypto_tls_creds_x509_class_init, | |
850 | .interfaces = (InterfaceInfo[]) { | |
851 | { TYPE_USER_CREATABLE }, | |
852 | { } | |
853 | } | |
854 | }; | |
855 | ||
856 | ||
857 | static void | |
858 | qcrypto_tls_creds_x509_register_types(void) | |
859 | { | |
860 | type_register_static(&qcrypto_tls_creds_x509_info); | |
861 | } | |
862 | ||
863 | ||
864 | type_init(qcrypto_tls_creds_x509_register_types); |