[libvirt] [PATCH 0/4] Fix handling of CA certificate chains

This series fixes the CA certificate validation so that it correctly works when a client and server cert are both signed by intermediate CAs, sharing a common ancestor CA.

From: "Daniel P. Berrange" <berrange@redhat.com> The virnettlscontexttest.c tests both virNetTLSContext and virNetTLSSession functionality. Split into two separate tests, to make the code size more manageable Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- cfg.mk | 2 +- tests/Makefile.am | 17 +- tests/virnettlscontexttest.c | 787 +------------------------------------------ tests/virnettlshelpers.c | 420 +++++++++++++++++++++++ tests/virnettlshelpers.h | 79 +++++ tests/virnettlssessiontest.c | 414 +++++++++++++++++++++++ 6 files changed, 945 insertions(+), 774 deletions(-) create mode 100644 tests/virnettlshelpers.c create mode 100644 tests/virnettlshelpers.h create mode 100644 tests/virnettlssessiontest.c diff --git a/cfg.mk b/cfg.mk index 13de268..c59d432 100644 --- a/cfg.mk +++ b/cfg.mk @@ -899,7 +899,7 @@ $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protoco exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.h$$ _src1=libvirt|fdstream|qemu/qemu_monitor|util/(vircommand|virfile)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon -_test1=shunloadtest|virnettlscontexttest|vircgroupmock +_test1=shunloadtest|virnettlscontexttest|virnettlssessiontest|vircgroupmock exclude_file_name_regexp--sc_avoid_write = \ ^(src/($(_src1))|daemon/libvirtd|tools/console|tests/($(_test1)))\.c$$ diff --git a/tests/Makefile.am b/tests/Makefile.am index 789de9f..4aa02dd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -134,7 +134,7 @@ test_programs += virdbustest \ endif if WITH_GNUTLS -test_programs += virnettlscontexttest +test_programs += virnettlscontexttest virnettlssessiontest endif if WITH_SECDRIVER_SELINUX @@ -601,17 +601,28 @@ virnetsockettest_LDADD = $(LDADDS) if WITH_GNUTLS virnettlscontexttest_SOURCES = \ - virnettlscontexttest.c testutils.h testutils.c + virnettlscontexttest.c \ + virnettlshelpers.h virnettlshelpers.c \ + testutils.h testutils.c virnettlscontexttest_LDADD = $(LDADDS) +virnettlssessiontest_SOURCES = \ + virnettlssessiontest.c \ + virnettlshelpers.h virnettlshelpers.c \ + testutils.h testutils.c +virnettlssessiontest_LDADD = $(LDADDS) if HAVE_LIBTASN1 virnettlscontexttest_SOURCES += pkix_asn1_tab.c virnettlscontexttest_LDADD += -ltasn1 +virnettlssessiontest_SOURCES += pkix_asn1_tab.c +virnettlssessiontest_LDADD += -ltasn1 else EXTRA_DIST += pkix_asn1_tab.c endif else EXTRA_DIST += \ - virnettlscontexttest.c testutils.h testutils.c pkix_asn1_tab.c + virnettlscontexttest.c virnettlssessiontest.c \ + virnettlshelpers.h virnettlshelpers.c \ + testutils.h testutils.c pkix_asn1_tab.c endif virtimetest_SOURCES = \ diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index 908432b..0a0d31a 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c @@ -23,10 +23,9 @@ #include <stdlib.h> #include <fcntl.h> #include <sys/socket.h> -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> #include "testutils.h" +#include "virnettlshelpers.h" #include "virutil.h" #include "virerror.h" #include "viralloc.h" @@ -34,409 +33,13 @@ #include "virfile.h" #include "vircommand.h" #include "virsocketaddr.h" -#include "gnutls_1_0_compat.h" #if !defined WIN32 && HAVE_LIBTASN1_H && LIBGNUTLS_VERSION_NUMBER >= 0x020600 -# include <libtasn1.h> # include "rpc/virnettlscontext.h" # define VIR_FROM_THIS VIR_FROM_RPC -const char *keyfile = abs_builddir "/virnettlscontexttest-key.pem"; - -/* - * These store some static data that is needed when - * encoding extensions in the x509 certs - */ -ASN1_TYPE pkix_asn1; -extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; - -/* - * To avoid consuming random entropy to generate keys, - * here's one we prepared earlier :-) - */ -gnutls_x509_privkey_t privkey; -# define PRIVATE_KEY \ - "-----BEGIN PRIVATE KEY-----\n" \ - "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \ - "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \ - "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \ - "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \ - "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \ - "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \ - "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \ - "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \ - "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \ - "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \ - "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \ - "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \ - "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \ - "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \ - "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \ - "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \ - "-----END PRIVATE KEY-----\n" - - -/* - * This contains parameter about how to generate - * certificates. - */ -struct testTLSCertReq { - gnutls_x509_crt_t crt; - gnutls_x509_crt_t cacrt; /* If not set, then the cert will be self-signed */ - - const char *filename; - - /* Identifying information */ - const char *country; - const char *cn; - const char *altname1; - const char *altname2; - const char *ipaddr1; - const char *ipaddr2; - - /* Basic constraints */ - bool basicConstraintsEnable; - bool basicConstraintsCritical; - bool basicConstraintsIsCA; - - /* Key usage */ - bool keyUsageEnable; - bool keyUsageCritical; - int keyUsageValue; - - /* Key purpose (aka Extended key usage) */ - bool keyPurposeEnable; - bool keyPurposeCritical; - const char *keyPurposeOID1; - const char *keyPurposeOID2; - - /* zero for current time, or non-zero for hours from now */ - int start_offset; - /* zero for 24 hours from now, or non-zero for hours from now */ - int expire_offset; -}; - - -/* - * Turns an ASN1 object into a DER encoded byte array - */ -static void testTLSDerEncode(ASN1_TYPE src, - const char *src_name, - gnutls_datum_t * res) -{ - int size; - char *data = NULL; - - size = 0; - asn1_der_coding(src, src_name, NULL, &size, NULL); - - if (VIR_ALLOC_N(data, size) < 0) - abort(); - - asn1_der_coding(src, src_name, data, &size, NULL); - - res->data = (unsigned char *)data; - res->size = size; -} - - -/* - * This is a fairly lame x509 certificate generator. - * - * Do not copy/use this code for generating real certificates - * since it leaves out many things that you would want in - * certificates for real world usage. - * - * This is good enough only for doing tests of the libvirt - * TLS certificate code - */ -static void -testTLSGenerateCert(struct testTLSCertReq *req) -{ - gnutls_x509_crt_t crt; - int err; - static char buffer[1024*1024]; - size_t size = sizeof(buffer); - char serial[5] = { 1, 2, 3, 4, 0 }; - gnutls_datum_t der; - time_t start = time(NULL) + (60*60*req->start_offset); - time_t expire = time(NULL) + (60*60*(req->expire_offset - ? req->expire_offset : 24)); - - /* - * Prepare our new certificate object - */ - if ((err = gnutls_x509_crt_init(&crt)) < 0) { - VIR_WARN("Failed to initialize certificate %s", gnutls_strerror(err)); - abort(); - } - if ((err = gnutls_x509_crt_set_key(crt, privkey)) < 0) { - VIR_WARN("Failed to set certificate key %s", gnutls_strerror(err)); - abort(); - } - - /* - * A v3 certificate is required in order to be able - * set any of the basic constraints, key purpose and - * key usage data - */ - gnutls_x509_crt_set_version(crt, 3); - - if (req->country) { - if ((err = gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, - req->country, strlen(req->country))) < 0) { - VIR_WARN("Failed to set certificate country name %s", gnutls_strerror(err)); - abort(); - } - } - if (req->cn) { - if ((err = gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, - req->cn, strlen(req->cn))) < 0) { - VIR_WARN("Failed to set certificate common name %s", gnutls_strerror(err)); - abort(); - } - } - - /* - * Setup the subject altnames, which are used - * for hostname checks in live sessions - */ - if (req->altname1) { - if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, - req->altname1, - strlen(req->altname1), - GNUTLS_FSAN_APPEND))) { - VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); - abort(); - } - } - if (req->altname2) { - if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, - req->altname2, - strlen(req->altname2), - GNUTLS_FSAN_APPEND))) { - VIR_WARN("Failed to set certificate %s alt name", gnutls_strerror(err)); - abort(); - } - } - - /* - * IP address need to be put into the cert in their - * raw byte form, not strings, hence this is a little - * more complicated - */ - if (req->ipaddr1) { - virSocketAddr addr; - char *data; - int len; - if (virSocketAddrParse(&addr, req->ipaddr1, 0) < 0) { - VIR_WARN("Cannot parse %s", req->ipaddr1); - abort(); - } - - if (addr.data.sa.sa_family == AF_INET) { - data = (char*)&addr.data.inet4.sin_addr; - len = 4; - } else { - data = (char*)&addr.data.inet6.sin6_addr; - len = 16; - } - - if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_IPADDRESS, - data, len, GNUTLS_FSAN_APPEND))) { - VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); - abort(); - } - } - if (req->ipaddr2) { - virSocketAddr addr; - char *data; - int len; - if (virSocketAddrParse(&addr, req->ipaddr2, 0) < 0) { - VIR_WARN("Cannot parse %s", req->ipaddr2); - abort(); - } - - if (addr.data.sa.sa_family == AF_INET) { - data = (char*)&addr.data.inet4.sin_addr; - len = 4; - } else { - data = (char*)&addr.data.inet6.sin6_addr; - len = 16; - } - - if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_IPADDRESS, - data, len, GNUTLS_FSAN_APPEND))) { - VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); - abort(); - } - } - - - /* - * Basic constraints are used to decide if the cert - * is for a CA or not. We can't use the convenient - * gnutls API for setting this, since it hardcodes - * the 'critical' field which we want control over - */ - if (req->basicConstraintsEnable) { - ASN1_TYPE ext = ASN1_TYPE_EMPTY; - - asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); - asn1_write_value(ext, "cA", req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); - asn1_write_value(ext, "pathLenConstraint", NULL, 0); - testTLSDerEncode(ext, "", &der); - if ((err = gnutls_x509_crt_set_extension_by_oid(crt, - "2.5.29.19", - der.data, - der.size, - req->basicConstraintsCritical)) < 0) { - VIR_WARN("Failed to set certificate basic constraints %s", gnutls_strerror(err)); - VIR_FREE(der.data); - abort(); - } - asn1_delete_structure(&ext); - VIR_FREE(der.data); - } - - /* - * Next up the key usage extension. Again we can't - * use the gnutls API since it hardcodes the extension - * to be 'critical' - */ - if (req->keyUsageEnable) { - ASN1_TYPE ext = ASN1_TYPE_EMPTY; - char str[2]; - - str[0] = req->keyUsageValue & 0xff; - str[1] = (req->keyUsageValue >> 8) & 0xff; - - asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); - asn1_write_value(ext, "", str, 9); - testTLSDerEncode(ext, "", &der); - if ((err = gnutls_x509_crt_set_extension_by_oid(crt, - "2.5.29.15", - der.data, - der.size, - req->keyUsageCritical)) < 0) { - VIR_WARN("Failed to set certificate key usage %s", gnutls_strerror(err)); - VIR_FREE(der.data); - abort(); - } - asn1_delete_structure(&ext); - VIR_FREE(der.data); - } - - /* - * Finally the key purpose extension. This time - * gnutls has the opposite problem, always hardcoding - * it to be non-critical. So once again we have to - * set this the hard way building up ASN1 data ourselves - */ - if (req->keyPurposeEnable) { - ASN1_TYPE ext = ASN1_TYPE_EMPTY; - - asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); - if (req->keyPurposeOID1) { - asn1_write_value(ext, "", "NEW", 1); - asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); - } - if (req->keyPurposeOID2) { - asn1_write_value(ext, "", "NEW", 1); - asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); - } - testTLSDerEncode(ext, "", &der); - if ((err = gnutls_x509_crt_set_extension_by_oid(crt, - "2.5.29.37", - der.data, - der.size, - req->keyPurposeCritical)) < 0) { - VIR_WARN("Failed to set certificate key purpose %s", gnutls_strerror(err)); - VIR_FREE(der.data); - abort(); - } - asn1_delete_structure(&ext); - VIR_FREE(der.data); - } - - /* - * Any old serial number will do, so lets pick 5 - */ - if ((err = gnutls_x509_crt_set_serial(crt, serial, 5)) < 0) { - VIR_WARN("Failed to set certificate serial %s", gnutls_strerror(err)); - abort(); - } - - if ((err = gnutls_x509_crt_set_activation_time(crt, start)) < 0) { - VIR_WARN("Failed to set certificate activation %s", gnutls_strerror(err)); - abort(); - } - if ((err = gnutls_x509_crt_set_expiration_time(crt, expire)) < 0) { - VIR_WARN("Failed to set certificate expiration %s", gnutls_strerror(err)); - abort(); - } - - - /* - * If no 'cart' is set then we are self signing - * the cert. This is done for CA certs - */ - if ((err = gnutls_x509_crt_sign(crt, req->cacrt ? req->cacrt : crt, privkey) < 0)) { - VIR_WARN("Failed to sign certificate %s", gnutls_strerror(err)); - abort(); - } - - /* - * Finally write the new cert out to disk - */ - if ((err = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) { - VIR_WARN("Failed to export certificate %s", gnutls_strerror(err)); - abort(); - } - - if (virFileWriteStr(req->filename, buffer, 0600) < 0) { - VIR_WARN("Failed to write certificate %s %s", req->filename, gnutls_strerror(err)); - abort(); - } - - req->crt = crt; - return; -} - - -/* - * This loads the private key we defined earlier - */ -static gnutls_x509_privkey_t testTLSLoadKey(void) -{ - gnutls_x509_privkey_t key; - const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, strlen(PRIVATE_KEY) }; - int err; - - if ((err = gnutls_x509_privkey_init(&key)) < 0) { - VIR_WARN("Failed to init key %s", gnutls_strerror(err)); - abort(); - } - - if ((err = gnutls_x509_privkey_import(key, &data, - GNUTLS_X509_FMT_PEM)) < 0) { - if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) { - VIR_WARN("Failed to import key %s", gnutls_strerror(err)); - abort(); - } - - if ((err = gnutls_x509_privkey_import_pkcs8(key, &data, GNUTLS_X509_FMT_PEM, NULL, 0)) < 0) { - VIR_WARN("Failed to import PKCS8 key %s", gnutls_strerror(err)); - abort(); - } - } - - return key; -} - - struct testTLSContextData { bool isServer; struct testTLSCertReq careq; @@ -500,257 +103,19 @@ static int testTLSContextInit(const void *opaque) cleanup: virObjectUnref(ctxt); - gnutls_x509_crt_deinit(data->careq.crt); - gnutls_x509_crt_deinit(data->certreq.crt); - data->careq.crt = data->certreq.crt = NULL; - /* When troubleshooting this tests, we often want to leave the certs on disk */ - if (getenv("VIRT_TEST_DEBUG_CERTS") == NULL) { - unlink(data->careq.filename); - unlink(data->certreq.filename); - } + testTLSDiscardCert(&data->careq); + testTLSDiscardCert(&data->certreq); return ret; } -struct testTLSSessionData { - struct testTLSCertReq careq; - struct testTLSCertReq othercareq; - struct testTLSCertReq serverreq; - struct testTLSCertReq clientreq; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const* wildcards; -}; - - -static ssize_t testWrite(const char *buf, size_t len, void *opaque) -{ - int *fd = opaque; - - return write(*fd, buf, len); -} - -static ssize_t testRead(char *buf, size_t len, void *opaque) -{ - int *fd = opaque; - - return read(*fd, buf, len); -} - -/* - * 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 int testTLSSessionInit(const void *opaque) -{ - struct testTLSSessionData *data = (struct testTLSSessionData *)opaque; - virNetTLSContextPtr clientCtxt = NULL; - virNetTLSContextPtr serverCtxt = NULL; - virNetTLSSessionPtr clientSess = NULL; - virNetTLSSessionPtr serverSess = NULL; - int ret = -1; - int channel[2]; - bool clientShake = false; - bool serverShake = false; - - - /* We'll use this for our fake client-server connection */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) < 0) - abort(); - - /* - * We have an evil loop to do the handshake in a single - * thread, so we need these non-blocking to avoid deadlock - * of ourselves - */ - ignore_value(virSetNonBlock(channel[0])); - ignore_value(virSetNonBlock(channel[1])); - - - /* Generate all the certs we need for this test */ - testTLSGenerateCert(&data->careq); - data->serverreq.cacrt = data->careq.crt; - testTLSGenerateCert(&data->serverreq); - - if (data->othercareq.filename) { - testTLSGenerateCert(&data->othercareq); - data->clientreq.cacrt = data->othercareq.crt; - } else { - data->clientreq.cacrt = data->careq.crt; - } - testTLSGenerateCert(&data->clientreq); - - - /* We skip initial sanity checks here because we - * want to make sure that problems are being - * detected at the TLS session validation stage - */ - serverCtxt = virNetTLSContextNewServer(data->careq.filename, - NULL, - data->serverreq.filename, - keyfile, - data->wildcards, - false, - true); - - clientCtxt = virNetTLSContextNewClient(data->othercareq.filename ? - data->othercareq.filename : - data->careq.filename, - NULL, - data->clientreq.filename, - keyfile, - false, - true); - - if (!serverCtxt) { - VIR_WARN("Unexpected failure loading %s against %s", - data->careq.filename, data->serverreq.filename); - goto cleanup; - } - if (!clientCtxt) { - VIR_WARN("Unexpected failure loading %s against %s", - data->othercareq.filename ? data->othercareq.filename : - data->careq.filename, data->clientreq.filename); - goto cleanup; - } - - - /* Now the real part of the test, setup the sessions */ - serverSess = virNetTLSSessionNew(serverCtxt, NULL); - clientSess = virNetTLSSessionNew(clientCtxt, data->hostname); - - if (!serverSess) { - VIR_WARN("Unexpected failure using %s against %s", - data->careq.filename, data->serverreq.filename); - goto cleanup; - } - if (!clientSess) { - VIR_WARN("Unexpected failure using %s against %s", - data->othercareq.filename ? data->othercareq.filename : - data->careq.filename, data->clientreq.filename); - goto cleanup; - } - - /* For handshake to work, we need to set the I/O callbacks - * to read/write over the socketpair - */ - virNetTLSSessionSetIOCallbacks(serverSess, testWrite, testRead, &channel[0]); - virNetTLSSessionSetIOCallbacks(clientSess, testWrite, testRead, &channel[1]); - - /* - * 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 - */ - do { - int rv; - if (!serverShake) { - rv = virNetTLSSessionHandshake(serverSess); - if (rv < 0) - goto cleanup; - if (rv == VIR_NET_TLS_HANDSHAKE_COMPLETE) - serverShake = true; - } - if (!clientShake) { - rv = virNetTLSSessionHandshake(clientSess); - if (rv < 0) - goto cleanup; - if (rv == VIR_NET_TLS_HANDSHAKE_COMPLETE) - clientShake = true; - } - } while (!clientShake && !serverShake); - - - /* Finally make sure the server validation does what - * we were expecting - */ - if (virNetTLSContextCheckCertificate(serverCtxt, - serverSess) < 0) { - if (!data->expectServerFail) { - VIR_WARN("Unexpected server cert check fail"); - goto cleanup; - } else { - VIR_DEBUG("Got expected server cert fail"); - } - } else { - if (data->expectServerFail) { - VIR_WARN("Expected server cert check fail"); - goto cleanup; - } else { - VIR_DEBUG("Not unexpected server cert fail"); - } - } - - /* - * And the same for the client validation check - */ - if (virNetTLSContextCheckCertificate(clientCtxt, - clientSess) < 0) { - if (!data->expectClientFail) { - VIR_WARN("Unexpected client cert check fail"); - goto cleanup; - } else { - VIR_DEBUG("Got expected client cert fail"); - } - } else { - if (data->expectClientFail) { - VIR_WARN("Expected client cert check fail"); - goto cleanup; - } else { - VIR_DEBUG("Not unexpected client cert fail"); - } - } - - ret = 0; - -cleanup: - virObjectUnref(serverCtxt); - virObjectUnref(clientCtxt); - virObjectUnref(serverSess); - virObjectUnref(clientSess); - gnutls_x509_crt_deinit(data->careq.crt); - if (data->othercareq.filename) - gnutls_x509_crt_deinit(data->othercareq.crt); - gnutls_x509_crt_deinit(data->clientreq.crt); - gnutls_x509_crt_deinit(data->serverreq.crt); - data->careq.crt = data->othercareq.crt = data->clientreq.crt = data->serverreq.crt = NULL; - - /* When troubleshooting this tests, we often want to leave the certs on disk */ - if (getenv("VIRT_TEST_DEBUG_CERTS") == NULL) { - unlink(data->careq.filename); - if (data->othercareq.filename) - unlink(data->othercareq.filename); - unlink(data->clientreq.filename); - unlink(data->serverreq.filename); - } - VIR_FORCE_CLOSE(channel[0]); - VIR_FORCE_CLOSE(channel[1]); - return ret; -} - - static int mymain(void) { int ret = 0; - if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) - abort(); - gnutls_global_init(); - - privkey = testTLSLoadKey(); - - if (virFileWriteStr(keyfile, PRIVATE_KEY, 0600) < 0) - return EXIT_FAILURE; + testTLSInit(); # define DO_CTX_TEST(_isServer, _caReq, _certReq, _expectFail) \ do { \ @@ -763,40 +128,6 @@ mymain(void) ret = -1; \ } while (0) -# define DO_SESS_TEST(_caReq, _serverReq, _clientReq, _expectServerFail,\ - _expectClientFail, _hostname, _wildcards) \ - do { \ - static struct testTLSSessionData data; \ - static struct testTLSCertReq other; \ - data.careq = _caReq; \ - data.othercareq = other; \ - data.serverreq = _serverReq; \ - data.clientreq = _clientReq; \ - data.expectServerFail = _expectServerFail; \ - data.expectClientFail = _expectClientFail; \ - data.hostname = _hostname; \ - data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ - ret = -1; \ - } while (0) - -# define DO_SESS_TEST_EXT(_caReq, _othercaReq, _serverReq, _clientReq, \ - _expectServerFail, _expectClientFail, \ - _hostname, _wildcards) \ - do { \ - static struct testTLSSessionData data; \ - data.careq = _caReq; \ - data.othercareq = _othercaReq; \ - data.serverreq = _serverReq; \ - data.clientreq = _clientReq; \ - data.expectServerFail = _expectServerFail; \ - data.expectClientFail = _expectClientFail; \ - data.hostname = _hostname; \ - data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ - ret = -1; \ - } while (0) - /* A perfect CA, perfect client & perfect server */ /* Basic:CA:critical */ @@ -850,24 +181,10 @@ mymain(void) false, false, NULL, NULL, 0, 0, }; - /* Basic:not-CA:not-critical */ -# if 0 - /* Default GNUTLS session config forbids use of CAs without - * basic constraints, so skip this otherwise valid test - */ + /* Key usage:cert-sign:critical */ static struct testTLSCertReq cacert3req = { NULL, NULL, "cacert3.pem", "UK", "libvirt CA 3", NULL, NULL, NULL, NULL, - true, false, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; -# endif - /* Key usage:cert-sign:critical */ - static struct testTLSCertReq cacert4req = { - NULL, NULL, "cacert4.pem", "UK", - "libvirt CA 4", NULL, NULL, NULL, NULL, true, true, true, true, true, GNUTLS_KEY_KEY_CERT_SIGN, false, false, NULL, NULL, @@ -876,35 +193,32 @@ mymain(void) DO_CTX_TEST(true, cacert1req, servercertreq, false); DO_CTX_TEST(true, cacert2req, servercertreq, false); -# if 0 DO_CTX_TEST(true, cacert3req, servercertreq, false); -# endif - DO_CTX_TEST(true, cacert4req, servercertreq, false); /* Now some bad certs */ /* Key usage:dig-sig:not-critical */ - static struct testTLSCertReq cacert5req = { - NULL, NULL, "cacert5.pem", "UK", - "libvirt CA 5", NULL, NULL, NULL, NULL, + static struct testTLSCertReq cacert4req = { + NULL, NULL, "cacert4.pem", "UK", + "libvirt CA 4", NULL, NULL, NULL, NULL, true, true, true, true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, false, false, NULL, NULL, 0, 0, }; /* no-basic */ - static struct testTLSCertReq cacert6req = { - NULL, NULL, "cacert6.pem", "UK", - "libvirt CA 6", NULL, NULL, NULL, NULL, + static struct testTLSCertReq cacert5req = { + NULL, NULL, "cacert5.pem", "UK", + "libvirt CA 5", NULL, NULL, NULL, NULL, false, false, false, false, false, 0, false, false, NULL, NULL, 0, 0, }; /* Key usage:dig-sig:critical */ - static struct testTLSCertReq cacert7req = { - NULL, NULL, "cacert7.pem", "UK", - "libvirt CA 7", NULL, NULL, NULL, NULL, + static struct testTLSCertReq cacert6req = { + NULL, NULL, "cacert6.pem", "UK", + "libvirt CA 6", NULL, NULL, NULL, NULL, true, true, true, true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, false, false, NULL, NULL, @@ -916,9 +230,9 @@ mymain(void) * be rejected. GNUTLS < 3 does not reject it and * we don't anticipate them changing this behaviour */ - DO_CTX_TEST(true, cacert5req, servercertreq, GNUTLS_VERSION_MAJOR >= 3); + DO_CTX_TEST(true, cacert4req, servercertreq, GNUTLS_VERSION_MAJOR >= 3); + DO_CTX_TEST(true, cacert5req, servercertreq, true); DO_CTX_TEST(true, cacert6req, servercertreq, true); - DO_CTX_TEST(true, cacert7req, servercertreq, true); /* Various good servers */ @@ -1200,74 +514,7 @@ mymain(void) DO_CTX_TEST(true, cacertreq, servercertnewreq, true); DO_CTX_TEST(false, cacertreq, clientcertnewreq, true); - - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", NULL); - DO_SESS_TEST_EXT(cacertreq, cacert1req, servercertreq, clientcertreq, true, true, "libvirt.org", NULL); - - /* When an altname is set, the CN is ignored, so it must be duplicated - * as an altname for it to match */ - static struct testTLSCertReq servercertalt1req = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.org", "www.libvirt.org", "libvirt.org", "192.168.122.1", "fec0::dead:beaf", - true, true, false, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0, - }; - /* This intentionally doesn't replicate */ - static struct testTLSCertReq servercertalt2req = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.org", "www.libvirt.org", "wiki.libvirt.org", "192.168.122.1", "fec0::dead:beaf", - true, true, false, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0, - }; - - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "www.libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, true, "wiki.libvirt.org", NULL); - - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, true, "libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "www.libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "wiki.libvirt.org", NULL); - - const char *const wildcards1[] = { - "C=UK,CN=dogfood", - NULL, - }; - const char *const wildcards2[] = { - "C=UK,CN=libvirt", - NULL, - }; - const char *const wildcards3[] = { - "C=UK,CN=dogfood", - "C=UK,CN=libvirt", - NULL, - }; - const char *const wildcards4[] = { - "C=UK,CN=libvirtstuff", - NULL, - }; - const char *const wildcards5[] = { - "C=UK,CN=libvirt*", - NULL, - }; - const char *const wildcards6[] = { - "C=UK,CN=*virt*", - NULL, - }; - - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards1); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards2); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards3); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards4); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards5); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards6); - - unlink(keyfile); - - asn1_delete_structure(&pkix_asn1); + testTLSCleanup(); return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/virnettlshelpers.c b/tests/virnettlshelpers.c new file mode 100644 index 0000000..96b2f6e --- /dev/null +++ b/tests/virnettlshelpers.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2011-2012 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 <config.h> + +#include <stdlib.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include "virnettlshelpers.h" +#include "viralloc.h" +#include "virlog.h" +#include "virfile.h" +#include "virsocketaddr.h" + +#if !defined WIN32 && HAVE_LIBTASN1_H && LIBGNUTLS_VERSION_NUMBER >= 0x020600 + +# define VIR_FROM_THIS VIR_FROM_RPC + +const char *keyfile = abs_builddir "/virnettlscontexttest-key.pem"; + +/* + * These store some static data that is needed when + * encoding extensions in the x509 certs + */ +ASN1_TYPE pkix_asn1; +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +/* + * To avoid consuming random entropy to generate keys, + * here's one we prepared earlier :-) + */ +gnutls_x509_privkey_t privkey; +# define PRIVATE_KEY \ + "-----BEGIN PRIVATE KEY-----\n" \ + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \ + "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \ + "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \ + "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \ + "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \ + "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \ + "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \ + "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \ + "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \ + "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \ + "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \ + "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \ + "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \ + "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \ + "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \ + "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \ + "-----END PRIVATE KEY-----\n" + +/* + * This loads the private key we defined earlier + */ +static gnutls_x509_privkey_t testTLSLoadKey(void) +{ + gnutls_x509_privkey_t key; + const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, strlen(PRIVATE_KEY) }; + int err; + + if ((err = gnutls_x509_privkey_init(&key)) < 0) { + VIR_WARN("Failed to init key %s", gnutls_strerror(err)); + abort(); + } + + if ((err = gnutls_x509_privkey_import(key, &data, + GNUTLS_X509_FMT_PEM)) < 0) { + if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) { + VIR_WARN("Failed to import key %s", gnutls_strerror(err)); + abort(); + } + + if ((err = gnutls_x509_privkey_import_pkcs8(key, &data, GNUTLS_X509_FMT_PEM, NULL, 0)) < 0) { + VIR_WARN("Failed to import PKCS8 key %s", gnutls_strerror(err)); + abort(); + } + } + + return key; +} + + +void testTLSInit(void) +{ + gnutls_global_init(); + + if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) + abort(); + + privkey = testTLSLoadKey(); + if (virFileWriteStr(keyfile, PRIVATE_KEY, 0600) < 0) + abort(); +} + + +void testTLSCleanup(void) +{ + asn1_delete_structure(&pkix_asn1); + unlink(keyfile); +} + +/* + * Turns an ASN1 object into a DER encoded byte array + */ +static void testTLSDerEncode(ASN1_TYPE src, + const char *src_name, + gnutls_datum_t * res) +{ + int size; + char *data = NULL; + + size = 0; + asn1_der_coding(src, src_name, NULL, &size, NULL); + + if (VIR_ALLOC_N(data, size) < 0) + abort(); + + asn1_der_coding(src, src_name, data, &size, NULL); + + res->data = (unsigned char *)data; + res->size = size; +} + + +/* + * This is a fairly lame x509 certificate generator. + * + * Do not copy/use this code for generating real certificates + * since it leaves out many things that you would want in + * certificates for real world usage. + * + * This is good enough only for doing tests of the libvirt + * TLS certificate code + */ +void +testTLSGenerateCert(struct testTLSCertReq *req) +{ + gnutls_x509_crt_t crt; + int err; + static char buffer[1024*1024]; + size_t size = sizeof(buffer); + char serial[5] = { 1, 2, 3, 4, 0 }; + gnutls_datum_t der; + time_t start = time(NULL) + (60*60*req->start_offset); + time_t expire = time(NULL) + (60*60*(req->expire_offset + ? req->expire_offset : 24)); + + /* + * Prepare our new certificate object + */ + if ((err = gnutls_x509_crt_init(&crt)) < 0) { + VIR_WARN("Failed to initialize certificate %s", gnutls_strerror(err)); + abort(); + } + if ((err = gnutls_x509_crt_set_key(crt, privkey)) < 0) { + VIR_WARN("Failed to set certificate key %s", gnutls_strerror(err)); + abort(); + } + + /* + * A v3 certificate is required in order to be able + * set any of the basic constraints, key purpose and + * key usage data + */ + gnutls_x509_crt_set_version(crt, 3); + + if (req->country) { + if ((err = gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, + req->country, strlen(req->country))) < 0) { + VIR_WARN("Failed to set certificate country name %s", gnutls_strerror(err)); + abort(); + } + } + if (req->cn) { + if ((err = gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, + req->cn, strlen(req->cn))) < 0) { + VIR_WARN("Failed to set certificate common name %s", gnutls_strerror(err)); + abort(); + } + } + + /* + * Setup the subject altnames, which are used + * for hostname checks in live sessions + */ + if (req->altname1) { + if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, + req->altname1, + strlen(req->altname1), + GNUTLS_FSAN_APPEND))) { + VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); + abort(); + } + } + if (req->altname2) { + if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, + req->altname2, + strlen(req->altname2), + GNUTLS_FSAN_APPEND))) { + VIR_WARN("Failed to set certificate %s alt name", gnutls_strerror(err)); + abort(); + } + } + + /* + * IP address need to be put into the cert in their + * raw byte form, not strings, hence this is a little + * more complicated + */ + if (req->ipaddr1) { + virSocketAddr addr; + char *data; + int len; + if (virSocketAddrParse(&addr, req->ipaddr1, 0) < 0) { + VIR_WARN("Cannot parse %s", req->ipaddr1); + abort(); + } + + if (addr.data.sa.sa_family == AF_INET) { + data = (char*)&addr.data.inet4.sin_addr; + len = 4; + } else { + data = (char*)&addr.data.inet6.sin6_addr; + len = 16; + } + + if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND))) { + VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); + abort(); + } + } + if (req->ipaddr2) { + virSocketAddr addr; + char *data; + int len; + if (virSocketAddrParse(&addr, req->ipaddr2, 0) < 0) { + VIR_WARN("Cannot parse %s", req->ipaddr2); + abort(); + } + + if (addr.data.sa.sa_family == AF_INET) { + data = (char*)&addr.data.inet4.sin_addr; + len = 4; + } else { + data = (char*)&addr.data.inet6.sin6_addr; + len = 16; + } + + if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND))) { + VIR_WARN("Failed to set certificate alt name %s", gnutls_strerror(err)); + abort(); + } + } + + + /* + * Basic constraints are used to decide if the cert + * is for a CA or not. We can't use the convenient + * gnutls API for setting this, since it hardcodes + * the 'critical' field which we want control over + */ + if (req->basicConstraintsEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); + asn1_write_value(ext, "cA", req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); + asn1_write_value(ext, "pathLenConstraint", NULL, 0); + testTLSDerEncode(ext, "", &der); + if ((err = gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.19", + der.data, + der.size, + req->basicConstraintsCritical)) < 0) { + VIR_WARN("Failed to set certificate basic constraints %s", gnutls_strerror(err)); + VIR_FREE(der.data); + abort(); + } + asn1_delete_structure(&ext); + VIR_FREE(der.data); + } + + /* + * Next up the key usage extension. Again we can't + * use the gnutls API since it hardcodes the extension + * to be 'critical' + */ + if (req->keyUsageEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + char str[2]; + + str[0] = req->keyUsageValue & 0xff; + str[1] = (req->keyUsageValue >> 8) & 0xff; + + asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); + asn1_write_value(ext, "", str, 9); + testTLSDerEncode(ext, "", &der); + if ((err = gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.15", + der.data, + der.size, + req->keyUsageCritical)) < 0) { + VIR_WARN("Failed to set certificate key usage %s", gnutls_strerror(err)); + VIR_FREE(der.data); + abort(); + } + asn1_delete_structure(&ext); + VIR_FREE(der.data); + } + + /* + * Finally the key purpose extension. This time + * gnutls has the opposite problem, always hardcoding + * it to be non-critical. So once again we have to + * set this the hard way building up ASN1 data ourselves + */ + if (req->keyPurposeEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); + if (req->keyPurposeOID1) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); + } + if (req->keyPurposeOID2) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); + } + testTLSDerEncode(ext, "", &der); + if ((err = gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.37", + der.data, + der.size, + req->keyPurposeCritical)) < 0) { + VIR_WARN("Failed to set certificate key purpose %s", gnutls_strerror(err)); + VIR_FREE(der.data); + abort(); + } + asn1_delete_structure(&ext); + VIR_FREE(der.data); + } + + /* + * Any old serial number will do, so lets pick 5 + */ + if ((err = gnutls_x509_crt_set_serial(crt, serial, 5)) < 0) { + VIR_WARN("Failed to set certificate serial %s", gnutls_strerror(err)); + abort(); + } + + if ((err = gnutls_x509_crt_set_activation_time(crt, start)) < 0) { + VIR_WARN("Failed to set certificate activation %s", gnutls_strerror(err)); + abort(); + } + if ((err = gnutls_x509_crt_set_expiration_time(crt, expire)) < 0) { + VIR_WARN("Failed to set certificate expiration %s", gnutls_strerror(err)); + abort(); + } + + + /* + * If no 'cart' is set then we are self signing + * the cert. This is done for CA certs + */ + if ((err = gnutls_x509_crt_sign(crt, req->cacrt ? req->cacrt : crt, privkey) < 0)) { + VIR_WARN("Failed to sign certificate %s", gnutls_strerror(err)); + abort(); + } + + /* + * Finally write the new cert out to disk + */ + if ((err = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) { + VIR_WARN("Failed to export certificate %s", gnutls_strerror(err)); + abort(); + } + + if (virFileWriteStr(req->filename, buffer, 0600) < 0) { + VIR_WARN("Failed to write certificate %s %s", req->filename, gnutls_strerror(err)); + abort(); + } + + req->crt = crt; + return; +} + + +void testTLSDiscardCert(struct testTLSCertReq *req) +{ + if (!req->crt) + return; + + gnutls_x509_crt_deinit(req->crt); + req->crt = NULL; + + if (getenv("VIRT_TEST_DEBUG_CERTS") == NULL) + unlink(req->filename); +} + +#endif diff --git a/tests/virnettlshelpers.h b/tests/virnettlshelpers.h new file mode 100644 index 0000000..3ea9978 --- /dev/null +++ b/tests/virnettlshelpers.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011-2013 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 <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#if !defined WIN32 && HAVE_LIBTASN1_H && LIBGNUTLS_VERSION_NUMBER >= 0x020600 +# include "gnutls_1_0_compat.h" + +# include <libtasn1.h> + +# include "rpc/virnettlscontext.h" + +extern const char *keyfile; + +/* + * This contains parameter about how to generate + * certificates. + */ +struct testTLSCertReq { + gnutls_x509_crt_t crt; + gnutls_x509_crt_t cacrt; /* If not set, then the cert will be self-signed */ + + const char *filename; + + /* Identifying information */ + const char *country; + const char *cn; + const char *altname1; + const char *altname2; + const char *ipaddr1; + const char *ipaddr2; + + /* Basic constraints */ + bool basicConstraintsEnable; + bool basicConstraintsCritical; + bool basicConstraintsIsCA; + + /* Key usage */ + bool keyUsageEnable; + bool keyUsageCritical; + int keyUsageValue; + + /* Key purpose (aka Extended key usage) */ + bool keyPurposeEnable; + bool keyPurposeCritical; + const char *keyPurposeOID1; + const char *keyPurposeOID2; + + /* zero for current time, or non-zero for hours from now */ + int start_offset; + /* zero for 24 hours from now, or non-zero for hours from now */ + int expire_offset; +}; + +void testTLSGenerateCert(struct testTLSCertReq *req); +void testTLSDiscardCert(struct testTLSCertReq *req); + +void testTLSInit(void); +void testTLSCleanup(void); + +#endif diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c new file mode 100644 index 0000000..9c5b3ca --- /dev/null +++ b/tests/virnettlssessiontest.c @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2011-2012 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 <config.h> + +#include <stdlib.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include "testutils.h" +#include "virnettlshelpers.h" +#include "virutil.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "virfile.h" +#include "vircommand.h" +#include "virsocketaddr.h" + +#if !defined WIN32 && HAVE_LIBTASN1_H && LIBGNUTLS_VERSION_NUMBER >= 0x020600 + +# define VIR_FROM_THIS VIR_FROM_RPC + +struct testTLSSessionData { + struct testTLSCertReq careq; + struct testTLSCertReq othercareq; + struct testTLSCertReq serverreq; + struct testTLSCertReq clientreq; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const* wildcards; +}; + + +static ssize_t testWrite(const char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return write(*fd, buf, len); +} + +static ssize_t testRead(char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return read(*fd, buf, len); +} + +/* + * 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 int testTLSSessionInit(const void *opaque) +{ + struct testTLSSessionData *data = (struct testTLSSessionData *)opaque; + virNetTLSContextPtr clientCtxt = NULL; + virNetTLSContextPtr serverCtxt = NULL; + virNetTLSSessionPtr clientSess = NULL; + virNetTLSSessionPtr serverSess = NULL; + int ret = -1; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + + + /* We'll use this for our fake client-server connection */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) < 0) + abort(); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + ignore_value(virSetNonBlock(channel[0])); + ignore_value(virSetNonBlock(channel[1])); + + + /* Generate all the certs we need for this test */ + testTLSGenerateCert(&data->careq); + data->serverreq.cacrt = data->careq.crt; + testTLSGenerateCert(&data->serverreq); + + if (data->othercareq.filename) { + testTLSGenerateCert(&data->othercareq); + data->clientreq.cacrt = data->othercareq.crt; + } else { + data->clientreq.cacrt = data->careq.crt; + } + testTLSGenerateCert(&data->clientreq); + + + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage + */ + serverCtxt = virNetTLSContextNewServer(data->careq.filename, + NULL, + data->serverreq.filename, + keyfile, + data->wildcards, + false, + true); + + clientCtxt = virNetTLSContextNewClient(data->othercareq.filename ? + data->othercareq.filename : + data->careq.filename, + NULL, + data->clientreq.filename, + keyfile, + false, + true); + + if (!serverCtxt) { + VIR_WARN("Unexpected failure loading %s against %s", + data->careq.filename, data->serverreq.filename); + goto cleanup; + } + if (!clientCtxt) { + VIR_WARN("Unexpected failure loading %s against %s", + data->othercareq.filename ? data->othercareq.filename : + data->careq.filename, data->clientreq.filename); + goto cleanup; + } + + + /* Now the real part of the test, setup the sessions */ + serverSess = virNetTLSSessionNew(serverCtxt, NULL); + clientSess = virNetTLSSessionNew(clientCtxt, data->hostname); + + if (!serverSess) { + VIR_WARN("Unexpected failure using %s against %s", + data->careq.filename, data->serverreq.filename); + goto cleanup; + } + if (!clientSess) { + VIR_WARN("Unexpected failure using %s against %s", + data->othercareq.filename ? data->othercareq.filename : + data->careq.filename, data->clientreq.filename); + goto cleanup; + } + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + virNetTLSSessionSetIOCallbacks(serverSess, testWrite, testRead, &channel[0]); + virNetTLSSessionSetIOCallbacks(clientSess, testWrite, testRead, &channel[1]); + + /* + * 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 + */ + do { + int rv; + if (!serverShake) { + rv = virNetTLSSessionHandshake(serverSess); + if (rv < 0) + goto cleanup; + if (rv == VIR_NET_TLS_HANDSHAKE_COMPLETE) + serverShake = true; + } + if (!clientShake) { + rv = virNetTLSSessionHandshake(clientSess); + if (rv < 0) + goto cleanup; + if (rv == VIR_NET_TLS_HANDSHAKE_COMPLETE) + clientShake = true; + } + } while (!clientShake && !serverShake); + + + /* Finally make sure the server validation does what + * we were expecting + */ + if (virNetTLSContextCheckCertificate(serverCtxt, + serverSess) < 0) { + if (!data->expectServerFail) { + VIR_WARN("Unexpected server cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Got expected server cert fail"); + } + } else { + if (data->expectServerFail) { + VIR_WARN("Expected server cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Not unexpected server cert fail"); + } + } + + /* + * And the same for the client validation check + */ + if (virNetTLSContextCheckCertificate(clientCtxt, + clientSess) < 0) { + if (!data->expectClientFail) { + VIR_WARN("Unexpected client cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Got expected client cert fail"); + } + } else { + if (data->expectClientFail) { + VIR_WARN("Expected client cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Not unexpected client cert fail"); + } + } + + ret = 0; + +cleanup: + virObjectUnref(serverCtxt); + virObjectUnref(clientCtxt); + virObjectUnref(serverSess); + virObjectUnref(clientSess); + + testTLSDiscardCert(&data->careq); + if (data->othercareq.filename) + testTLSDiscardCert(&data->othercareq); + testTLSDiscardCert(&data->clientreq); + testTLSDiscardCert(&data->serverreq); + + VIR_FORCE_CLOSE(channel[0]); + VIR_FORCE_CLOSE(channel[1]); + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + + testTLSInit(); + +# define DO_SESS_TEST(_caReq, _serverReq, _clientReq, _expectServerFail,\ + _expectClientFail, _hostname, _wildcards) \ + do { \ + static struct testTLSSessionData data; \ + static struct testTLSCertReq other; \ + data.careq = _caReq; \ + data.othercareq = other; \ + data.serverreq = _serverReq; \ + data.clientreq = _clientReq; \ + data.expectServerFail = _expectServerFail; \ + data.expectClientFail = _expectClientFail; \ + data.hostname = _hostname; \ + data.wildcards = _wildcards; \ + if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + ret = -1; \ + } while (0) + +# define DO_SESS_TEST_EXT(_caReq, _othercaReq, _serverReq, _clientReq, \ + _expectServerFail, _expectClientFail, \ + _hostname, _wildcards) \ + do { \ + static struct testTLSSessionData data; \ + data.careq = _caReq; \ + data.othercareq = _othercaReq; \ + data.serverreq = _serverReq; \ + data.clientreq = _clientReq; \ + data.expectServerFail = _expectServerFail; \ + data.expectClientFail = _expectClientFail; \ + data.hostname = _hostname; \ + data.wildcards = _wildcards; \ + if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + ret = -1; \ + } while (0) + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + static struct testTLSCertReq cacertreq = { + NULL, NULL, "cacert.pem", "UK", + "libvirt CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0, + }; + static struct testTLSCertReq cacert1req = { + NULL, NULL, "cacert1.pem", "UK", + "libvirt CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0, + }; + static struct testTLSCertReq servercertreq = { + NULL, NULL, "servercert.pem", "UK", + "libvirt.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, + }; + static struct testTLSCertReq clientcertreq = { + NULL, NULL, "clientcert.pem", "UK", + "libvirt", 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, + }; + + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", NULL); + DO_SESS_TEST_EXT(cacertreq, cacert1req, servercertreq, clientcertreq, true, true, "libvirt.org", NULL); + + /* When an altname is set, the CN is ignored, so it must be duplicated + * as an altname for it to match */ + static struct testTLSCertReq servercertalt1req = { + NULL, NULL, "servercert.pem", "UK", + "libvirt.org", "www.libvirt.org", "libvirt.org", "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0, + }; + /* This intentionally doesn't replicate */ + static struct testTLSCertReq servercertalt2req = { + NULL, NULL, "servercert.pem", "UK", + "libvirt.org", "www.libvirt.org", "wiki.libvirt.org", "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0, + }; + + DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "libvirt.org", NULL); + DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "www.libvirt.org", NULL); + DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, true, "wiki.libvirt.org", NULL); + + DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, true, "libvirt.org", NULL); + DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "www.libvirt.org", NULL); + DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "wiki.libvirt.org", NULL); + + const char *const wildcards1[] = { + "C=UK,CN=dogfood", + NULL, + }; + const char *const wildcards2[] = { + "C=UK,CN=libvirt", + NULL, + }; + const char *const wildcards3[] = { + "C=UK,CN=dogfood", + "C=UK,CN=libvirt", + NULL, + }; + const char *const wildcards4[] = { + "C=UK,CN=libvirtstuff", + NULL, + }; + const char *const wildcards5[] = { + "C=UK,CN=libvirt*", + NULL, + }; + const char *const wildcards6[] = { + "C=UK,CN=*virt*", + NULL, + }; + + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards1); + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards2); + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards3); + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards4); + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards5); + DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards6); + + testTLSCleanup(); + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) + +#else + +int +main(void) +{ + return EXIT_AM_SKIP; +} + +#endif -- 1.8.3.1

