* Daniel P. Berrange (berra...@redhat.com) wrote: > Add a QIOChannel subclass that can run the TLS protocol over > the top of another QIOChannel instance. The object provides a > simplified API to perform the handshake when starting the TLS > session. The layering of TLS over the underlying channel does > not have to be setup immediately. It is possible to take an > existing QIOChannel that has done some handshake and then swap > in the QIOChannelTLS layer. This allows for use with protocols > which start TLS right away, and those which start plain text > and then negotiate TLS. > > Signed-off-by: Daniel P. Berrange <berra...@redhat.com>
> --- > +#ifdef QIO_DEBUG > +#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while > (0) > +#else > +#define DPRINTF(fmt, ...) do { } while (0) > +#endif Can you use the trace_ stuff rather than dprintf's; I've been trying to remove them all from the migration code (and with trace configured in stderr mode it works pretty easily). On a different question; if this TLS channel is backed by a socket, can I do a shutdown call that will bubble down to the socket? Dave > + > + > +static ssize_t qio_channel_tls_write_handler(const char *buf, > + size_t len, > + void *opaque) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); > + ssize_t ret; > + > + ret = qio_channel_write(tioc->master, buf, len, NULL); > + if (ret == QIO_CHANNEL_ERR_BLOCK) { > + errno = EAGAIN; > + return -1; > + } else if (ret < 0) { > + errno = EIO; > + return -1; > + } > + return ret; > +} > + > +static ssize_t qio_channel_tls_read_handler(char *buf, > + size_t len, > + void *opaque) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); > + ssize_t ret; > + > + ret = qio_channel_read(tioc->master, buf, len, NULL); > + if (ret == QIO_CHANNEL_ERR_BLOCK) { > + errno = EAGAIN; > + return -1; > + } else if (ret < 0) { > + errno = EIO; > + return -1; > + } > + return ret; > +} > + > + > +QIOChannelTLS * > +qio_channel_tls_new_server(QIOChannel *master, > + QCryptoTLSCreds *creds, > + const char *aclname, > + Error **errp) > +{ > + QIOChannelTLS *ioc; > + > + ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); > + > + ioc->master = master; > + object_ref(OBJECT(master)); > + > + ioc->session = qcrypto_tls_session_new( > + creds, > + NULL, > + aclname, > + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, > + errp); > + if (!ioc->session) { > + goto error; > + } > + > + qcrypto_tls_session_set_callbacks( > + ioc->session, > + qio_channel_tls_write_handler, > + qio_channel_tls_read_handler, > + ioc); > + > + return ioc; > + > + error: > + DPRINTF("Session setup failed %s\n", > + error_get_pretty(*errp)); > + object_unref(OBJECT(ioc)); > + return NULL; > +} > + > +QIOChannelTLS * > +qio_channel_tls_new_client(QIOChannel *master, > + QCryptoTLSCreds *creds, > + const char *hostname, > + Error **errp) > +{ > + QIOChannelTLS *ioc; > + > + ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); > + > + ioc->master = master; > + object_ref(OBJECT(master)); > + > + ioc->session = qcrypto_tls_session_new( > + creds, > + hostname, > + NULL, > + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, > + errp); > + if (!ioc->session) { > + goto error; > + } > + > + qcrypto_tls_session_set_callbacks( > + ioc->session, > + qio_channel_tls_write_handler, > + qio_channel_tls_read_handler, > + ioc); > + > + return ioc; > + > + error: > + DPRINTF("Session setup failed %s\n", > + error_get_pretty(*errp)); > + object_unref(OBJECT(ioc)); > + return NULL; > +} > + > + > +static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, > + GIOCondition condition, > + gpointer user_data); > + > +static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, > + QIOTask *task) > +{ > + Error *err = NULL; > + QCryptoTLSSessionHandshakeStatus status; > + > + if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { > + qio_task_abort(task, err); > + goto cleanup; > + } > + > + status = qcrypto_tls_session_get_handshake_status(ioc->session); > + if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { > + if (qcrypto_tls_session_check_credentials(ioc->session, > + &err) < 0) { > + DPRINTF("Check creds failed session=%p err=%s\n", > + ioc->session, error_get_pretty(err)); > + qio_task_abort(task, err); > + goto cleanup; > + } > + DPRINTF("Handshake compelte session=%p\n", > + ioc->session); > + qio_task_complete(task); > + } else { > + GIOCondition condition; > + DPRINTF("Handshake still running %d\n", status); > + if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) { > + condition = G_IO_OUT; > + } else { > + condition = G_IO_IN; > + } > + > + qio_channel_add_watch(ioc->master, > + condition, > + qio_channel_tls_handshake_io, > + task, > + NULL); > + } > + > + cleanup: > + error_free(err); > +} > + > + > +static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, > + GIOCondition condition, > + gpointer user_data) > +{ > + QIOTask *task = user_data; > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS( > + qio_task_get_source(task)); > + > + qio_channel_tls_handshake_task( > + tioc, task); > + > + object_unref(OBJECT(tioc)); > + > + return FALSE; > +} > + > +void qio_channel_tls_handshake(QIOChannelTLS *ioc, > + QIOTaskFunc func, > + gpointer opaque, > + GDestroyNotify destroy) > +{ > + QIOTask *task; > + > + task = qio_task_new(OBJECT(ioc), > + func, opaque, destroy); > + > + qio_channel_tls_handshake_task(ioc, task); > +} > + > + > +static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) > +{ > +} > + > + > +static void qio_channel_tls_finalize(Object *obj) > +{ > + QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj); > + > + object_unref(OBJECT(ioc->master)); > + qcrypto_tls_session_free(ioc->session); > +} > + > + > +static ssize_t qio_channel_tls_readv(QIOChannel *ioc, > + const struct iovec *iov, > + size_t niov, > + int **fds, > + size_t *nfds, > + Error **errp) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); > + size_t i; > + ssize_t got = 0; > + > + if (fds || nfds) { > + error_setg(errp, "%s", > + _("Cannot receive file descriptors over TLS channel")); > + return -1; > + } > + > + for (i = 0 ; i < niov ; i++) { > + ssize_t ret = qcrypto_tls_session_read(tioc->session, > + iov[i].iov_base, > + iov[i].iov_len); > + if (ret < 0) { > + if (errno == EAGAIN) { > + if (got) { > + return got; > + } else { > + return QIO_CHANNEL_ERR_BLOCK; > + } > + } > + > + error_setg_errno(errp, errno, "%s", > + _("Cannot read from TLS channel")); > + return -1; > + } > + got += ret; > + if (ret < iov[i].iov_len) { > + break; > + } > + } > + return got; > +} > + > + > +static ssize_t qio_channel_tls_writev(QIOChannel *ioc, > + const struct iovec *iov, > + size_t niov, > + int *fds, > + size_t nfds, > + Error **errp) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); > + size_t i; > + ssize_t done = 0; > + > + if (fds || nfds) { > + error_setg(errp, "%s", > + _("Cannot send file descriptors over TLS channel")); > + return -1; > + } > + > + for (i = 0 ; i < niov ; i++) { > + ssize_t ret = qcrypto_tls_session_write(tioc->session, > + iov[i].iov_base, > + iov[i].iov_len); > + if (ret <= 0) { > + if (errno == EAGAIN) { > + if (done) { > + return done; > + } else { > + return QIO_CHANNEL_ERR_BLOCK; > + } > + } > + > + error_setg_errno(errp, errno, "%s", > + _("Cannot write to TLS channel")); > + return -1; > + } > + done += ret; > + if (ret < iov[i].iov_len) { > + break; > + } > + } > + return done; > +} > + > +static void qio_channel_tls_set_blocking(QIOChannel *ioc, > + bool enabled) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); > + > + qio_channel_set_blocking(tioc->master, enabled); > +} > + > +static int qio_channel_tls_close(QIOChannel *ioc, > + Error **errp) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); > + > + return qio_channel_close(tioc->master, errp); > +} > + > +static GSource *qio_channel_tls_create_watch(QIOChannel *ioc, > + GIOCondition condition) > +{ > + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); > + > + return qio_channel_create_watch(tioc->master, condition); > +} > + > +QCryptoTLSSession * > +qio_channel_tls_get_session(QIOChannelTLS *ioc) > +{ > + return ioc->session; > +} > + > +static void qio_channel_tls_class_init(ObjectClass *klass, > + void *class_data G_GNUC_UNUSED) > +{ > + QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); > + > + ioc_klass->io_writev = qio_channel_tls_writev; > + ioc_klass->io_readv = qio_channel_tls_readv; > + ioc_klass->io_set_blocking = qio_channel_tls_set_blocking; > + ioc_klass->io_close = qio_channel_tls_close; > + ioc_klass->io_create_watch = qio_channel_tls_create_watch; > +} > + > +static const TypeInfo qio_channel_tls_info = { > + .parent = TYPE_QIO_CHANNEL, > + .name = TYPE_QIO_CHANNEL_TLS, > + .instance_size = sizeof(QIOChannelTLS), > + .instance_init = qio_channel_tls_init, > + .instance_finalize = qio_channel_tls_finalize, > + .class_init = qio_channel_tls_class_init, > +}; > + > +static void qio_channel_tls_register_types(void) > +{ > + type_register_static(&qio_channel_tls_info); > +} > + > +type_init(qio_channel_tls_register_types); > diff --git a/tests/.gitignore b/tests/.gitignore > index bb66d94..aa90bb2 100644 > --- a/tests/.gitignore > +++ b/tests/.gitignore > @@ -26,6 +26,7 @@ test-iov > test-io-channel-file > test-io-channel-file.txt > test-io-channel-socket > +test-io-channel-tls > test-io-task > test-mul64 > test-opts-visitor > diff --git a/tests/Makefile b/tests/Makefile > index f896051..8138362 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -81,6 +81,7 @@ check-unit-$(CONFIG_GNUTLS) += > tests/test-crypto-tlssession$(EXESUF) > check-unit-y += tests/test-io-task$(EXESUF) > check-unit-y += tests/test-io-channel-socket$(EXESUF) > check-unit-y += tests/test-io-channel-file$(EXESUF) > +check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) > > check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh > > @@ -366,6 +367,9 @@ tests/test-io-channel-socket$(EXESUF): > tests/test-io-channel-socket.o \ > tests/io-channel-helpers.o $(test-io-obj-y) > tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ > tests/io-channel-helpers.o $(test-io-obj-y) > +tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ > + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ > + tests/io-channel-helpers.o $(test-io-obj-y) > > libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o > libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o > diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c > new file mode 100644 > index 0000000..75a1396 > --- /dev/null > +++ b/tests/test-io-channel-tls.c > @@ -0,0 +1,335 @@ > +/* > + * 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 <berra...@redhat.com> > + */ > + > + > +#include <stdlib.h> > +#include <fcntl.h> > + > +#include "config-host.h" > +#include "crypto-tls-x509-helpers.h" > +#include "io/channel-tls.h" > +#include "io/channel-socket.h" > +#include "io-channel-helpers.h" > +#include "crypto/tlscredsx509.h" > +#include "qemu/acl.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(Object *source, > + Error *err, > + gpointer opaque) > +{ > + struct QIOChannelTLSHandshakeData *data = opaque; > + > + data->finished = true; > + data->failed = err != NULL; > +} > + > + > +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint > endpoint, > + const char *certdir, > + Error **errp) > +{ > + 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"), > + errp, > + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? > + "server" : "client"), > + "dir", certdir, > + "verify-peer", "yes", > + /* 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 > + ); > + > + if (*errp) { > + return 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; > + qemu_acl *acl; > + const char * const *wildcards; > + int channel[2]; > + struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; > + struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; > + Error *err = NULL; > + 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-crypto-tlssession-client/" > +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-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, > + &err); > + g_assert(clientCreds != NULL); > + > + serverCreds = test_tls_creds_create( > + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, > + SERVER_CERT_DIR, > + &err); > + g_assert(serverCreds != NULL); > + > + acl = qemu_acl_init("channeltlsacl"); > + qemu_acl_reset(acl); > + wildcards = data->wildcards; > + while (wildcards && *wildcards) { > + qemu_acl_append(acl, 0, *wildcards); > + wildcards++; > + } > + > + clientChanSock = qio_channel_socket_new_fd( > + channel[0], &err); > + g_assert(clientChanSock != NULL); > + serverChanSock = qio_channel_socket_new_fd( > + channel[1], &err); > + 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); > + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false); > + > + /* Now the real part of the test, setup the sessions */ > + clientChanTLS = qio_channel_tls_new_client( > + QIO_CHANNEL(clientChanSock), clientCreds, > + data->hostname, &err); > + g_assert(clientChanTLS != NULL); > + > + serverChanTLS = qio_channel_tls_new_server( > + QIO_CHANNEL(serverChanSock), serverCreds, > + "channeltlsacl", &err); > + g_assert(serverChanTLS != NULL); > + > + qio_channel_tls_handshake(clientChanTLS, > + test_tls_handshake_done, > + &clientHandshake, > + NULL); > + qio_channel_tls_handshake(serverChanTLS, > + test_tls_handshake_done, > + &serverHandshake, > + 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_io_channel_comms(false, > + QIO_CHANNEL(clientChanTLS), > + QIO_CHANNEL(serverChanTLS)); > + > + test_io_channel_comms(true, > + QIO_CHANNEL(clientChanTLS), > + QIO_CHANNEL(serverChanTLS)); > + > + 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)); > + > + close(channel[0]); > + close(channel[1]); > +} > + > + > +int main(int argc, char **argv) > +{ > + int ret; > + > + 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 */ > -- > 2.4.3 > -- Dr. David Alan Gilbert / dgilb...@redhat.com / Manchester, UK