123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- /*
- * QEMU I/O channel TLS test
- *
- * Copyright (C) 2015 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
- #include "qemu/osdep.h"
- #include "crypto-tls-x509-helpers.h"
- #include "io/channel-tls.h"
- #include "io/channel-socket.h"
- #include "io-channel-helpers.h"
- #include "crypto/init.h"
- #include "crypto/tlscredsx509.h"
- #include "qapi/error.h"
- #include "qemu/module.h"
- #include "authz/list.h"
- #include "qom/object_interfaces.h"
- #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
- #define WORKDIR "tests/test-io-channel-tls-work/"
- #define KEYFILE WORKDIR "key-ctx.pem"
- struct QIOChannelTLSTestData {
- const char *servercacrt;
- const char *clientcacrt;
- const char *servercrt;
- const char *clientcrt;
- bool expectServerFail;
- bool expectClientFail;
- const char *hostname;
- const char *const *wildcards;
- };
- struct QIOChannelTLSHandshakeData {
- bool finished;
- bool failed;
- };
- static void test_tls_handshake_done(QIOTask *task,
- gpointer opaque)
- {
- struct QIOChannelTLSHandshakeData *data = opaque;
- data->finished = true;
- data->failed = qio_task_propagate_error(task, NULL);
- }
- static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
- const char *certdir)
- {
- Object *parent = object_get_objects_root();
- Object *creds = object_new_with_props(
- TYPE_QCRYPTO_TLS_CREDS_X509,
- parent,
- (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
- "testtlscredsserver" : "testtlscredsclient"),
- &error_abort,
- "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
- "server" : "client"),
- "dir", certdir,
- "verify-peer", "yes",
- "priority", "NORMAL",
- /* We skip initial sanity checks here because we
- * want to make sure that problems are being
- * detected at the TLS session validation stage,
- * and the test-crypto-tlscreds test already
- * validate the sanity check code.
- */
- "sanity-check", "no",
- NULL
- );
- return QCRYPTO_TLS_CREDS(creds);
- }
- /*
- * This tests validation checking of peer certificates
- *
- * This is replicating the checks that are done for an
- * active TLS session after handshake completes. To
- * simulate that we create our TLS contexts, skipping
- * sanity checks. When then get a socketpair, and
- * initiate a TLS session across them. Finally do
- * do actual cert validation tests
- */
- static void test_io_channel_tls(const void *opaque)
- {
- struct QIOChannelTLSTestData *data =
- (struct QIOChannelTLSTestData *)opaque;
- QCryptoTLSCreds *clientCreds;
- QCryptoTLSCreds *serverCreds;
- QIOChannelTLS *clientChanTLS;
- QIOChannelTLS *serverChanTLS;
- QIOChannelSocket *clientChanSock;
- QIOChannelSocket *serverChanSock;
- QAuthZList *auth;
- const char * const *wildcards;
- int channel[2];
- struct QIOChannelTLSHandshakeData clientHandshake = { false, false };
- struct QIOChannelTLSHandshakeData serverHandshake = { false, false };
- QIOChannelTest *test;
- GMainContext *mainloop;
- /* We'll use this for our fake client-server connection */
- g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0);
- #define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/"
- #define SERVER_CERT_DIR "tests/test-io-channel-tls-server/"
- mkdir(CLIENT_CERT_DIR, 0700);
- mkdir(SERVER_CERT_DIR, 0700);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
- g_assert(link(data->servercacrt,
- SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
- g_assert(link(data->servercrt,
- SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
- g_assert(link(KEYFILE,
- SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
- g_assert(link(data->clientcacrt,
- CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
- g_assert(link(data->clientcrt,
- CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
- g_assert(link(KEYFILE,
- CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
- clientCreds = test_tls_creds_create(
- QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
- CLIENT_CERT_DIR);
- g_assert(clientCreds != NULL);
- serverCreds = test_tls_creds_create(
- QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
- SERVER_CERT_DIR);
- g_assert(serverCreds != NULL);
- auth = qauthz_list_new("channeltlsacl",
- QAUTHZ_LIST_POLICY_DENY,
- &error_abort);
- wildcards = data->wildcards;
- while (wildcards && *wildcards) {
- qauthz_list_append_rule(auth, *wildcards,
- QAUTHZ_LIST_POLICY_ALLOW,
- QAUTHZ_LIST_FORMAT_GLOB,
- &error_abort);
- wildcards++;
- }
- clientChanSock = qio_channel_socket_new_fd(
- channel[0], &error_abort);
- g_assert(clientChanSock != NULL);
- serverChanSock = qio_channel_socket_new_fd(
- channel[1], &error_abort);
- g_assert(serverChanSock != NULL);
- /*
- * We have an evil loop to do the handshake in a single
- * thread, so we need these non-blocking to avoid deadlock
- * of ourselves
- */
- qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL);
- qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL);
- /* Now the real part of the test, setup the sessions */
- clientChanTLS = qio_channel_tls_new_client(
- QIO_CHANNEL(clientChanSock), clientCreds,
- data->hostname, &error_abort);
- g_assert(clientChanTLS != NULL);
- serverChanTLS = qio_channel_tls_new_server(
- QIO_CHANNEL(serverChanSock), serverCreds,
- "channeltlsacl", &error_abort);
- g_assert(serverChanTLS != NULL);
- qio_channel_tls_handshake(clientChanTLS,
- test_tls_handshake_done,
- &clientHandshake,
- NULL,
- NULL);
- qio_channel_tls_handshake(serverChanTLS,
- test_tls_handshake_done,
- &serverHandshake,
- NULL,
- NULL);
- /*
- * Finally we loop around & around doing handshake on each
- * session until we get an error, or the handshake completes.
- * This relies on the socketpair being nonblocking to avoid
- * deadlocking ourselves upon handshake
- */
- mainloop = g_main_context_default();
- do {
- g_main_context_iteration(mainloop, TRUE);
- } while (!clientHandshake.finished ||
- !serverHandshake.finished);
- g_assert(clientHandshake.failed == data->expectClientFail);
- g_assert(serverHandshake.failed == data->expectServerFail);
- test = qio_channel_test_new();
- qio_channel_test_run_threads(test, false,
- QIO_CHANNEL(clientChanTLS),
- QIO_CHANNEL(serverChanTLS));
- qio_channel_test_validate(test);
- test = qio_channel_test_new();
- qio_channel_test_run_threads(test, true,
- QIO_CHANNEL(clientChanTLS),
- QIO_CHANNEL(serverChanTLS));
- qio_channel_test_validate(test);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
- unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
- unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
- rmdir(CLIENT_CERT_DIR);
- rmdir(SERVER_CERT_DIR);
- object_unparent(OBJECT(serverCreds));
- object_unparent(OBJECT(clientCreds));
- object_unref(OBJECT(serverChanTLS));
- object_unref(OBJECT(clientChanTLS));
- object_unref(OBJECT(serverChanSock));
- object_unref(OBJECT(clientChanSock));
- object_unparent(OBJECT(auth));
- close(channel[0]);
- close(channel[1]);
- }
- int main(int argc, char **argv)
- {
- int ret;
- g_assert(qcrypto_init(NULL) == 0);
- module_call_init(MODULE_INIT_QOM);
- g_test_init(&argc, &argv, NULL);
- setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
- mkdir(WORKDIR, 0700);
- test_tls_init(KEYFILE);
- # define TEST_CHANNEL(name, caCrt, \
- serverCrt, clientCrt, \
- expectServerFail, expectClientFail, \
- hostname, wildcards) \
- struct QIOChannelTLSTestData name = { \
- caCrt, caCrt, serverCrt, clientCrt, \
- expectServerFail, expectClientFail, \
- hostname, wildcards \
- }; \
- g_test_add_data_func("/qio/channel/tls/" # name, \
- &name, test_io_channel_tls);
- /* A perfect CA, perfect client & perfect server */
- /* Basic:CA:critical */
- TLS_ROOT_REQ(cacertreq,
- "UK", "qemu CA", NULL, NULL, NULL, NULL,
- true, true, true,
- true, true, GNUTLS_KEY_KEY_CERT_SIGN,
- false, false, NULL, NULL,
- 0, 0);
- TLS_CERT_REQ(servercertreq, cacertreq,
- "UK", "qemu.org", NULL, NULL, NULL, NULL,
- true, true, false,
- true, true,
- GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
- true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
- 0, 0);
- TLS_CERT_REQ(clientcertreq, cacertreq,
- "UK", "qemu", NULL, NULL, NULL, NULL,
- true, true, false,
- true, true,
- GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
- true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
- 0, 0);
- const char *const wildcards[] = {
- "C=UK,CN=qemu*",
- NULL,
- };
- TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename,
- clientcertreq.filename, false, false,
- "qemu.org", wildcards);
- ret = g_test_run();
- test_tls_discard_cert(&clientcertreq);
- test_tls_discard_cert(&servercertreq);
- test_tls_discard_cert(&cacertreq);
- test_tls_cleanup(KEYFILE);
- rmdir(WORKDIR);
- return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
- }
- #else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
- int
- main(void)
- {
- return EXIT_SUCCESS;
- }
- #endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
|