From: "Daniel P. Berrange" <berrange@redhat.com> Currently every test case in the TLS test suite generates the certs fresh. This is a waste of time, since its parameters don't change across test cases. Create certs once in main method. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/virnettlscontexttest.c | 670 +++++++++++++++++++++++-------------------- tests/virnettlshelpers.c | 9 +- tests/virnettlshelpers.h | 4 +- tests/virnettlssessiontest.c | 152 +++++----- 4 files changed, 445 insertions(+), 390 deletions(-) diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index 0a0d31a..a02e724 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c @@ -62,10 +62,6 @@ static int testTLSContextInit(const void *opaque) virNetTLSContextPtr ctxt = NULL; int ret = -1; - testTLSGenerateCert(&data->careq); - data->certreq.cacrt = data->careq.crt; - testTLSGenerateCert(&data->certreq); - if (data->isServer) { ctxt = virNetTLSContextNewServer(data->careq.filename, NULL, @@ -103,8 +99,6 @@ static int testTLSContextInit(const void *opaque) cleanup: virObjectUnref(ctxt); - testTLSDiscardCert(&data->careq); - testTLSDiscardCert(&data->certreq); return ret; } @@ -124,38 +118,54 @@ mymain(void) data.careq = _caReq; \ data.certreq = _certReq; \ data.expectFail = _expectFail; \ - if (virtTestRun("TLS Context", 1, testTLSContextInit, &data) < 0) \ + if (virtTestRun("TLS Context " #_caReq " + " #_certReq, 1, \ + testTLSContextInit, &data) < 0) \ ret = -1; \ } while (0) +# define TLS_CERT_REQ(varname, cavarname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo \ + }; \ + testTLSGenerateCert(&varname, cavarname.crt) + +# define TLS_ROOT_REQ(varname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo \ + }; \ + testTLSGenerateCert(&varname, NULL) + + /* A perfect CA, perfect client & perfect server */ /* Basic:CA:critical */ - static struct testTLSCertReq cacertreq = { - NULL, NULL, "cacert.pem", "UK", - "libvirt CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; - static struct testTLSCertReq servercertreq = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.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, - }; - static struct testTLSCertReq clientcertreq = { - NULL, NULL, "clientcert.pem", "UK", - "libvirt", 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, - }; - + TLS_ROOT_REQ(cacertreq, + "UK", "libvirt 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", "libvirt.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", "libvirt", 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); DO_CTX_TEST(true, cacertreq, servercertreq, false); DO_CTX_TEST(false, cacertreq, clientcertreq, false); @@ -164,249 +174,241 @@ mymain(void) /* Some other CAs which are good */ /* Basic:CA:critical */ - static struct testTLSCertReq cacert1req = { - NULL, NULL, "cacert1.pem", "UK", - "libvirt CA 1", NULL, NULL, NULL, NULL, - true, true, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_ROOT_REQ(cacert1req, + "UK", "libvirt CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert1req, cacert1req, + "UK", "libvirt.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); + /* Basic:CA:not-critical */ - static struct testTLSCertReq cacert2req = { - NULL, NULL, "cacert2.pem", "UK", - "libvirt CA 2", NULL, NULL, NULL, NULL, - true, false, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_ROOT_REQ(cacert2req, + "UK", "libvirt CA 2", NULL, NULL, NULL, NULL, + true, false, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert2req, cacert2req, + "UK", "libvirt.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); + /* Key usage:cert-sign:critical */ - static struct testTLSCertReq cacert3req = { - NULL, NULL, "cacert3.pem", "UK", - "libvirt CA 3", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; - - DO_CTX_TEST(true, cacert1req, servercertreq, false); - DO_CTX_TEST(true, cacert2req, servercertreq, false); - DO_CTX_TEST(true, cacert3req, servercertreq, false); + TLS_ROOT_REQ(cacert3req, + "UK", "libvirt CA 3", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert3req, cacert3req, + "UK", "libvirt.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); + + DO_CTX_TEST(true, cacert1req, servercert1req, false); + DO_CTX_TEST(true, cacert2req, servercert2req, false); + DO_CTX_TEST(true, cacert3req, servercert3req, false); /* Now some bad certs */ /* Key usage:dig-sig:not-critical */ - static struct testTLSCertReq cacert4req = { - NULL, NULL, "cacert4.pem", "UK", - "libvirt CA 4", NULL, NULL, NULL, NULL, - true, true, true, - true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, - false, false, NULL, NULL, - 0, 0, - }; + TLS_ROOT_REQ(cacert4req, + "UK", "libvirt CA 4", NULL, NULL, NULL, NULL, + true, true, true, + true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert4req, cacert4req, + "UK", "libvirt.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); /* no-basic */ - static struct testTLSCertReq cacert5req = { - NULL, NULL, "cacert5.pem", "UK", - "libvirt CA 5", NULL, NULL, NULL, NULL, - false, false, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_ROOT_REQ(cacert5req, + "UK", "libvirt CA 5", NULL, NULL, NULL, NULL, + false, false, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert5req, cacert5req, + "UK", "libvirt.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); /* Key usage:dig-sig:critical */ - static struct testTLSCertReq cacert6req = { - NULL, NULL, "cacert6.pem", "UK", - "libvirt CA 6", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, - false, false, NULL, NULL, - 0, 0, - }; + TLS_ROOT_REQ(cacert6req, + "UK", "libvirt CA 6", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert6req, cacert6req, + "UK", "libvirt.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); /* Technically a CA cert with basic constraints * key purpose == key signing + non-critical should * be rejected. GNUTLS < 3 does not reject it and * we don't anticipate them changing this behaviour */ - DO_CTX_TEST(true, cacert4req, servercertreq, GNUTLS_VERSION_MAJOR >= 3); - DO_CTX_TEST(true, cacert5req, servercertreq, true); - DO_CTX_TEST(true, cacert6req, servercertreq, true); + DO_CTX_TEST(true, cacert4req, servercert4req, GNUTLS_VERSION_MAJOR >= 3); + DO_CTX_TEST(true, cacert5req, servercert5req, true); + DO_CTX_TEST(true, cacert6req, servercert6req, true); /* Various good servers */ /* no usage or purpose */ - static struct testTLSCertReq servercert1req = { - NULL, NULL, "servercert1.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert7req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); /* usage:cert-sign+dig-sig+encipher:critical */ - static struct testTLSCertReq servercert2req = { - NULL, NULL, "servercert2.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert8req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* usage:cert-sign:not-critical */ - static struct testTLSCertReq servercert3req = { - NULL, NULL, "servercert3.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, false, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert9req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* purpose:server:critical */ - static struct testTLSCertReq servercert4req = { - NULL, NULL, "servercert4.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert10req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); /* purpose:server:not-critical */ - static struct testTLSCertReq servercert5req = { - NULL, NULL, "servercert5.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert11req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); /* purpose:client+server:critical */ - static struct testTLSCertReq servercert6req = { - NULL, NULL, "servercert6.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0, - }; + TLS_CERT_REQ(servercert12req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); /* purpose:client+server:not-critical */ - static struct testTLSCertReq servercert7req = { - NULL, NULL, "servercert7.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0, - }; - - DO_CTX_TEST(true, cacertreq, servercert1req, false); - DO_CTX_TEST(true, cacertreq, servercert2req, false); - DO_CTX_TEST(true, cacertreq, servercert3req, false); - DO_CTX_TEST(true, cacertreq, servercert4req, false); - DO_CTX_TEST(true, cacertreq, servercert5req, false); - DO_CTX_TEST(true, cacertreq, servercert6req, false); + TLS_CERT_REQ(servercert13req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + DO_CTX_TEST(true, cacertreq, servercert7req, false); + DO_CTX_TEST(true, cacertreq, servercert8req, false); + DO_CTX_TEST(true, cacertreq, servercert9req, false); + DO_CTX_TEST(true, cacertreq, servercert10req, false); + DO_CTX_TEST(true, cacertreq, servercert11req, false); + DO_CTX_TEST(true, cacertreq, servercert12req, false); + DO_CTX_TEST(true, cacertreq, servercert13req, false); /* Bad servers */ /* usage:cert-sign:critical */ - static struct testTLSCertReq servercert8req = { - NULL, NULL, "servercert8.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert14req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* purpose:client:critical */ - static struct testTLSCertReq servercert9req = { - NULL, NULL, "servercert9.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert15req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); /* usage: none:critical */ - static struct testTLSCertReq servercert10req = { - NULL, NULL, "servercert10.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(servercert16req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); - DO_CTX_TEST(true, cacertreq, servercert8req, true); - DO_CTX_TEST(true, cacertreq, servercert9req, true); - DO_CTX_TEST(true, cacertreq, servercert10req, true); + DO_CTX_TEST(true, cacertreq, servercert14req, true); + DO_CTX_TEST(true, cacertreq, servercert15req, true); + DO_CTX_TEST(true, cacertreq, servercert16req, true); /* Various good clients */ /* no usage or purpose */ - static struct testTLSCertReq clientcert1req = { - NULL, NULL, "clientcert1.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert1req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); /* usage:cert-sign+dig-sig+encipher:critical */ - static struct testTLSCertReq clientcert2req = { - NULL, NULL, "clientcert2.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert2req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* usage:cert-sign:not-critical */ - static struct testTLSCertReq clientcert3req = { - NULL, NULL, "clientcert3.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, false, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert3req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* purpose:client:critical */ - static struct testTLSCertReq clientcert4req = { - NULL, NULL, "clientcert4.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert4req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); /* purpose:client:not-critical */ - static struct testTLSCertReq clientcert5req = { - NULL, NULL, "clientcert5.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert5req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); /* purpose:client+client:critical */ - static struct testTLSCertReq clientcert6req = { - NULL, NULL, "clientcert6.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0, - }; + TLS_CERT_REQ(clientcert6req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); /* purpose:client+client:not-critical */ - static struct testTLSCertReq clientcert7req = { - NULL, NULL, "clientcert7.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0, - }; + TLS_CERT_REQ(clientcert7req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); DO_CTX_TEST(false, cacertreq, clientcert1req, false); DO_CTX_TEST(false, cacertreq, clientcert2req, false); @@ -418,32 +420,26 @@ mymain(void) /* Bad clients */ /* usage:cert-sign:critical */ - static struct testTLSCertReq clientcert8req = { - NULL, NULL, "clientcert8.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert8req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); /* purpose:client:critical */ - static struct testTLSCertReq clientcert9req = { - NULL, NULL, "clientcert9.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert9req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); /* usage: none:critical */ - static struct testTLSCertReq clientcert10req = { - NULL, NULL, "clientcert10.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, false, - true, true, 0, - false, false, NULL, NULL, - 0, 0, - }; + TLS_CERT_REQ(clientcert10req, cacertreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); DO_CTX_TEST(false, cacertreq, clientcert8req, true); DO_CTX_TEST(false, cacertreq, clientcert9req, true); @@ -453,66 +449,114 @@ mymain(void) /* Expired stuff */ - static struct testTLSCertReq cacertexpreq = { - NULL, NULL, "cacert.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, -1, - }; - static struct testTLSCertReq servercertexpreq = { - NULL, NULL, "servercert.pem", "UK", - "libvirt", 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, -1, - }; - static struct testTLSCertReq clientcertexpreq = { - NULL, NULL, "clientcert.pem", "UK", - "libvirt", 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, -1, - }; - - DO_CTX_TEST(true, cacertexpreq, servercertreq, true); - DO_CTX_TEST(true, cacertreq, servercertexpreq, true); - DO_CTX_TEST(false, cacertreq, clientcertexpreq, true); + TLS_ROOT_REQ(cacertexpreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, -1); + TLS_CERT_REQ(servercertexpreq, cacertexpreq, + "UK", "libvirt.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(servercertexp1req, cacertreq, + "UK", "libvirt", 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, -1); + TLS_CERT_REQ(clientcertexp1req, cacertreq, + "UK", "libvirt", 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, -1); + + DO_CTX_TEST(true, cacertexpreq, servercertexpreq, true); + DO_CTX_TEST(true, cacertreq, servercertexp1req, true); + DO_CTX_TEST(false, cacertreq, clientcertexp1req, true); /* Not activated stuff */ - static struct testTLSCertReq cacertnewreq = { - NULL, NULL, "cacert.pem", "UK", - "libvirt", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 1, 2, - }; - static struct testTLSCertReq servercertnewreq = { - NULL, NULL, "servercert.pem", "UK", - "libvirt", 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, - 1, 2, - }; - static struct testTLSCertReq clientcertnewreq = { - NULL, NULL, "clientcert.pem", "UK", - "libvirt", 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, - 1, 2, - }; - - DO_CTX_TEST(true, cacertnewreq, servercertreq, true); - DO_CTX_TEST(true, cacertreq, servercertnewreq, true); - DO_CTX_TEST(false, cacertreq, clientcertnewreq, true); + TLS_ROOT_REQ(cacertnewreq, + "UK", "libvirt", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 1, 2); + TLS_CERT_REQ(servercertnewreq, cacertnewreq, + "UK", "libvirt", 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(servercertnew1req, cacertreq, + "UK", "libvirt", 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, + 1, 2); + TLS_CERT_REQ(clientcertnew1req, cacertreq, + "UK", "libvirt", 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, + 1, 2); + + DO_CTX_TEST(true, cacertnewreq, servercertnewreq, true); + DO_CTX_TEST(true, cacertreq, servercertnew1req, true); + DO_CTX_TEST(false, cacertreq, clientcertnew1req, true); + + testTLSDiscardCert(&cacertreq); + testTLSDiscardCert(&cacert1req); + testTLSDiscardCert(&cacert2req); + testTLSDiscardCert(&cacert3req); + testTLSDiscardCert(&cacert4req); + testTLSDiscardCert(&cacert5req); + testTLSDiscardCert(&cacert6req); + + testTLSDiscardCert(&servercertreq); + testTLSDiscardCert(&servercert1req); + testTLSDiscardCert(&servercert2req); + testTLSDiscardCert(&servercert3req); + testTLSDiscardCert(&servercert4req); + testTLSDiscardCert(&servercert5req); + testTLSDiscardCert(&servercert6req); + testTLSDiscardCert(&servercert7req); + testTLSDiscardCert(&servercert8req); + testTLSDiscardCert(&servercert9req); + testTLSDiscardCert(&servercert10req); + testTLSDiscardCert(&servercert11req); + testTLSDiscardCert(&servercert12req); + testTLSDiscardCert(&servercert13req); + testTLSDiscardCert(&servercert14req); + testTLSDiscardCert(&servercert15req); + testTLSDiscardCert(&servercert16req); + + testTLSDiscardCert(&clientcertreq); + testTLSDiscardCert(&clientcert1req); + testTLSDiscardCert(&clientcert2req); + testTLSDiscardCert(&clientcert3req); + testTLSDiscardCert(&clientcert4req); + testTLSDiscardCert(&clientcert5req); + testTLSDiscardCert(&clientcert6req); + testTLSDiscardCert(&clientcert7req); + testTLSDiscardCert(&clientcert8req); + testTLSDiscardCert(&clientcert9req); + testTLSDiscardCert(&clientcert10req); + + testTLSDiscardCert(&cacertexpreq); + testTLSDiscardCert(&servercertexpreq); + testTLSDiscardCert(&servercertexp1req); + testTLSDiscardCert(&clientcertexp1req); + + testTLSDiscardCert(&cacertnewreq); + testTLSDiscardCert(&servercertnewreq); + testTLSDiscardCert(&servercertnew1req); + testTLSDiscardCert(&clientcertnew1req); testTLSCleanup(); diff --git a/tests/virnettlshelpers.c b/tests/virnettlshelpers.c index 96b2f6e..8236e82 100644 --- a/tests/virnettlshelpers.c +++ b/tests/virnettlshelpers.c @@ -152,7 +152,8 @@ static void testTLSDerEncode(ASN1_TYPE src, * TLS certificate code */ void -testTLSGenerateCert(struct testTLSCertReq *req) +testTLSGenerateCert(struct testTLSCertReq *req, + gnutls_x509_crt_t ca) { gnutls_x509_crt_t crt; int err; @@ -379,10 +380,10 @@ testTLSGenerateCert(struct testTLSCertReq *req) /* - * If no 'cart' is set then we are self signing - * the cert. This is done for CA certs + * If no 'ca' is set then we are self signing + * the cert. This is done for the root CA certs */ - if ((err = gnutls_x509_crt_sign(crt, req->cacrt ? req->cacrt : crt, privkey) < 0)) { + if ((err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey) < 0)) { VIR_WARN("Failed to sign certificate %s", gnutls_strerror(err)); abort(); } diff --git a/tests/virnettlshelpers.h b/tests/virnettlshelpers.h index 3ea9978..50a4ba1 100644 --- a/tests/virnettlshelpers.h +++ b/tests/virnettlshelpers.h @@ -36,7 +36,6 @@ extern const char *keyfile; */ struct testTLSCertReq { gnutls_x509_crt_t crt; - gnutls_x509_crt_t cacrt; /* If not set, then the cert will be self-signed */ const char *filename; @@ -70,7 +69,8 @@ struct testTLSCertReq { int expire_offset; }; -void testTLSGenerateCert(struct testTLSCertReq *req); +void testTLSGenerateCert(struct testTLSCertReq *req, + gnutls_x509_crt_t ca); void testTLSDiscardCert(struct testTLSCertReq *req); void testTLSInit(void); diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c index 9c5b3ca..6c71ac9 100644 --- a/tests/virnettlssessiontest.c +++ b/tests/virnettlssessiontest.c @@ -100,20 +100,6 @@ static int testTLSSessionInit(const void *opaque) ignore_value(virSetNonBlock(channel[1])); - /* Generate all the certs we need for this test */ - testTLSGenerateCert(&data->careq); - data->serverreq.cacrt = data->careq.crt; - testTLSGenerateCert(&data->serverreq); - - if (data->othercareq.filename) { - testTLSGenerateCert(&data->othercareq); - data->clientreq.cacrt = data->othercareq.crt; - } else { - data->clientreq.cacrt = data->careq.crt; - } - testTLSGenerateCert(&data->clientreq); - - /* We skip initial sanity checks here because we * want to make sure that problems are being * detected at the TLS session validation stage @@ -243,12 +229,6 @@ cleanup: virObjectUnref(serverSess); virObjectUnref(clientSess); - testTLSDiscardCert(&data->careq); - if (data->othercareq.filename) - testTLSDiscardCert(&data->othercareq); - testTLSDiscardCert(&data->clientreq); - testTLSDiscardCert(&data->serverreq); - VIR_FORCE_CLOSE(channel[0]); VIR_FORCE_CLOSE(channel[1]); return ret; @@ -275,7 +255,8 @@ mymain(void) data.expectClientFail = _expectClientFail; \ data.hostname = _hostname; \ data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + if (virtTestRun("TLS Session " #_serverReq " + " #_clientReq, \ + 1, testTLSSessionInit, &data) < 0) \ ret = -1; \ } while (0) @@ -292,68 +273,87 @@ mymain(void) data.expectClientFail = _expectClientFail; \ data.hostname = _hostname; \ data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + if (virtTestRun("TLS Session " #_serverReq " + " #_clientReq, \ + 1, testTLSSessionInit, &data) < 0) \ ret = -1; \ } while (0) +# define TLS_CERT_REQ(varname, cavarname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \ + }; \ + testTLSGenerateCert(&varname, cavarname.crt) + +# define TLS_ROOT_REQ(varname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \ + }; \ + testTLSGenerateCert(&varname, NULL) + /* A perfect CA, perfect client & perfect server */ /* Basic:CA:critical */ - static struct testTLSCertReq cacertreq = { - NULL, NULL, "cacert.pem", "UK", - "libvirt CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; - static struct testTLSCertReq cacert1req = { - NULL, NULL, "cacert1.pem", "UK", - "libvirt CA 1", NULL, NULL, NULL, NULL, - true, true, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; - static struct testTLSCertReq servercertreq = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.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, - }; - static struct testTLSCertReq clientcertreq = { - NULL, NULL, "clientcert.pem", "UK", - "libvirt", 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, - }; + TLS_ROOT_REQ(cacertreq, + "UK", "libvirt CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_ROOT_REQ(altcacertreq, + "UK", "libvirt CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "libvirt.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", "libvirt", 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); + + TLS_CERT_REQ(clientcertaltreq, altcacertreq, + "UK", "libvirt", 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); DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", NULL); - DO_SESS_TEST_EXT(cacertreq, cacert1req, servercertreq, clientcertreq, true, true, "libvirt.org", NULL); + DO_SESS_TEST_EXT(cacertreq, altcacertreq, servercertreq, clientcertaltreq, true, true, "libvirt.org", NULL); + /* When an altname is set, the CN is ignored, so it must be duplicated * as an altname for it to match */ - static struct testTLSCertReq servercertalt1req = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.org", "www.libvirt.org", "libvirt.org", "192.168.122.1", "fec0::dead:beaf", - 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(servercertalt1req, cacertreq, + "UK", "libvirt.org", "www.libvirt.org", "libvirt.org", "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); /* This intentionally doesn't replicate */ - static struct testTLSCertReq servercertalt2req = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.org", "www.libvirt.org", "wiki.libvirt.org", "192.168.122.1", "fec0::dead:beaf", - 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(servercertalt2req, cacertreq, + "UK", "libvirt.org", "www.libvirt.org", "wiki.libvirt.org", "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "libvirt.org", NULL); DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "www.libvirt.org", NULL); @@ -396,6 +396,16 @@ mymain(void) DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards5); DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards6); + testTLSDiscardCert(&clientcertreq); + testTLSDiscardCert(&clientcertaltreq); + + testTLSDiscardCert(&servercertreq); + testTLSDiscardCert(&servercertalt1req); + testTLSDiscardCert(&servercertalt2req); + + testTLSDiscardCert(&cacertreq); + testTLSDiscardCert(&altcacertreq); + testTLSCleanup(); return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 1.8.3.1

On 06.08.2013 13:35, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Currently every test case in the TLS test suite generates the certs fresh. This is a waste of time, since its parameters don't change across test cases. Create certs once in main method.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/virnettlscontexttest.c | 670 +++++++++++++++++++++++-------------------- tests/virnettlshelpers.c | 9 +- tests/virnettlshelpers.h | 4 +- tests/virnettlssessiontest.c | 152 +++++----- 4 files changed, 445 insertions(+), 390 deletions(-)
diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index 0a0d31a..a02e724 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c
@@ -124,38 +118,54 @@ mymain(void) data.careq = _caReq; \ data.certreq = _certReq; \ data.expectFail = _expectFail; \ - if (virtTestRun("TLS Context", 1, testTLSContextInit, &data) < 0) \ + if (virtTestRun("TLS Context " #_caReq " + " #_certReq, 1, \ + testTLSContextInit, &data) < 0) \ ret = -1; \ } while (0)
+# define TLS_CERT_REQ(varname, cavarname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo \ + }; \ + testTLSGenerateCert(&varname, cavarname.crt) +
Trailing whitespace
+# define TLS_ROOT_REQ(varname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo \ + }; \ + testTLSGenerateCert(&varname, NULL) + +
diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c index 9c5b3ca..6c71ac9 100644 --- a/tests/virnettlssessiontest.c +++ b/tests/virnettlssessiontest.c
@@ -292,68 +273,87 @@ mymain(void) data.expectClientFail = _expectClientFail; \ data.hostname = _hostname; \ data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + if (virtTestRun("TLS Session " #_serverReq " + " #_clientReq, \ + 1, testTLSSessionInit, &data) < 0) \ ret = -1; \ } while (0)
+# define TLS_CERT_REQ(varname, cavarname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \ + }; \ + testTLSGenerateCert(&varname, cavarname.crt) +
Trailing whitespace
+# define TLS_ROOT_REQ(varname, \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \ + static struct testTLSCertReq varname = { \ + NULL, #varname ".pem", \ + co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \ + kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \ + }; \ + testTLSGenerateCert(&varname, NULL) + /* A perfect CA, perfect client & perfect server */
/* Basic:CA:critical */ - static struct testTLSCertReq cacertreq = { - NULL, NULL, "cacert.pem", "UK", - "libvirt CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0, - }; - static struct testTLSCertReq cacert1req = { - NULL, NULL, "cacert1.pem", "UK", - "libvirt CA 1", NULL, NULL, NULL, NULL, - true, true, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0, - }; - static struct testTLSCertReq servercertreq = { - NULL, NULL, "servercert.pem", "UK", - "libvirt.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, - }; - static struct testTLSCertReq clientcertreq = { - NULL, NULL, "clientcert.pem", "UK", - "libvirt", 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, - }; + TLS_ROOT_REQ(cacertreq,
Trailing whitespace
+ "UK", "libvirt CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0);
Michal

From: "Daniel P. Berrange" <berrange@redhat.com> Currently a 'struct testTLSCertReq' instance is passed into the TLS test cases. This is not flexible enough to cope with certificate chains, where one file now corresponds to multiple certificates. Change the test cases so that we pass in filenames instead. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/virnettlscontexttest.c | 92 +++++++++++++++++++++--------------------- tests/virnettlssessiontest.c | 95 ++++++++++++++++++++++++-------------------- 2 files changed, 98 insertions(+), 89 deletions(-) diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index a02e724..977a095 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c @@ -42,8 +42,8 @@ struct testTLSContextData { bool isServer; - struct testTLSCertReq careq; - struct testTLSCertReq certreq; + const char *cacrt; + const char *crt; bool expectFail; }; @@ -63,17 +63,17 @@ static int testTLSContextInit(const void *opaque) int ret = -1; if (data->isServer) { - ctxt = virNetTLSContextNewServer(data->careq.filename, + ctxt = virNetTLSContextNewServer(data->cacrt, NULL, - data->certreq.filename, + data->crt, keyfile, NULL, true, true); } else { - ctxt = virNetTLSContextNewClient(data->careq.filename, + ctxt = virNetTLSContextNewClient(data->cacrt, NULL, - data->certreq.filename, + data->crt, keyfile, true, true); @@ -82,14 +82,14 @@ static int testTLSContextInit(const void *opaque) if (ctxt) { if (data->expectFail) { VIR_WARN("Expected failure %s against %s", - data->careq.filename, data->certreq.filename); + data->cacrt, data->crt); goto cleanup; } } else { virErrorPtr err = virGetLastError(); if (!data->expectFail) { VIR_WARN("Unexpected failure %s against %s", - data->careq.filename, data->certreq.filename); + data->cacrt, data->crt); goto cleanup; } VIR_DEBUG("Got error %s", err ? err->message : "<unknown>"); @@ -111,14 +111,14 @@ mymain(void) testTLSInit(); -# define DO_CTX_TEST(_isServer, _caReq, _certReq, _expectFail) \ +# define DO_CTX_TEST(_isServer, _caCrt, _crt, _expectFail) \ do { \ static struct testTLSContextData data; \ data.isServer = _isServer; \ - data.careq = _caReq; \ - data.certreq = _certReq; \ + data.cacrt = _caCrt; \ + data.crt = _crt; \ data.expectFail = _expectFail; \ - if (virtTestRun("TLS Context " #_caReq " + " #_certReq, 1, \ + if (virtTestRun("TLS Context " #_caCrt " + " #_crt, 1, \ testTLSContextInit, &data) < 0) \ ret = -1; \ } while (0) @@ -167,8 +167,8 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 0, 0); - DO_CTX_TEST(true, cacertreq, servercertreq, false); - DO_CTX_TEST(false, cacertreq, clientcertreq, false); + DO_CTX_TEST(true, cacertreq.filename, servercertreq.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcertreq.filename, false); /* Some other CAs which are good */ @@ -215,9 +215,9 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 0, 0); - DO_CTX_TEST(true, cacert1req, servercert1req, false); - DO_CTX_TEST(true, cacert2req, servercert2req, false); - DO_CTX_TEST(true, cacert3req, servercert3req, false); + DO_CTX_TEST(true, cacert1req.filename, servercert1req.filename, false); + DO_CTX_TEST(true, cacert2req.filename, servercert2req.filename, false); + DO_CTX_TEST(true, cacert3req.filename, servercert3req.filename, false); /* Now some bad certs */ @@ -266,9 +266,9 @@ mymain(void) * be rejected. GNUTLS < 3 does not reject it and * we don't anticipate them changing this behaviour */ - DO_CTX_TEST(true, cacert4req, servercert4req, GNUTLS_VERSION_MAJOR >= 3); - DO_CTX_TEST(true, cacert5req, servercert5req, true); - DO_CTX_TEST(true, cacert6req, servercert6req, true); + DO_CTX_TEST(true, cacert4req.filename, servercert4req.filename, GNUTLS_VERSION_MAJOR >= 3); + DO_CTX_TEST(true, cacert5req.filename, servercert5req.filename, true); + DO_CTX_TEST(true, cacert6req.filename, servercert6req.filename, true); /* Various good servers */ @@ -322,13 +322,13 @@ mymain(void) true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, 0, 0); - DO_CTX_TEST(true, cacertreq, servercert7req, false); - DO_CTX_TEST(true, cacertreq, servercert8req, false); - DO_CTX_TEST(true, cacertreq, servercert9req, false); - DO_CTX_TEST(true, cacertreq, servercert10req, false); - DO_CTX_TEST(true, cacertreq, servercert11req, false); - DO_CTX_TEST(true, cacertreq, servercert12req, false); - DO_CTX_TEST(true, cacertreq, servercert13req, false); + DO_CTX_TEST(true, cacertreq.filename, servercert7req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert8req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert9req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert10req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert11req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert12req.filename, false); + DO_CTX_TEST(true, cacertreq.filename, servercert13req.filename, false); /* Bad servers */ /* usage:cert-sign:critical */ @@ -353,9 +353,9 @@ mymain(void) false, false, NULL, NULL, 0, 0); - DO_CTX_TEST(true, cacertreq, servercert14req, true); - DO_CTX_TEST(true, cacertreq, servercert15req, true); - DO_CTX_TEST(true, cacertreq, servercert16req, true); + DO_CTX_TEST(true, cacertreq.filename, servercert14req.filename, true); + DO_CTX_TEST(true, cacertreq.filename, servercert15req.filename, true); + DO_CTX_TEST(true, cacertreq.filename, servercert16req.filename, true); @@ -410,13 +410,13 @@ mymain(void) true, false, GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, 0, 0); - DO_CTX_TEST(false, cacertreq, clientcert1req, false); - DO_CTX_TEST(false, cacertreq, clientcert2req, false); - DO_CTX_TEST(false, cacertreq, clientcert3req, false); - DO_CTX_TEST(false, cacertreq, clientcert4req, false); - DO_CTX_TEST(false, cacertreq, clientcert5req, false); - DO_CTX_TEST(false, cacertreq, clientcert6req, false); - DO_CTX_TEST(false, cacertreq, clientcert7req, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert1req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert2req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert3req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert4req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert5req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert6req.filename, false); + DO_CTX_TEST(false, cacertreq.filename, clientcert7req.filename, false); /* Bad clients */ /* usage:cert-sign:critical */ @@ -441,9 +441,9 @@ mymain(void) false, false, NULL, NULL, 0, 0); - DO_CTX_TEST(false, cacertreq, clientcert8req, true); - DO_CTX_TEST(false, cacertreq, clientcert9req, true); - DO_CTX_TEST(false, cacertreq, clientcert10req, true); + DO_CTX_TEST(false, cacertreq.filename, clientcert8req.filename, true); + DO_CTX_TEST(false, cacertreq.filename, clientcert9req.filename, true); + DO_CTX_TEST(false, cacertreq.filename, clientcert10req.filename, true); @@ -474,9 +474,9 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 0, -1); - DO_CTX_TEST(true, cacertexpreq, servercertexpreq, true); - DO_CTX_TEST(true, cacertreq, servercertexp1req, true); - DO_CTX_TEST(false, cacertreq, clientcertexp1req, true); + DO_CTX_TEST(true, cacertexpreq.filename, servercertexpreq.filename, true); + DO_CTX_TEST(true, cacertreq.filename, servercertexp1req.filename, true); + DO_CTX_TEST(false, cacertreq.filename, clientcertexp1req.filename, true); /* Not activated stuff */ @@ -506,9 +506,9 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 1, 2); - DO_CTX_TEST(true, cacertnewreq, servercertnewreq, true); - DO_CTX_TEST(true, cacertreq, servercertnew1req, true); - DO_CTX_TEST(false, cacertreq, clientcertnew1req, true); + DO_CTX_TEST(true, cacertnewreq.filename, servercertnewreq.filename, true); + DO_CTX_TEST(true, cacertreq.filename, servercertnew1req.filename, true); + DO_CTX_TEST(false, cacertreq.filename, clientcertnew1req.filename, true); testTLSDiscardCert(&cacertreq); testTLSDiscardCert(&cacert1req); diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c index 6c71ac9..66df682 100644 --- a/tests/virnettlssessiontest.c +++ b/tests/virnettlssessiontest.c @@ -39,10 +39,10 @@ # define VIR_FROM_THIS VIR_FROM_RPC struct testTLSSessionData { - struct testTLSCertReq careq; - struct testTLSCertReq othercareq; - struct testTLSCertReq serverreq; - struct testTLSCertReq clientreq; + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; bool expectServerFail; bool expectClientFail; const char *hostname; @@ -104,32 +104,29 @@ static int testTLSSessionInit(const void *opaque) * want to make sure that problems are being * detected at the TLS session validation stage */ - serverCtxt = virNetTLSContextNewServer(data->careq.filename, + serverCtxt = virNetTLSContextNewServer(data->servercacrt, NULL, - data->serverreq.filename, + data->servercrt, keyfile, data->wildcards, false, true); - clientCtxt = virNetTLSContextNewClient(data->othercareq.filename ? - data->othercareq.filename : - data->careq.filename, + clientCtxt = virNetTLSContextNewClient(data->clientcacrt, NULL, - data->clientreq.filename, + data->clientcrt, keyfile, false, true); if (!serverCtxt) { VIR_WARN("Unexpected failure loading %s against %s", - data->careq.filename, data->serverreq.filename); + data->servercacrt, data->servercrt); goto cleanup; } if (!clientCtxt) { VIR_WARN("Unexpected failure loading %s against %s", - data->othercareq.filename ? data->othercareq.filename : - data->careq.filename, data->clientreq.filename); + data->clientcacrt, data->clientcrt); goto cleanup; } @@ -140,13 +137,12 @@ static int testTLSSessionInit(const void *opaque) if (!serverSess) { VIR_WARN("Unexpected failure using %s against %s", - data->careq.filename, data->serverreq.filename); + data->servercacrt, data->servercrt); goto cleanup; } if (!clientSess) { VIR_WARN("Unexpected failure using %s against %s", - data->othercareq.filename ? data->othercareq.filename : - data->careq.filename, data->clientreq.filename); + data->clientcacrt, data->clientcrt); goto cleanup; } @@ -242,38 +238,37 @@ mymain(void) testTLSInit(); -# define DO_SESS_TEST(_caReq, _serverReq, _clientReq, _expectServerFail,\ +# define DO_SESS_TEST(_caCrt, _serverCrt, _clientCrt, _expectServerFail, \ _expectClientFail, _hostname, _wildcards) \ do { \ static struct testTLSSessionData data; \ - static struct testTLSCertReq other; \ - data.careq = _caReq; \ - data.othercareq = other; \ - data.serverreq = _serverReq; \ - data.clientreq = _clientReq; \ + data.servercacrt = _caCrt; \ + data.clientcacrt = _caCrt; \ + data.servercrt = _serverCrt; \ + data.clientcrt = _clientCrt; \ data.expectServerFail = _expectServerFail; \ data.expectClientFail = _expectClientFail; \ data.hostname = _hostname; \ data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session " #_serverReq " + " #_clientReq, \ + if (virtTestRun("TLS Session " #_serverCrt " + " #_clientCrt, \ 1, testTLSSessionInit, &data) < 0) \ ret = -1; \ } while (0) -# define DO_SESS_TEST_EXT(_caReq, _othercaReq, _serverReq, _clientReq, \ +# define DO_SESS_TEST_EXT(_serverCaCrt, _clientCaCrt, _serverCrt, _clientCrt, \ _expectServerFail, _expectClientFail, \ _hostname, _wildcards) \ do { \ static struct testTLSSessionData data; \ - data.careq = _caReq; \ - data.othercareq = _othercaReq; \ - data.serverreq = _serverReq; \ - data.clientreq = _clientReq; \ + data.servercacrt = _serverCaCrt; \ + data.clientcacrt = _clientCaCrt; \ + data.servercrt = _serverCrt; \ + data.clientcrt = _clientCrt; \ data.expectServerFail = _expectServerFail; \ data.expectClientFail = _expectClientFail; \ data.hostname = _hostname; \ data.wildcards = _wildcards; \ - if (virtTestRun("TLS Session " #_serverReq " + " #_clientReq, \ + if (virtTestRun("TLS Session " #_serverCrt " + " #_clientCrt, \ 1, testTLSSessionInit, &data) < 0) \ ret = -1; \ } while (0) @@ -335,8 +330,10 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, 0, 0); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", NULL); - DO_SESS_TEST_EXT(cacertreq, altcacertreq, servercertreq, clientcertaltreq, true, true, "libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + false, false, "libvirt.org", NULL); + DO_SESS_TEST_EXT(cacertreq.filename, altcacertreq.filename, servercertreq.filename, + clientcertaltreq.filename, true, true, "libvirt.org", NULL); /* When an altname is set, the CN is ignored, so it must be duplicated @@ -355,13 +352,19 @@ mymain(void) true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, 0, 0); - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, false, "www.libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt1req, clientcertreq, false, true, "wiki.libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt1req.filename, clientcertreq.filename, + false, false, "libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt1req.filename, clientcertreq.filename, + false, false, "www.libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt1req.filename, clientcertreq.filename, + false, true, "wiki.libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, true, "libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "www.libvirt.org", NULL); - DO_SESS_TEST(cacertreq, servercertalt2req, clientcertreq, false, false, "wiki.libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt2req.filename, clientcertreq.filename, + false, true, "libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt2req.filename, clientcertreq.filename, + false, false, "www.libvirt.org", NULL); + DO_SESS_TEST(cacertreq.filename, servercertalt2req.filename, clientcertreq.filename, + false, false, "wiki.libvirt.org", NULL); const char *const wildcards1[] = { "C=UK,CN=dogfood", @@ -389,12 +392,18 @@ mymain(void) NULL, }; - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards1); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards2); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards3); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, true, false, "libvirt.org", wildcards4); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards5); - DO_SESS_TEST(cacertreq, servercertreq, clientcertreq, false, false, "libvirt.org", wildcards6); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + true, false, "libvirt.org", wildcards1); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + false, false, "libvirt.org", wildcards2); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + false, false, "libvirt.org", wildcards3); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + true, false, "libvirt.org", wildcards4); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + false, false, "libvirt.org", wildcards5); + DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, + false, false, "libvirt.org", wildcards6); testTLSDiscardCert(&clientcertreq); testTLSDiscardCert(&clientcertaltreq); -- 1.8.3.1

From: "Daniel P. Berrange" <berrange@redhat.com> The code added to validate CA certificates did not take into account the possibility that the cacert.pem file can contain multiple (concatenated) cert data blocks. Extend the code for loading CA certs to use the gnutls APIs for loading cert lists. Add test cases to check that multi-level trees of certs will validate correctly. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnettlscontext.c | 73 ++++++++++++++++++++++++++++++++++---------- tests/virnettlscontexttest.c | 59 +++++++++++++++++++++++++++++++++++ tests/virnettlshelpers.c | 34 +++++++++++++++++++++ tests/virnettlshelpers.h | 3 ++ tests/virnettlssessiontest.c | 63 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 214 insertions(+), 18 deletions(-) diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index af0ec21..55fb7d0 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -455,14 +455,15 @@ static int virNetTLSContextCheckCert(gnutls_x509_crt_t cert, static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert, const char *certFile, - gnutls_x509_crt_t cacert, + gnutls_x509_crt_t *cacerts, + size_t ncacerts, const char *cacertFile, bool isServer) { unsigned int status; if (gnutls_x509_crt_list_verify(&cert, 1, - &cacert, 1, + cacerts, ncacerts, NULL, 0, 0, &status) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? @@ -500,16 +501,15 @@ static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert, static gnutls_x509_crt_t virNetTLSContextLoadCertFromFile(const char *certFile, - bool isServer, - bool isCA ATTRIBUTE_UNUSED) + bool isServer) { gnutls_datum_t data; gnutls_x509_crt_t cert = NULL; char *buf = NULL; int ret = -1; - VIR_DEBUG("isServer %d isCA %d certFile %s", - isServer, isCA, certFile); + VIR_DEBUG("isServer %d certFile %s", + isServer, certFile); if (gnutls_x509_crt_init(&cert) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, "%s", @@ -543,40 +543,81 @@ cleanup: } +static int virNetTLSContextLoadCACertListFromFile(const char *certFile, + gnutls_x509_crt_t *certs, + size_t *ncerts) +{ + gnutls_datum_t data; + char *buf = NULL; + int ret = -1; + unsigned int certMax = *ncerts; + + *ncerts = 0; + VIR_DEBUG("certFile %s", certFile); + + if (virFileReadAll(certFile, (1<<16), &buf) < 0) + goto cleanup; + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to import CA certificate list %s"), + certFile); + goto cleanup; + } + *ncerts = certMax; + + ret = 0; + +cleanup: + VIR_FREE(buf); + return ret; +} + + +#define MAX_CERTS 16 static int virNetTLSContextSanityCheckCredentials(bool isServer, const char *cacertFile, const char *certFile) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS]; + size_t ncacerts = MAX_CERTS; + size_t i; int ret = -1; if ((access(certFile, R_OK) == 0) && - !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer, false))) + !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer))) goto cleanup; if ((access(cacertFile, R_OK) == 0) && - !(cacert = virNetTLSContextLoadCertFromFile(cacertFile, isServer, false))) + virNetTLSContextLoadCACertListFromFile(cacertFile, cacerts, &ncacerts) < 0) goto cleanup; if (cert && virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) goto cleanup; - if (cacert && - virNetTLSContextCheckCert(cacert, cacertFile, isServer, true) < 0) - goto cleanup; + for (i = 0 ; i < ncacerts ; i++) { + if (virNetTLSContextCheckCert(cacerts[i], cacertFile, isServer, true) < 0) + goto cleanup; + } - if (cert && cacert && - virNetTLSContextCheckCertPair(cert, certFile, cacert, cacertFile, isServer) < 0) + VIR_DEBUG("Here"); + if (cert && ncacerts && + virNetTLSContextCheckCertPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0) { + VIR_DEBUG("there"); goto cleanup; + } ret = 0; cleanup: if (cert) gnutls_x509_crt_deinit(cert); - if (cacert) - gnutls_x509_crt_deinit(cacert); + for (i = 0 ; i < ncacerts ; i++) + gnutls_x509_crt_deinit(cacerts[i]); return ret; } diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index 977a095..234922f 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c @@ -510,6 +510,57 @@ mymain(void) DO_CTX_TEST(true, cacertreq.filename, servercertnew1req.filename, true); DO_CTX_TEST(false, cacertreq.filename, clientcertnew1req.filename, true); + TLS_ROOT_REQ(cacertrootreq, + "UK", "libvirt root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "libvirt level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "libvirt level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "libvirt level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "libvirt.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(clientcertlevel2breq, cacertlevel1breq, + "UK", "libvirt client level 2b", 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); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + testTLSWriteCertChain("cacertchain.pem", + certchain, + ARRAY_CARDINALITY(certchain)); + + DO_CTX_TEST(true, "cacertchain.pem", servercertlevel3areq.filename, false); + DO_CTX_TEST(false, "cacertchain.pem", clientcertlevel2breq.filename, false); + testTLSDiscardCert(&cacertreq); testTLSDiscardCert(&cacert1req); testTLSDiscardCert(&cacert2req); @@ -558,6 +609,14 @@ mymain(void) testTLSDiscardCert(&servercertnew1req); testTLSDiscardCert(&clientcertnew1req); + testTLSDiscardCert(&cacertrootreq); + testTLSDiscardCert(&cacertlevel1areq); + testTLSDiscardCert(&cacertlevel1breq); + testTLSDiscardCert(&cacertlevel2areq); + testTLSDiscardCert(&servercertlevel3areq); + testTLSDiscardCert(&clientcertlevel2breq); + unlink("cacertchain.pem"); + testTLSCleanup(); return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/tests/virnettlshelpers.c b/tests/virnettlshelpers.c index 8236e82..baf043a 100644 --- a/tests/virnettlshelpers.c +++ b/tests/virnettlshelpers.c @@ -406,6 +406,40 @@ testTLSGenerateCert(struct testTLSCertReq *req, } +void testTLSWriteCertChain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + int fd; + int err; + static char buffer[1024*1024]; + size_t size; + + if ((fd = open(filename, O_WRONLY|O_CREAT, 0600)) < 0) { + VIR_WARN("Failed to open %s", filename); + abort(); + } + + for (i = 0 ; i < ncerts ; i++) { + size = sizeof(buffer); + if ((err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) { + VIR_WARN("Failed to export certificate %s", gnutls_strerror(err)); + unlink(filename); + abort(); + } + + if (safewrite(fd, buffer, size) != size) { + VIR_WARN("Failed to write certificate to %s", filename); + unlink(filename); + abort(); + } + } + + VIR_FORCE_CLOSE(fd); +} + + void testTLSDiscardCert(struct testTLSCertReq *req) { if (!req->crt) diff --git a/tests/virnettlshelpers.h b/tests/virnettlshelpers.h index 50a4ba1..7c3f8da 100644 --- a/tests/virnettlshelpers.h +++ b/tests/virnettlshelpers.h @@ -71,6 +71,9 @@ struct testTLSCertReq { void testTLSGenerateCert(struct testTLSCertReq *req, gnutls_x509_crt_t ca); +void testTLSWriteCertChain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts); void testTLSDiscardCert(struct testTLSCertReq *req); void testTLSInit(void); diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c index 66df682..c2d34bb 100644 --- a/tests/virnettlssessiontest.c +++ b/tests/virnettlssessiontest.c @@ -193,7 +193,7 @@ static int testTLSSessionInit(const void *opaque) VIR_WARN("Expected server cert check fail"); goto cleanup; } else { - VIR_DEBUG("Not unexpected server cert fail"); + VIR_DEBUG("No unexpected server cert fail"); } } @@ -213,7 +213,7 @@ static int testTLSSessionInit(const void *opaque) VIR_WARN("Expected client cert check fail"); goto cleanup; } else { - VIR_DEBUG("Not unexpected client cert fail"); + VIR_DEBUG("No unexpected client cert fail"); } } @@ -405,6 +405,57 @@ mymain(void) DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, false, false, "libvirt.org", wildcards6); + TLS_ROOT_REQ(cacertrootreq, + "UK", "libvirt root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "libvirt level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "libvirt level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "libvirt level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "libvirt.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(clientcertlevel2breq, cacertlevel1breq, + "UK", "libvirt client level 2b", 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); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + testTLSWriteCertChain("cacertchain.pem", + certchain, + ARRAY_CARDINALITY(certchain)); + + DO_SESS_TEST("cacertchain.pem", servercertlevel3areq.filename, clientcertlevel2breq.filename, + false, false, "libvirt.org", NULL); + testTLSDiscardCert(&clientcertreq); testTLSDiscardCert(&clientcertaltreq); @@ -415,6 +466,14 @@ mymain(void) testTLSDiscardCert(&cacertreq); testTLSDiscardCert(&altcacertreq); + testTLSDiscardCert(&cacertrootreq); + testTLSDiscardCert(&cacertlevel1areq); + testTLSDiscardCert(&cacertlevel1breq); + testTLSDiscardCert(&cacertlevel2areq); + testTLSDiscardCert(&servercertlevel3areq); + testTLSDiscardCert(&clientcertlevel2breq); + unlink("cacertchain.pem"); + testTLSCleanup(); return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 1.8.3.1

On 06.08.2013 13:35, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The code added to validate CA certificates did not take into account the possibility that the cacert.pem file can contain multiple (concatenated) cert data blocks. Extend the code for loading CA certs to use the gnutls APIs for loading cert lists. Add test cases to check that multi-level trees of certs will validate correctly.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnettlscontext.c | 73 ++++++++++++++++++++++++++++++++++---------- tests/virnettlscontexttest.c | 59 +++++++++++++++++++++++++++++++++++ tests/virnettlshelpers.c | 34 +++++++++++++++++++++ tests/virnettlshelpers.h | 3 ++ tests/virnettlssessiontest.c | 63 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 214 insertions(+), 18 deletions(-)
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index af0ec21..55fb7d0 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c
+#define MAX_CERTS 16 static int virNetTLSContextSanityCheckCredentials(bool isServer, const char *cacertFile, const char *certFile) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS]; + size_t ncacerts = MAX_CERTS; + size_t i; int ret = -1;
if ((access(certFile, R_OK) == 0) && - !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer, false))) + !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer))) goto cleanup; if ((access(cacertFile, R_OK) == 0) && - !(cacert = virNetTLSContextLoadCertFromFile(cacertFile, isServer, false))) + virNetTLSContextLoadCACertListFromFile(cacertFile, cacerts, &ncacerts) < 0) goto cleanup;
if (cert && virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) goto cleanup;
- if (cacert && - virNetTLSContextCheckCert(cacert, cacertFile, isServer, true) < 0) - goto cleanup; + for (i = 0 ; i < ncacerts ; i++) {
Spacing
+ if (virNetTLSContextCheckCert(cacerts[i], cacertFile, isServer, true) < 0) + goto cleanup; + }
- if (cert && cacert && - virNetTLSContextCheckCertPair(cert, certFile, cacert, cacertFile, isServer) < 0) + VIR_DEBUG("Here"); + if (cert && ncacerts && + virNetTLSContextCheckCertPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0) { + VIR_DEBUG("there"); goto cleanup; + }
ret = 0;
cleanup: if (cert) gnutls_x509_crt_deinit(cert); - if (cacert) - gnutls_x509_crt_deinit(cacert); + for (i = 0 ; i < ncacerts ; i++)
Spacing
+ gnutls_x509_crt_deinit(cacerts[i]); return ret; }
diff --git a/tests/virnettlshelpers.c b/tests/virnettlshelpers.c index 8236e82..baf043a 100644 --- a/tests/virnettlshelpers.c +++ b/tests/virnettlshelpers.c @@ -406,6 +406,40 @@ testTLSGenerateCert(struct testTLSCertReq *req, }
+void testTLSWriteCertChain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + int fd; + int err; + static char buffer[1024*1024]; + size_t size; + + if ((fd = open(filename, O_WRONLY|O_CREAT, 0600)) < 0) { + VIR_WARN("Failed to open %s", filename); + abort(); + } + + for (i = 0 ; i < ncerts ; i++) {
Spacing
+ size = sizeof(buffer); + if ((err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) { + VIR_WARN("Failed to export certificate %s", gnutls_strerror(err)); + unlink(filename); + abort(); + } +
Michal

On Tue, Aug 6, 2013 at 6:35 AM, Daniel P. Berrange <berrange@redhat.com> wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The code added to validate CA certificates did not take into account the possibility that the cacert.pem file can contain multiple (concatenated) cert data blocks. Extend the code for loading CA certs to use the gnutls APIs for loading cert lists. Add test cases to check that multi-level trees of certs will validate correctly.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnettlscontext.c | 73 ++++++++++++++++++++++++++++++++++---------- tests/virnettlscontexttest.c | 59 +++++++++++++++++++++++++++++++++++ tests/virnettlshelpers.c | 34 +++++++++++++++++++++ tests/virnettlshelpers.h | 3 ++ tests/virnettlssessiontest.c | 63 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 214 insertions(+), 18 deletions(-)
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index af0ec21..55fb7d0 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -455,14 +455,15 @@ static int virNetTLSContextCheckCert(gnutls_x509_crt_t cert,
static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert, const char *certFile, - gnutls_x509_crt_t cacert, + gnutls_x509_crt_t *cacerts, + size_t ncacerts, const char *cacertFile, bool isServer) { unsigned int status;
if (gnutls_x509_crt_list_verify(&cert, 1, - &cacert, 1, + cacerts, ncacerts, NULL, 0, 0, &status) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, isServer ? @@ -500,16 +501,15 @@ static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert,
static gnutls_x509_crt_t virNetTLSContextLoadCertFromFile(const char *certFile, - bool isServer, - bool isCA ATTRIBUTE_UNUSED) + bool isServer) { gnutls_datum_t data; gnutls_x509_crt_t cert = NULL; char *buf = NULL; int ret = -1;
- VIR_DEBUG("isServer %d isCA %d certFile %s", - isServer, isCA, certFile); + VIR_DEBUG("isServer %d certFile %s", + isServer, certFile);
if (gnutls_x509_crt_init(&cert) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, "%s", @@ -543,40 +543,81 @@ cleanup: }
+static int virNetTLSContextLoadCACertListFromFile(const char *certFile, + gnutls_x509_crt_t *certs, + size_t *ncerts) +{ + gnutls_datum_t data; + char *buf = NULL; + int ret = -1; + unsigned int certMax = *ncerts; + + *ncerts = 0; + VIR_DEBUG("certFile %s", certFile); + + if (virFileReadAll(certFile, (1<<16), &buf) < 0) + goto cleanup; + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_list_import(certs, &certMax, &data, GNUTLS_X509_FMT_PEM, 0) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Unable to import CA certificate list %s"), + certFile); + goto cleanup; + } + *ncerts = certMax; + + ret = 0; + +cleanup: + VIR_FREE(buf); + return ret; +} + + +#define MAX_CERTS 16 static int virNetTLSContextSanityCheckCredentials(bool isServer, const char *cacertFile, const char *certFile) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS]; + size_t ncacerts = MAX_CERTS; + size_t i; int ret = -1;
if ((access(certFile, R_OK) == 0) && - !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer, false))) + !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer))) goto cleanup; if ((access(cacertFile, R_OK) == 0) && - !(cacert = virNetTLSContextLoadCertFromFile(cacertFile, isServer, false))) + virNetTLSContextLoadCACertListFromFile(cacertFile, cacerts, &ncacerts) < 0) goto cleanup;
if (cert && virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) goto cleanup;
- if (cacert && - virNetTLSContextCheckCert(cacert, cacertFile, isServer, true) < 0) - goto cleanup; + for (i = 0 ; i < ncacerts ; i++) { + if (virNetTLSContextCheckCert(cacerts[i], cacertFile, isServer, true) < 0) + goto cleanup; + }
- if (cert && cacert && - virNetTLSContextCheckCertPair(cert, certFile, cacert, cacertFile, isServer) < 0) + VIR_DEBUG("Here"); + if (cert && ncacerts && + virNetTLSContextCheckCertPair(cert, certFile, cacerts, ncacerts, cacertFile, isServer) < 0) { + VIR_DEBUG("there"); goto cleanup; + }
ret = 0;
cleanup: if (cert) gnutls_x509_crt_deinit(cert); - if (cacert) - gnutls_x509_crt_deinit(cacert); + for (i = 0 ; i < ncacerts ; i++) + gnutls_x509_crt_deinit(cacerts[i]); return ret; }
diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c index 977a095..234922f 100644 --- a/tests/virnettlscontexttest.c +++ b/tests/virnettlscontexttest.c @@ -510,6 +510,57 @@ mymain(void) DO_CTX_TEST(true, cacertreq.filename, servercertnew1req.filename, true); DO_CTX_TEST(false, cacertreq.filename, clientcertnew1req.filename, true);
+ TLS_ROOT_REQ(cacertrootreq, + "UK", "libvirt root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "libvirt level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "libvirt level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "libvirt level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "libvirt.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(clientcertlevel2breq, cacertlevel1breq, + "UK", "libvirt client level 2b", 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); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + testTLSWriteCertChain("cacertchain.pem", + certchain, + ARRAY_CARDINALITY(certchain)); + + DO_CTX_TEST(true, "cacertchain.pem", servercertlevel3areq.filename, false); + DO_CTX_TEST(false, "cacertchain.pem", clientcertlevel2breq.filename, false); + testTLSDiscardCert(&cacertreq); testTLSDiscardCert(&cacert1req); testTLSDiscardCert(&cacert2req); @@ -558,6 +609,14 @@ mymain(void) testTLSDiscardCert(&servercertnew1req); testTLSDiscardCert(&clientcertnew1req);
+ testTLSDiscardCert(&cacertrootreq); + testTLSDiscardCert(&cacertlevel1areq); + testTLSDiscardCert(&cacertlevel1breq); + testTLSDiscardCert(&cacertlevel2areq); + testTLSDiscardCert(&servercertlevel3areq); + testTLSDiscardCert(&clientcertlevel2breq); + unlink("cacertchain.pem"); + testTLSCleanup();
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/tests/virnettlshelpers.c b/tests/virnettlshelpers.c index 8236e82..baf043a 100644 --- a/tests/virnettlshelpers.c +++ b/tests/virnettlshelpers.c @@ -406,6 +406,40 @@ testTLSGenerateCert(struct testTLSCertReq *req, }
+void testTLSWriteCertChain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + int fd; + int err; + static char buffer[1024*1024]; + size_t size; + + if ((fd = open(filename, O_WRONLY|O_CREAT, 0600)) < 0) { + VIR_WARN("Failed to open %s", filename); + abort(); + } + + for (i = 0 ; i < ncerts ; i++) { + size = sizeof(buffer); + if ((err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) { + VIR_WARN("Failed to export certificate %s", gnutls_strerror(err)); + unlink(filename); + abort(); + } + + if (safewrite(fd, buffer, size) != size) { + VIR_WARN("Failed to write certificate to %s", filename); + unlink(filename); + abort(); + } + } + + VIR_FORCE_CLOSE(fd); +} + + void testTLSDiscardCert(struct testTLSCertReq *req) { if (!req->crt) diff --git a/tests/virnettlshelpers.h b/tests/virnettlshelpers.h index 50a4ba1..7c3f8da 100644 --- a/tests/virnettlshelpers.h +++ b/tests/virnettlshelpers.h @@ -71,6 +71,9 @@ struct testTLSCertReq {
void testTLSGenerateCert(struct testTLSCertReq *req, gnutls_x509_crt_t ca); +void testTLSWriteCertChain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts); void testTLSDiscardCert(struct testTLSCertReq *req);
void testTLSInit(void); diff --git a/tests/virnettlssessiontest.c b/tests/virnettlssessiontest.c index 66df682..c2d34bb 100644 --- a/tests/virnettlssessiontest.c +++ b/tests/virnettlssessiontest.c @@ -193,7 +193,7 @@ static int testTLSSessionInit(const void *opaque) VIR_WARN("Expected server cert check fail"); goto cleanup; } else { - VIR_DEBUG("Not unexpected server cert fail"); + VIR_DEBUG("No unexpected server cert fail"); } }
@@ -213,7 +213,7 @@ static int testTLSSessionInit(const void *opaque) VIR_WARN("Expected client cert check fail"); goto cleanup; } else { - VIR_DEBUG("Not unexpected client cert fail"); + VIR_DEBUG("No unexpected client cert fail"); } }
@@ -405,6 +405,57 @@ mymain(void) DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename, false, false, "libvirt.org", wildcards6);
+ TLS_ROOT_REQ(cacertrootreq, + "UK", "libvirt root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "libvirt level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "libvirt level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "libvirt level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "libvirt.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(clientcertlevel2breq, cacertlevel1breq, + "UK", "libvirt client level 2b", 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); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + testTLSWriteCertChain("cacertchain.pem", + certchain, + ARRAY_CARDINALITY(certchain)); + + DO_SESS_TEST("cacertchain.pem", servercertlevel3areq.filename, clientcertlevel2breq.filename, + false, false, "libvirt.org", NULL); + testTLSDiscardCert(&clientcertreq); testTLSDiscardCert(&clientcertaltreq);
@@ -415,6 +466,14 @@ mymain(void) testTLSDiscardCert(&cacertreq); testTLSDiscardCert(&altcacertreq);
+ testTLSDiscardCert(&cacertrootreq); + testTLSDiscardCert(&cacertlevel1areq); + testTLSDiscardCert(&cacertlevel1breq); + testTLSDiscardCert(&cacertlevel2areq); + testTLSDiscardCert(&servercertlevel3areq); + testTLSDiscardCert(&clientcertlevel2breq); + unlink("cacertchain.pem"); + testTLSCleanup();
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; -- 1.8.3.1
To follow Michal's ACK. I've tried just this patch with a chained certificate I had and I can confirm this worked. -- Doug Goldstein

On 06.08.2013 13:35, Daniel P. Berrange wrote:
This series fixes the CA certificate validation so that it correctly works when a client and server cert are both signed by intermediate CAs, sharing a common ancestor CA.
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
ACK series but see my comments to 2/4 and 4/4. Michal

On 08/06/2013 05:35 AM, Daniel P. Berrange wrote:
This series fixes the CA certificate validation so that it correctly works when a client and server cert are both signed by intermediate CAs, sharing a common ancestor CA.
After the application of this series, I'm starting to see sporadic test failures; when I run 'make -j3 check' the test sometimes fails like this; but when I then do 'cd tests; ./virnettlssessiontest', it passes. I suspect you have a race where parallel tests are now trying to access the same file, and whoever loses the test fails; whereas a serial run passes every time. 11) TLS Session servercertreq.filename + clientcertreq.filename ... OK 12) TLS Session servercertreq.filename + clientcertreq.filename ... libvirt: XML-RPC error : authentication failed: Failed to verify peer's certificate OK 13) TLS Session servercertreq.filename + clientcertreq.filename ... OK 14) TLS Session servercertreq.filename + clientcertreq.filename ... libvirt: XML-RPC error : authentication failed: Failed to verify peer's certificate FAILED 15) TLS Session servercertlevel3areq.filename + clientcertlevel2breq.filename ... libvirt: XML-RPC error : Cannot read private key '/home/eblake/libvirt/tests/virnettlscontexttest-key.pem': No such file or directory FAILED
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (4)
-
Daniel P. Berrange
-
Doug Goldstein
-
Eric Blake
-
Michal Privoznik