2 * Copyright (C) 2015 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
18 * Author: Daniel P. Berrange <berrange@redhat.com>
21 #include "qemu/osdep.h"
23 #include "crypto-tls-x509-helpers.h"
24 #include "crypto/tlscredsx509.h"
25 #include "crypto/tlssession.h"
26 #include "qom/object_interfaces.h"
27 #include "qemu/sockets.h"
30 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
32 #define WORKDIR "tests/test-crypto-tlssession-work/"
33 #define KEYFILE WORKDIR "key-ctx.pem"
35 struct QCryptoTLSSessionTestData
{
36 const char *servercacrt
;
37 const char *clientcacrt
;
38 const char *servercrt
;
39 const char *clientcrt
;
40 bool expectServerFail
;
41 bool expectClientFail
;
43 const char *const *wildcards
;
47 static ssize_t
testWrite(const char *buf
, size_t len
, void *opaque
)
51 return write(*fd
, buf
, len
);
54 static ssize_t
testRead(char *buf
, size_t len
, void *opaque
)
58 return read(*fd
, buf
, len
);
61 static QCryptoTLSCreds
*test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint
,
66 Object
*parent
= object_get_objects_root();
67 Object
*creds
= object_new_with_props(
68 TYPE_QCRYPTO_TLS_CREDS_X509
,
70 (endpoint
== QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
?
71 "testtlscredsserver" : "testtlscredsclient"),
73 "endpoint", (endpoint
== QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
?
77 /* We skip initial sanity checks here because we
78 * want to make sure that problems are being
79 * detected at the TLS session validation stage,
80 * and the test-crypto-tlscreds test already
81 * validate the sanity check code.
88 error_propagate(errp
, err
);
91 return QCRYPTO_TLS_CREDS(creds
);
96 * This tests validation checking of peer certificates
98 * This is replicating the checks that are done for an
99 * active TLS session after handshake completes. To
100 * simulate that we create our TLS contexts, skipping
101 * sanity checks. We then get a socketpair, and
102 * initiate a TLS session across them. Finally do
103 * do actual cert validation tests
105 static void test_crypto_tls_session(const void *opaque
)
107 struct QCryptoTLSSessionTestData
*data
=
108 (struct QCryptoTLSSessionTestData
*)opaque
;
109 QCryptoTLSCreds
*clientCreds
;
110 QCryptoTLSCreds
*serverCreds
;
111 QCryptoTLSSession
*clientSess
= NULL
;
112 QCryptoTLSSession
*serverSess
= NULL
;
114 const char * const *wildcards
;
116 bool clientShake
= false;
117 bool serverShake
= false;
121 /* We'll use this for our fake client-server connection */
122 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, channel
);
126 * We have an evil loop to do the handshake in a single
127 * thread, so we need these non-blocking to avoid deadlock
130 qemu_set_nonblock(channel
[0]);
131 qemu_set_nonblock(channel
[1]);
133 #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/"
134 #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/"
135 mkdir(CLIENT_CERT_DIR
, 0700);
136 mkdir(SERVER_CERT_DIR
, 0700);
138 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
);
139 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT
);
140 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY
);
142 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
);
143 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT
);
144 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY
);
146 g_assert(link(data
->servercacrt
,
147 SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
) == 0);
148 g_assert(link(data
->servercrt
,
149 SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT
) == 0);
150 g_assert(link(KEYFILE
,
151 SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY
) == 0);
153 g_assert(link(data
->clientcacrt
,
154 CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
) == 0);
155 g_assert(link(data
->clientcrt
,
156 CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT
) == 0);
157 g_assert(link(KEYFILE
,
158 CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY
) == 0);
160 clientCreds
= test_tls_creds_create(
161 QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT
,
164 g_assert(clientCreds
!= NULL
);
166 serverCreds
= test_tls_creds_create(
167 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
,
170 g_assert(serverCreds
!= NULL
);
172 acl
= qemu_acl_init("tlssessionacl");
174 wildcards
= data
->wildcards
;
175 while (wildcards
&& *wildcards
) {
176 qemu_acl_append(acl
, 0, *wildcards
);
180 /* Now the real part of the test, setup the sessions */
181 clientSess
= qcrypto_tls_session_new(
182 clientCreds
, data
->hostname
, NULL
,
183 QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT
, &err
);
184 serverSess
= qcrypto_tls_session_new(
186 data
->wildcards
? "tlssessionacl" : NULL
,
187 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
, &err
);
189 g_assert(clientSess
!= NULL
);
190 g_assert(serverSess
!= NULL
);
192 /* For handshake to work, we need to set the I/O callbacks
193 * to read/write over the socketpair
195 qcrypto_tls_session_set_callbacks(serverSess
,
198 qcrypto_tls_session_set_callbacks(clientSess
,
203 * Finally we loop around & around doing handshake on each
204 * session until we get an error, or the handshake completes.
205 * This relies on the socketpair being nonblocking to avoid
206 * deadlocking ourselves upon handshake
211 rv
= qcrypto_tls_session_handshake(serverSess
,
214 if (qcrypto_tls_session_get_handshake_status(serverSess
) ==
215 QCRYPTO_TLS_HANDSHAKE_COMPLETE
) {
220 rv
= qcrypto_tls_session_handshake(clientSess
,
223 if (qcrypto_tls_session_get_handshake_status(clientSess
) ==
224 QCRYPTO_TLS_HANDSHAKE_COMPLETE
) {
228 } while (!clientShake
&& !serverShake
);
231 /* Finally make sure the server validation does what
234 if (qcrypto_tls_session_check_credentials(serverSess
, &err
) < 0) {
235 g_assert(data
->expectServerFail
);
239 g_assert(!data
->expectServerFail
);
243 * And the same for the client validation check
245 if (qcrypto_tls_session_check_credentials(clientSess
, &err
) < 0) {
246 g_assert(data
->expectClientFail
);
250 g_assert(!data
->expectClientFail
);
253 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
);
254 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT
);
255 unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY
);
257 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT
);
258 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT
);
259 unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY
);
261 rmdir(CLIENT_CERT_DIR
);
262 rmdir(SERVER_CERT_DIR
);
264 object_unparent(OBJECT(serverCreds
));
265 object_unparent(OBJECT(clientCreds
));
267 qcrypto_tls_session_free(serverSess
);
268 qcrypto_tls_session_free(clientSess
);
275 int main(int argc
, char **argv
)
279 module_call_init(MODULE_INIT_QOM
);
280 g_test_init(&argc
, &argv
, NULL
);
281 setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
283 mkdir(WORKDIR
, 0700);
285 test_tls_init(KEYFILE
);
287 # define TEST_SESS_REG(name, caCrt, \
288 serverCrt, clientCrt, \
289 expectServerFail, expectClientFail, \
290 hostname, wildcards) \
291 struct QCryptoTLSSessionTestData name = { \
292 caCrt, caCrt, serverCrt, clientCrt, \
293 expectServerFail, expectClientFail, \
294 hostname, wildcards \
296 g_test_add_data_func("/qcrypto/tlssession/" # name, \
297 &name, test_crypto_tls_session); \
300 # define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \
301 serverCrt, clientCrt, \
302 expectServerFail, expectClientFail, \
303 hostname, wildcards) \
304 struct QCryptoTLSSessionTestData name = { \
305 serverCaCrt, clientCaCrt, serverCrt, clientCrt, \
306 expectServerFail, expectClientFail, \
307 hostname, wildcards \
309 g_test_add_data_func("/qcrypto/tlssession/" # name, \
310 &name, test_crypto_tls_session); \
312 /* A perfect CA, perfect client & perfect server */
314 /* Basic:CA:critical */
315 TLS_ROOT_REQ(cacertreq
,
316 "UK", "qemu CA", NULL
, NULL
, NULL
, NULL
,
318 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
319 false, false, NULL
, NULL
,
322 TLS_ROOT_REQ(altcacertreq
,
323 "UK", "qemu CA 1", NULL
, NULL
, NULL
, NULL
,
326 false, false, NULL
, NULL
,
329 TLS_CERT_REQ(servercertreq
, cacertreq
,
330 "UK", "qemu.org", NULL
, NULL
, NULL
, NULL
,
333 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
334 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
336 TLS_CERT_REQ(clientcertreq
, cacertreq
,
337 "UK", "qemu", NULL
, NULL
, NULL
, NULL
,
340 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
341 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
344 TLS_CERT_REQ(clientcertaltreq
, altcacertreq
,
345 "UK", "qemu", NULL
, NULL
, NULL
, NULL
,
348 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
349 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
352 TEST_SESS_REG(basicca
, cacertreq
.filename
,
353 servercertreq
.filename
, clientcertreq
.filename
,
354 false, false, "qemu.org", NULL
);
355 TEST_SESS_REG_EXT(differentca
, cacertreq
.filename
,
356 altcacertreq
.filename
, servercertreq
.filename
,
357 clientcertaltreq
.filename
, true, true, "qemu.org", NULL
);
360 /* When an altname is set, the CN is ignored, so it must be duplicated
361 * as an altname for it to match */
362 TLS_CERT_REQ(servercertalt1req
, cacertreq
,
363 "UK", "qemu.org", "www.qemu.org", "qemu.org",
364 "192.168.122.1", "fec0::dead:beaf",
367 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
368 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
370 /* This intentionally doesn't replicate */
371 TLS_CERT_REQ(servercertalt2req
, cacertreq
,
372 "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org",
373 "192.168.122.1", "fec0::dead:beaf",
376 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
377 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
380 TEST_SESS_REG(altname1
, cacertreq
.filename
,
381 servercertalt1req
.filename
, clientcertreq
.filename
,
382 false, false, "qemu.org", NULL
);
383 TEST_SESS_REG(altname2
, cacertreq
.filename
,
384 servercertalt1req
.filename
, clientcertreq
.filename
,
385 false, false, "www.qemu.org", NULL
);
386 TEST_SESS_REG(altname3
, cacertreq
.filename
,
387 servercertalt1req
.filename
, clientcertreq
.filename
,
388 false, true, "wiki.qemu.org", NULL
);
390 TEST_SESS_REG(altname4
, cacertreq
.filename
,
391 servercertalt2req
.filename
, clientcertreq
.filename
,
392 false, true, "qemu.org", NULL
);
393 TEST_SESS_REG(altname5
, cacertreq
.filename
,
394 servercertalt2req
.filename
, clientcertreq
.filename
,
395 false, false, "www.qemu.org", NULL
);
396 TEST_SESS_REG(altname6
, cacertreq
.filename
,
397 servercertalt2req
.filename
, clientcertreq
.filename
,
398 false, false, "wiki.qemu.org", NULL
);
400 const char *const wildcards1
[] = {
404 const char *const wildcards2
[] = {
408 const char *const wildcards3
[] = {
413 const char *const wildcards4
[] = {
417 const char *const wildcards5
[] = {
421 const char *const wildcards6
[] = {
426 TEST_SESS_REG(wildcard1
, cacertreq
.filename
,
427 servercertreq
.filename
, clientcertreq
.filename
,
428 true, false, "qemu.org", wildcards1
);
429 TEST_SESS_REG(wildcard2
, cacertreq
.filename
,
430 servercertreq
.filename
, clientcertreq
.filename
,
431 false, false, "qemu.org", wildcards2
);
432 TEST_SESS_REG(wildcard3
, cacertreq
.filename
,
433 servercertreq
.filename
, clientcertreq
.filename
,
434 false, false, "qemu.org", wildcards3
);
435 TEST_SESS_REG(wildcard4
, cacertreq
.filename
,
436 servercertreq
.filename
, clientcertreq
.filename
,
437 true, false, "qemu.org", wildcards4
);
438 TEST_SESS_REG(wildcard5
, cacertreq
.filename
,
439 servercertreq
.filename
, clientcertreq
.filename
,
440 false, false, "qemu.org", wildcards5
);
441 TEST_SESS_REG(wildcard6
, cacertreq
.filename
,
442 servercertreq
.filename
, clientcertreq
.filename
,
443 false, false, "qemu.org", wildcards6
);
445 TLS_ROOT_REQ(cacertrootreq
,
446 "UK", "qemu root", NULL
, NULL
, NULL
, NULL
,
448 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
449 false, false, NULL
, NULL
,
451 TLS_CERT_REQ(cacertlevel1areq
, cacertrootreq
,
452 "UK", "qemu level 1a", NULL
, NULL
, NULL
, NULL
,
454 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
455 false, false, NULL
, NULL
,
457 TLS_CERT_REQ(cacertlevel1breq
, cacertrootreq
,
458 "UK", "qemu level 1b", NULL
, NULL
, NULL
, NULL
,
460 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
461 false, false, NULL
, NULL
,
463 TLS_CERT_REQ(cacertlevel2areq
, cacertlevel1areq
,
464 "UK", "qemu level 2a", NULL
, NULL
, NULL
, NULL
,
466 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
467 false, false, NULL
, NULL
,
469 TLS_CERT_REQ(servercertlevel3areq
, cacertlevel2areq
,
470 "UK", "qemu.org", NULL
, NULL
, NULL
, NULL
,
473 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
474 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
476 TLS_CERT_REQ(clientcertlevel2breq
, cacertlevel1breq
,
477 "UK", "qemu client level 2b", NULL
, NULL
, NULL
, NULL
,
480 GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
481 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
484 gnutls_x509_crt_t certchain
[] = {
486 cacertlevel1areq
.crt
,
487 cacertlevel1breq
.crt
,
488 cacertlevel2areq
.crt
,
491 test_tls_write_cert_chain(WORKDIR
"cacertchain-sess.pem",
493 G_N_ELEMENTS(certchain
));
495 TEST_SESS_REG(cachain
, WORKDIR
"cacertchain-sess.pem",
496 servercertlevel3areq
.filename
, clientcertlevel2breq
.filename
,
497 false, false, "qemu.org", NULL
);
501 test_tls_discard_cert(&clientcertreq
);
502 test_tls_discard_cert(&clientcertaltreq
);
504 test_tls_discard_cert(&servercertreq
);
505 test_tls_discard_cert(&servercertalt1req
);
506 test_tls_discard_cert(&servercertalt2req
);
508 test_tls_discard_cert(&cacertreq
);
509 test_tls_discard_cert(&altcacertreq
);
511 test_tls_discard_cert(&cacertrootreq
);
512 test_tls_discard_cert(&cacertlevel1areq
);
513 test_tls_discard_cert(&cacertlevel1breq
);
514 test_tls_discard_cert(&cacertlevel2areq
);
515 test_tls_discard_cert(&servercertlevel3areq
);
516 test_tls_discard_cert(&clientcertlevel2breq
);
517 unlink(WORKDIR
"cacertchain-sess.pem");
519 test_tls_cleanup(KEYFILE
);
522 return ret
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
525 #else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
533 #endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */