1 /* 2 * Copyright (C) 2015 Red Hat, Inc. 3 * 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. 8 * 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. 13 * 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/>. 17 * 18 * Author: Daniel P. Berrange <berrange@redhat.com> 19 */ 20 21 #include "qemu/osdep.h" 22 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" 28 #include "qemu/acl.h" 29 30 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT 31 32 #define WORKDIR "tests/test-crypto-tlssession-work/" 33 #define KEYFILE WORKDIR "key-ctx.pem" 34 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; 42 const char *hostname; 43 const char *const *wildcards; 44 }; 45 46 47 static ssize_t testWrite(const char *buf, size_t len, void *opaque) 48 { 49 int *fd = opaque; 50 51 return write(*fd, buf, len); 52 } 53 54 static ssize_t testRead(char *buf, size_t len, void *opaque) 55 { 56 int *fd = opaque; 57 58 return read(*fd, buf, len); 59 } 60 61 static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, 62 const char *certdir, 63 Error **errp) 64 { 65 Error *err = NULL; 66 Object *parent = object_get_objects_root(); 67 Object *creds = object_new_with_props( 68 TYPE_QCRYPTO_TLS_CREDS_X509, 69 parent, 70 (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? 71 "testtlscredsserver" : "testtlscredsclient"), 72 &err, 73 "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? 74 "server" : "client"), 75 "dir", certdir, 76 "verify-peer", "yes", 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. 82 */ 83 "sanity-check", "no", 84 NULL 85 ); 86 87 if (err) { 88 error_propagate(errp, err); 89 return NULL; 90 } 91 return QCRYPTO_TLS_CREDS(creds); 92 } 93 94 95 /* 96 * This tests validation checking of peer certificates 97 * 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 104 */ 105 static void test_crypto_tls_session(const void *opaque) 106 { 107 struct QCryptoTLSSessionTestData *data = 108 (struct QCryptoTLSSessionTestData *)opaque; 109 QCryptoTLSCreds *clientCreds; 110 QCryptoTLSCreds *serverCreds; 111 QCryptoTLSSession *clientSess = NULL; 112 QCryptoTLSSession *serverSess = NULL; 113 qemu_acl *acl; 114 const char * const *wildcards; 115 int channel[2]; 116 bool clientShake = false; 117 bool serverShake = false; 118 Error *err = NULL; 119 int ret; 120 121 /* We'll use this for our fake client-server connection */ 122 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); 123 g_assert(ret == 0); 124 125 /* 126 * We have an evil loop to do the handshake in a single 127 * thread, so we need these non-blocking to avoid deadlock 128 * of ourselves 129 */ 130 qemu_set_nonblock(channel[0]); 131 qemu_set_nonblock(channel[1]); 132 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); 137 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); 141 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); 145 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); 152 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); 159 160 clientCreds = test_tls_creds_create( 161 QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, 162 CLIENT_CERT_DIR, 163 &err); 164 g_assert(clientCreds != NULL); 165 166 serverCreds = test_tls_creds_create( 167 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, 168 SERVER_CERT_DIR, 169 &err); 170 g_assert(serverCreds != NULL); 171 172 acl = qemu_acl_init("tlssessionacl"); 173 qemu_acl_reset(acl); 174 wildcards = data->wildcards; 175 while (wildcards && *wildcards) { 176 qemu_acl_append(acl, 0, *wildcards); 177 wildcards++; 178 } 179 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( 185 serverCreds, NULL, 186 data->wildcards ? "tlssessionacl" : NULL, 187 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); 188 189 g_assert(clientSess != NULL); 190 g_assert(serverSess != NULL); 191 192 /* For handshake to work, we need to set the I/O callbacks 193 * to read/write over the socketpair 194 */ 195 qcrypto_tls_session_set_callbacks(serverSess, 196 testWrite, testRead, 197 &channel[0]); 198 qcrypto_tls_session_set_callbacks(clientSess, 199 testWrite, testRead, 200 &channel[1]); 201 202 /* 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 207 */ 208 do { 209 int rv; 210 if (!serverShake) { 211 rv = qcrypto_tls_session_handshake(serverSess, 212 &err); 213 g_assert(rv >= 0); 214 if (qcrypto_tls_session_get_handshake_status(serverSess) == 215 QCRYPTO_TLS_HANDSHAKE_COMPLETE) { 216 serverShake = true; 217 } 218 } 219 if (!clientShake) { 220 rv = qcrypto_tls_session_handshake(clientSess, 221 &err); 222 g_assert(rv >= 0); 223 if (qcrypto_tls_session_get_handshake_status(clientSess) == 224 QCRYPTO_TLS_HANDSHAKE_COMPLETE) { 225 clientShake = true; 226 } 227 } 228 } while (!clientShake && !serverShake); 229 230 231 /* Finally make sure the server validation does what 232 * we were expecting 233 */ 234 if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { 235 g_assert(data->expectServerFail); 236 error_free(err); 237 err = NULL; 238 } else { 239 g_assert(!data->expectServerFail); 240 } 241 242 /* 243 * And the same for the client validation check 244 */ 245 if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { 246 g_assert(data->expectClientFail); 247 error_free(err); 248 err = NULL; 249 } else { 250 g_assert(!data->expectClientFail); 251 } 252 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); 256 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); 260 261 rmdir(CLIENT_CERT_DIR); 262 rmdir(SERVER_CERT_DIR); 263 264 object_unparent(OBJECT(serverCreds)); 265 object_unparent(OBJECT(clientCreds)); 266 267 qcrypto_tls_session_free(serverSess); 268 qcrypto_tls_session_free(clientSess); 269 270 close(channel[0]); 271 close(channel[1]); 272 } 273 274 275 int main(int argc, char **argv) 276 { 277 int ret; 278 279 module_call_init(MODULE_INIT_QOM); 280 g_test_init(&argc, &argv, NULL); 281 setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); 282 283 mkdir(WORKDIR, 0700); 284 285 test_tls_init(KEYFILE); 286 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 \ 295 }; \ 296 g_test_add_data_func("/qcrypto/tlssession/" # name, \ 297 &name, test_crypto_tls_session); \ 298 299 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 \ 308 }; \ 309 g_test_add_data_func("/qcrypto/tlssession/" # name, \ 310 &name, test_crypto_tls_session); \ 311 312 /* A perfect CA, perfect client & perfect server */ 313 314 /* Basic:CA:critical */ 315 TLS_ROOT_REQ(cacertreq, 316 "UK", "qemu CA", NULL, NULL, NULL, NULL, 317 true, true, true, 318 true, true, GNUTLS_KEY_KEY_CERT_SIGN, 319 false, false, NULL, NULL, 320 0, 0); 321 322 TLS_ROOT_REQ(altcacertreq, 323 "UK", "qemu CA 1", NULL, NULL, NULL, NULL, 324 true, true, true, 325 false, false, 0, 326 false, false, NULL, NULL, 327 0, 0); 328 329 TLS_CERT_REQ(servercertreq, cacertreq, 330 "UK", "qemu.org", NULL, NULL, NULL, NULL, 331 true, true, false, 332 true, true, 333 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 334 true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 335 0, 0); 336 TLS_CERT_REQ(clientcertreq, cacertreq, 337 "UK", "qemu", NULL, NULL, NULL, NULL, 338 true, true, false, 339 true, true, 340 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 341 true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 342 0, 0); 343 344 TLS_CERT_REQ(clientcertaltreq, altcacertreq, 345 "UK", "qemu", NULL, NULL, NULL, NULL, 346 true, true, false, 347 true, true, 348 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 349 true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 350 0, 0); 351 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); 358 359 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", 365 true, true, false, 366 true, true, 367 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 368 true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 369 0, 0); 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", 374 true, true, false, 375 true, true, 376 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 377 true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 378 0, 0); 379 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); 389 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); 399 400 const char *const wildcards1[] = { 401 "C=UK,CN=dogfood", 402 NULL, 403 }; 404 const char *const wildcards2[] = { 405 "C=UK,CN=qemu", 406 NULL, 407 }; 408 const char *const wildcards3[] = { 409 "C=UK,CN=dogfood", 410 "C=UK,CN=qemu", 411 NULL, 412 }; 413 const char *const wildcards4[] = { 414 "C=UK,CN=qemustuff", 415 NULL, 416 }; 417 const char *const wildcards5[] = { 418 "C=UK,CN=qemu*", 419 NULL, 420 }; 421 const char *const wildcards6[] = { 422 "C=UK,CN=*emu*", 423 NULL, 424 }; 425 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); 444 445 TLS_ROOT_REQ(cacertrootreq, 446 "UK", "qemu root", NULL, NULL, NULL, NULL, 447 true, true, true, 448 true, true, GNUTLS_KEY_KEY_CERT_SIGN, 449 false, false, NULL, NULL, 450 0, 0); 451 TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, 452 "UK", "qemu level 1a", NULL, NULL, NULL, NULL, 453 true, true, true, 454 true, true, GNUTLS_KEY_KEY_CERT_SIGN, 455 false, false, NULL, NULL, 456 0, 0); 457 TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, 458 "UK", "qemu level 1b", NULL, NULL, NULL, NULL, 459 true, true, true, 460 true, true, GNUTLS_KEY_KEY_CERT_SIGN, 461 false, false, NULL, NULL, 462 0, 0); 463 TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, 464 "UK", "qemu level 2a", NULL, NULL, NULL, NULL, 465 true, true, true, 466 true, true, GNUTLS_KEY_KEY_CERT_SIGN, 467 false, false, NULL, NULL, 468 0, 0); 469 TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, 470 "UK", "qemu.org", NULL, NULL, NULL, NULL, 471 true, true, false, 472 true, true, 473 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 474 true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 475 0, 0); 476 TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, 477 "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, 478 true, true, false, 479 true, true, 480 GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, 481 true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 482 0, 0); 483 484 gnutls_x509_crt_t certchain[] = { 485 cacertrootreq.crt, 486 cacertlevel1areq.crt, 487 cacertlevel1breq.crt, 488 cacertlevel2areq.crt, 489 }; 490 491 test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem", 492 certchain, 493 G_N_ELEMENTS(certchain)); 494 495 TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem", 496 servercertlevel3areq.filename, clientcertlevel2breq.filename, 497 false, false, "qemu.org", NULL); 498 499 ret = g_test_run(); 500 501 test_tls_discard_cert(&clientcertreq); 502 test_tls_discard_cert(&clientcertaltreq); 503 504 test_tls_discard_cert(&servercertreq); 505 test_tls_discard_cert(&servercertalt1req); 506 test_tls_discard_cert(&servercertalt2req); 507 508 test_tls_discard_cert(&cacertreq); 509 test_tls_discard_cert(&altcacertreq); 510 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"); 518 519 test_tls_cleanup(KEYFILE); 520 rmdir(WORKDIR); 521 522 return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 523 } 524 525 #else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ 526 527 int 528 main(void) 529 { 530 return EXIT_SUCCESS; 531 } 532 533 #endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ 534