[libvirt] [PATCH 0/3] More work on TLS certificate code

This series started off with an attempt to refactor the TLS certificate validation code to remove duplication. I then realized I ought to be testing such sensitive code too. So the big interesting part of this patch is a test case todo validation using very certificates. Rather than add a bunch of certs to GIT, I have written (nasty) GNUTLS code to generate certs on the fly with a variety of good & bad data in them.

From: "Daniel P. Berrange" <berrange@redhat.com> There is some commonality between the code for sanity checking certs when initializing libvirt and the code for validating certs during a live TLS session handshake. This patchset splits up the sanity checking function into several smaller functions each doing a specific type of check. The cert validation code is then updated to also call into these functions * src/rpc/virnettlscontext.c: Refactor cert validation code --- src/rpc/virnettlscontext.c | 466 +++++++++++++++++++++++++++----------------- 1 files changed, 283 insertions(+), 183 deletions(-) diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 7d271bc..37b55ad 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -97,68 +97,51 @@ static void virNetTLSLog(int level, const char *str) { } -static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, - bool isCA, - const char *certFile) +static int virNetTLSContextCheckCertTimes(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) { - gnutls_datum_t data; - gnutls_x509_crt_t cert = NULL; - char *buf = NULL; - int ret = -1; time_t now; - int status; - int i; - char *buffer = NULL; - size_t size; - unsigned int usage; - unsigned int critical; - bool allowClient = false, allowServer = false; - - VIR_DEBUG("isServer %d isCA %d certFile %s", - isServer, isCA, certFile); if ((now = time(NULL)) == ((time_t)-1)) { virReportSystemError(errno, "%s", _("cannot get current time")); - goto cleanup; - } - - if (gnutls_x509_crt_init(&cert) < 0) { - virNetError(VIR_ERR_SYSTEM_ERROR, "%s", - _("Unable to initialize certificate")); - goto cleanup; - } - - if (virFileReadAll(certFile, (1<<16), &buf) < 0) - goto cleanup; - - data.data = (unsigned char *)buf; - data.size = strlen(buf); - - if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { - virNetError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("Unable to import server certificate %s") : - _("Unable to import client certificate %s"), - certFile); - goto cleanup; + return -1; } if (gnutls_x509_crt_get_expiration_time(cert) < now) { - virNetError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("The server certificate %s has expired") : - _("The client certificate %s has expired"), + virNetError(VIR_ERR_SYSTEM_ERROR, + (isCA ? + _("The CA certificate %s has expired") : + (isServer ? + _("The server certificate %s has expired") : + _("The client certificate %s has expired"))), certFile); - goto cleanup; + return -1; } if (gnutls_x509_crt_get_activation_time(cert) > now) { - virNetError(VIR_ERR_SYSTEM_ERROR, isServer ? - _("The server certificate %s is not yet active") : - _("The client certificate %s is not yet active"), + virNetError(VIR_ERR_SYSTEM_ERROR, + (isCA ? + _("The CA certificate %s is not yet active") : + (isServer ? + _("The server certificate %s is not yet active") : + _("The client certificate %s is not yet active"))), certFile); - goto cleanup; + return -1; } + return 0; +} + +static int virNetTLSContextCheckCertBasicConstraints(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) +{ + int status; + status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); VIR_DEBUG("Cert %s basic constraints %d", certFile, status); @@ -168,29 +151,40 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, _("The certificate %s basic constraints show a CA, but we need one for a server") : _("The certificate %s basic constraints show a CA, but we need one for a client"), certFile); - goto cleanup; + return -1; } } else if (status == 0) { /* It is not a CA cert */ if (isCA) { virNetError(VIR_ERR_SYSTEM_ERROR, _("The certificate %s basic constraints do not show a CA"), certFile); - goto cleanup; + return -1; } } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { /* Missing basicConstraints */ if (isCA) { virNetError(VIR_ERR_SYSTEM_ERROR, _("The certificate %s is missing basic constraints for a CA"), certFile); - goto cleanup; + return -1; } } else { /* General error */ virNetError(VIR_ERR_SYSTEM_ERROR, _("Unable to query certificate %s basic constraints %s"), certFile, gnutls_strerror(status)); - goto cleanup; + return -1; } + return 0; +} + +static int virNetTLSContextCheckCertKeyUsage(gnutls_x509_crt_t cert, + const char *certFile, + bool isCA) +{ + int status; + unsigned int usage; + unsigned int critical; + status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical); @@ -202,7 +196,7 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, virNetError(VIR_ERR_SYSTEM_ERROR, _("Unable to query certificate %s key usage %s"), certFile, gnutls_strerror(status)); - goto cleanup; + return -1; } } @@ -212,7 +206,7 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, virNetError(VIR_ERR_SYSTEM_ERROR, _("Certificate %s usage does not permit certificate signing"), certFile); - goto cleanup; + return -1; } else { VIR_WARN("Certificate %s usage does not permit certificate signing", certFile); @@ -224,7 +218,7 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, virNetError(VIR_ERR_SYSTEM_ERROR, _("Certificate %s usage does not permit digital signature"), certFile); - goto cleanup; + return -1; } else { VIR_WARN("Certificate %s usage does not permit digital signature", certFile); @@ -235,7 +229,7 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, virNetError(VIR_ERR_SYSTEM_ERROR, _("Certificate %s usage does not permit key encipherment"), certFile); - goto cleanup; + return -1; } else { VIR_WARN("Certificate %s usage does not permit key encipherment", certFile); @@ -243,10 +237,25 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, } } + return 0; +} + + +static int virNetTLSContextCheckCertKeyPurpose(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer) +{ + int status; + int i; + unsigned int purposeCritical; + unsigned int critical; + char *buffer; + size_t size; + bool allowClient = false, allowServer = false; + critical = 0; for (i = 0 ; ; i++) { size = 0; - unsigned int purposeCritical; status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { @@ -261,20 +270,21 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, virNetError(VIR_ERR_SYSTEM_ERROR, _("Unable to query certificate %s key purpose %s"), certFile, gnutls_strerror(status)); - goto cleanup; + return -1; } if (VIR_ALLOC_N(buffer, size) < 0) { virReportOOMError(); - goto cleanup; + return -1; } status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical); if (status < 0) { + VIR_FREE(buffer); virNetError(VIR_ERR_SYSTEM_ERROR, _("Unable to query certificate %s key purpose %s"), certFile, gnutls_strerror(status)); - goto cleanup; + return -1; } if (purposeCritical) critical = true; @@ -291,24 +301,25 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, VIR_FREE(buffer); } - if (!isCA) { /* No purpose checks required for CA certs */ - if (isServer && !allowServer) { + if (isServer) { + if (!allowServer) { if (critical) { virNetError(VIR_ERR_SYSTEM_ERROR, _("Certificate %s purpose does not allow use for with a TLS server"), certFile); - goto cleanup; + return -1; } else { VIR_WARN("Certificate %s purpose does not allow use for with a TLS server", certFile); } } - if (!isServer && !allowClient) { + } else { + if (!allowClient) { if (critical) { virNetError(VIR_ERR_SYSTEM_ERROR, _("Certificate %s purpose does not allow use for with a TLS client"), certFile); - goto cleanup; + return -1; } else { VIR_WARN("Certificate %s purpose does not allow use for with a TLS client", certFile); @@ -316,6 +327,181 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, } } + return 0; +} + +/* Check DN is on tls_allowed_dn_list. */ +static int +virNetTLSContextCheckCertDNWhitelist(const char *dname, + const char *const*wildcards) +{ + while (*wildcards) { + int ret = fnmatch (*wildcards, dname, 0); + if (ret == 0) /* Succesful match */ + return 1; + if (ret != FNM_NOMATCH) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("Malformed TLS whitelist regular expression '%s'"), + *wildcards); + return -1; + } + + wildcards++; + } + + /* Log the client's DN for debugging */ + VIR_DEBUG("Failed whitelist check for client DN '%s'", dname); + + /* This is the most common error: make it informative. */ + virNetError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Client's Distinguished Name is not on the list " + "of allowed clients (tls_allowed_dn_list). Use " + "'certtool -i --infile clientcert.pem' to view the" + "Distinguished Name field in the client certificate," + "or run this daemon with --verbose option.")); + return 0; +} + + +static int +virNetTLSContextCheckCertDN(gnutls_x509_crt_t cert, + const char *certFile, + const char *hostname, + const char *const* whitelist) +{ + int ret; + char name[256]; + size_t namesize = sizeof name; + + memset(name, 0, namesize); + + ret = gnutls_x509_crt_get_dn(cert, name, &namesize); + if (ret != 0) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Failed to get certificate %s distinguished name: %s"), + certFile, gnutls_strerror(ret)); + return -1; + } + + if (whitelist && + virNetTLSContextCheckCertDNWhitelist(name, whitelist) <= 0) + return -1; + + if (hostname && + !gnutls_x509_crt_check_hostname(cert, hostname)) { + virNetError(VIR_ERR_RPC, + _("Certificate %s owner does not match the hostname %s"), + certFile, hostname); + return -1; + } + + return 0; +} + + +static int virNetTLSContextCheckCert(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA) +{ + if (virNetTLSContextCheckCertTimes(cert, certFile, + isServer, isCA) < 0) + return -1; + + if (virNetTLSContextCheckCertBasicConstraints(cert, certFile, + isServer, isCA) < 0) + return -1; + + if (virNetTLSContextCheckCertKeyUsage(cert, certFile, + isCA) < 0) + return -1; + + if (!isCA && + virNetTLSContextCheckCertKeyPurpose(cert, certFile, + isServer) < 0) + return -1; + + return 0; +} + + +static int virNetTLSContextCheckCertPair(gnutls_x509_crt_t cert, + const char *certFile, + gnutls_x509_crt_t cacert, + const char *cacertFile, + bool isServer) +{ + unsigned int status; + + if (gnutls_x509_crt_list_verify(&cert, 1, + &cacert, 1, + NULL, 0, + 0, &status) < 0) { + virNetError(VIR_ERR_SYSTEM_ERROR, isServer ? + _("Unable to verify server certificate %s against CA certificate %s") : + _("Unable to verify client certificate %s against CA certificate %s"), + certFile, cacertFile); + return -1; + } + + if (status != 0) { + const char *reason = _("Invalid certificate"); + + if (status & GNUTLS_CERT_INVALID) + reason = _("The certificate is not trusted."); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + reason = _("The certificate hasn't got a known issuer."); + + if (status & GNUTLS_CERT_REVOKED) + reason = _("The certificate has been revoked."); + +#ifndef GNUTLS_1_0_COMPAT + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + reason = _("The certificate uses an insecure algorithm"); +#endif + + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Our own certificate %s failed validation against %s: %s"), + certFile, cacertFile, reason); + return -1; + } + + return 0; +} + + +static gnutls_x509_crt_t virNetTLSContextLoadCertFromFile(const char *certFile, + bool isServer, + bool isCA) +{ + 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); + + if (gnutls_x509_crt_init(&cert) < 0) { + virNetError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Unable to initialize certificate")); + goto cleanup; + } + + if (virFileReadAll(certFile, (1<<16), &buf) < 0) + goto cleanup; + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { + virNetError(VIR_ERR_SYSTEM_ERROR, isServer ? + _("Unable to import server certificate %s") : + _("Unable to import client certificate %s"), + certFile); + goto cleanup; + } ret = 0; @@ -324,7 +510,6 @@ cleanup: gnutls_x509_crt_deinit(cert); cert = NULL; } - VIR_FREE(buffer); VIR_FREE(buf); return cert; } @@ -337,51 +522,25 @@ static int virNetTLSContextSanityCheckCredentials(bool isServer, gnutls_x509_crt_t cert = NULL; gnutls_x509_crt_t cacert = NULL; int ret = -1; - unsigned int status; - if (access(certFile, R_OK) == 0) { - if (!(cert = virNetTLSContextSanityCheckCert(isServer, false, certFile))) - goto cleanup; - } - if (access(cacertFile, R_OK) == 0) { - if (!(cacert = virNetTLSContextSanityCheckCert(isServer, true, cacertFile))) - goto cleanup; - } - - if (cert && cacert) { - if (gnutls_x509_crt_list_verify(&cert, 1, - &cacert, 1, - NULL, 0, - 0, &status) < 0) { - virNetError(VIR_ERR_SYSTEM_ERROR, "%s", isServer ? - _("Unable to verify server certificate against CA certificate") : - _("Unable to verify client certificate against CA certificate")); - goto cleanup; - } - - if (status != 0) { - const char *reason = _("Invalid certificate"); - - if (status & GNUTLS_CERT_INVALID) - reason = _("The certificate is not trusted."); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - reason = _("The certificate hasn't got a known issuer."); + if ((access(certFile, R_OK) == 0) && + !(cert = virNetTLSContextLoadCertFromFile(certFile, isServer, false))) + goto cleanup; + if ((access(cacertFile, R_OK) == 0) && + !(cacert = virNetTLSContextLoadCertFromFile(cacertFile, isServer, false))) + goto cleanup; - if (status & GNUTLS_CERT_REVOKED) - reason = _("The certificate has been revoked."); + if (cert && + virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) + goto cleanup; -#ifndef GNUTLS_1_0_COMPAT - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - reason = _("The certificate uses an insecure algorithm"); -#endif + if (cacert && + virNetTLSContextCheckCert(cacert, cacertFile, isServer, true) < 0) + goto cleanup; - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Our own certificate %s failed validation against %s: %s"), - certFile, cacertFile, reason); - goto cleanup; - } - } + if (cert && cacert && + virNetTLSContextCheckCertPair(cert, certFile, cacert, cacertFile, isServer) < 0) + goto cleanup; ret = 0; @@ -760,45 +919,6 @@ void virNetTLSContextRef(virNetTLSContextPtr ctxt) } -/* Check DN is on tls_allowed_dn_list. */ -static int -virNetTLSContextCheckDN(virNetTLSContextPtr ctxt, - const char *dname) -{ - const char *const*wildcards; - - /* If the list is not set, allow any DN. */ - wildcards = ctxt->x509dnWhitelist; - if (!wildcards) - return 1; - - while (*wildcards) { - int ret = fnmatch (*wildcards, dname, 0); - if (ret == 0) /* Succesful match */ - return 1; - if (ret != FNM_NOMATCH) { - virNetError(VIR_ERR_INTERNAL_ERROR, - _("Malformed TLS whitelist regular expression '%s'"), - *wildcards); - return -1; - } - - wildcards++; - } - - /* Log the client's DN for debugging */ - VIR_DEBUG("Failed whitelist check for client DN '%s'", dname); - - /* This is the most common error: make it informative. */ - virNetError(VIR_ERR_SYSTEM_ERROR, "%s", - _("Client's Distinguished Name is not on the list " - "of allowed clients (tls_allowed_dn_list). Use " - "'certtool -i --infile clientcert.pem' to view the" - "Distinguished Name field in the client certificate," - "or run this daemon with --verbose option.")); - return 0; -} - static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt, virNetTLSSessionPtr sess) { @@ -806,11 +926,6 @@ static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt, unsigned int status; const gnutls_datum_t *certs; unsigned int nCerts, i; - time_t now; - char name[256]; - size_t namesize = sizeof name; - - memset(name, 0, namesize); if ((ret = gnutls_certificate_verify_peers2(sess->session, &status)) < 0){ virNetError(VIR_ERR_SYSTEM_ERROR, @@ -819,12 +934,6 @@ static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt, goto authdeny; } - if ((now = time(NULL)) == ((time_t)-1)) { - virReportSystemError(errno, "%s", - _("cannot get current time")); - goto authfail; - } - if (status != 0) { const char *reason = _("Invalid certificate"); @@ -876,46 +985,37 @@ static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt, goto authfail; } - if (gnutls_x509_crt_get_expiration_time(cert) < now) { - /* Warning is reversed from what you expect, since with - * this code it is the Server checking the client and - * vica-versa */ - virNetError(VIR_ERR_SYSTEM_ERROR, "%s", sess->isServer ? - _("The client certificate has expired") : - _("The server certificate has expired")); - gnutls_x509_crt_deinit(cert); - goto authdeny; - } - - if (gnutls_x509_crt_get_activation_time(cert) > now) { - /* client/server order reversed. see above */ - virNetError(VIR_ERR_SYSTEM_ERROR, "%s", sess->isServer ? - _("The client certificate is not yet active") : - _("The server certificate is not yet active")); + if (virNetTLSContextCheckCertTimes(cert, "[session]", + sess->isServer, i > 0) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; } if (i == 0) { - ret = gnutls_x509_crt_get_dn(cert, name, &namesize); - if (ret != 0) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Failed to get certificate distinguished name: %s"), - gnutls_strerror(ret)); + if (virNetTLSContextCheckCertDN(cert, "[session]", sess->hostname, + ctxt->x509dnWhitelist) < 0) { gnutls_x509_crt_deinit(cert); - goto authfail; + goto authdeny; + } + + /* !sess->isServer, since on the client, we're validating the + * server's cert, and on the server, the client's cert + */ + if (virNetTLSContextCheckCertBasicConstraints(cert, "[session]", + !sess->isServer, false) < 0) { + gnutls_x509_crt_deinit(cert); + goto authdeny; } - if (virNetTLSContextCheckDN(ctxt, name) <= 0) { + if (virNetTLSContextCheckCertKeyUsage(cert, "[session]", + false) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; } - if (sess->hostname && - !gnutls_x509_crt_check_hostname(cert, sess->hostname)) { - virNetError(VIR_ERR_RPC, - _("Certificate's owner does not match the hostname (%s)"), - sess->hostname); + /* !sess->isServer - as above */ + if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]", + !sess->isServer) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; } -- 1.7.6

On 07/21/2011 06:30 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
There is some commonality between the code for sanity checking certs when initializing libvirt and the code for validating certs during a live TLS session handshake. This patchset splits up the sanity checking function into several smaller functions each doing a specific type of check. The cert validation code is then updated to also call into these functions
* src/rpc/virnettlscontext.c: Refactor cert validation code --- src/rpc/virnettlscontext.c | 466 +++++++++++++++++++++++++++----------------- 1 files changed, 283 insertions(+), 183 deletions(-)
ACK - not the easiest diff to follow, but does make sense for smaller self-contained functions and building up validation out of common pieces. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> When libvirtd starts it it will sanity check its own certs, and before libvirt clients connect to a remote server they will sanity check their own certs. This patch allows such sanity checking to be skipped. There is no strong reason to need todo this, other than to bypass possible libvirt bugs in sanity checking, or for testing purposes. libvirt.conf gains tls_no_sanity_certificate parameter to go along with tls_no_verify_certificate. The remote driver client URIs gain a no_sanity URI parameter * daemon/test_libvirtd.aug, daemon/libvirtd.conf, daemon/libvirtd.c, daemon/libvirtd.aug: Add parameter to allow cert sanity checks to be skipped * src/remote/remote_driver.c: Add no_sanity parameter to skip cert checks * src/rpc/virnettlscontext.c, src/rpc/virnettlscontext.h: Add new parameter for skipping sanity checks independantly of skipping session cert validation checks --- daemon/libvirtd.aug | 1 + daemon/libvirtd.c | 4 ++++ daemon/libvirtd.conf | 9 +++++++++ daemon/test_libvirtd.aug | 2 ++ src/remote/remote_driver.c | 11 +++++++---- src/rpc/virnettlscontext.c | 39 ++++++++++++++++++++++++--------------- src/rpc/virnettlscontext.h | 4 ++++ 7 files changed, 51 insertions(+), 19 deletions(-) diff --git a/daemon/libvirtd.aug b/daemon/libvirtd.aug index 0e06142..3f47ebb 100644 --- a/daemon/libvirtd.aug +++ b/daemon/libvirtd.aug @@ -48,6 +48,7 @@ module Libvirtd = | str_entry "crl_file" let authorization_entry = bool_entry "tls_no_verify_certificate" + | bool_entry "tls_no_sanity_certificate" | str_array_entry "tls_allowed_dn_list" | str_array_entry "sasl_allowed_username_list" diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 259fcc4..8f8b8b1 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -120,6 +120,7 @@ struct daemonConfig { char *mdns_name; int tls_no_verify_certificate; + int tls_no_sanity_certificate; char **tls_allowed_dn_list; char **sasl_allowed_username_list; @@ -535,12 +536,14 @@ static int daemonSetupNetworking(virNetServerPtr srv, config->cert_file, config->key_file, (const char *const*)config->tls_allowed_dn_list, + config->tls_no_sanity_certificate ? false : true, config->tls_no_verify_certificate ? false : true))) goto error; } else { if (!(ctxt = virNetTLSContextNewServerPath(NULL, !privileged, (const char *const*)config->tls_allowed_dn_list, + config->tls_no_sanity_certificate ? false : true, config->tls_no_verify_certificate ? false : true))) goto error; } @@ -1054,6 +1057,7 @@ daemonConfigLoad(struct daemonConfig *data, GET_CONF_INT (conf, filename, mdns_adv); GET_CONF_STR (conf, filename, mdns_name); + GET_CONF_INT (conf, filename, tls_no_sanity_certificate); GET_CONF_INT (conf, filename, tls_no_verify_certificate); GET_CONF_STR (conf, filename, key_file); diff --git a/daemon/libvirtd.conf b/daemon/libvirtd.conf index 3a071b0..217f2f4 100644 --- a/daemon/libvirtd.conf +++ b/daemon/libvirtd.conf @@ -187,6 +187,15 @@ # +# Flag to disable verification of our own server certificates +# +# When libvirtd starts it performs some sanity checks against +# its own certificates. +# +# Default is to always sanity. Uncommenting this will disable +# sanity checks which is not a good idea +#tls_no_sanity_certificate = 1 + # Flag to disable verification of client certificates # # Client certificate verification is the primary authentication mechanism. diff --git a/daemon/test_libvirtd.aug b/daemon/test_libvirtd.aug index 5f8b644..58b7170 100644 --- a/daemon/test_libvirtd.aug +++ b/daemon/test_libvirtd.aug @@ -193,6 +193,7 @@ crl_file = \"/etc/pki/CA/crl.pem\" # Default is to always verify. Uncommenting this will disable # verification - make sure an IP whitelist is set tls_no_verify_certificate = 1 +tls_no_sanity_certificate = 1 # A whitelist of allowed x509 Distinguished Names @@ -468,6 +469,7 @@ audit_level = 2 { "#comment" = "Default is to always verify. Uncommenting this will disable" } { "#comment" = "verification - make sure an IP whitelist is set" } { "tls_no_verify_certificate" = "1" } + { "tls_no_sanity_certificate" = "1" } { "#empty" } { "#empty" } { "#comment" = "A whitelist of allowed x509 Distinguished Names" } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 82b938c..a94b69b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -342,7 +342,7 @@ doRemoteOpen (virConnectPtr conn, */ char *name = NULL, *command = NULL, *sockname = NULL, *netcat = NULL; char *port = NULL, *authtype = NULL, *username = NULL; - int no_verify = 0, no_tty = 0; + bool no_sanity = false, no_verify = false, no_tty = false; char *pkipath = NULL; /* Return code from this function, and the private data. */ @@ -416,11 +416,14 @@ doRemoteOpen (virConnectPtr conn, netcat = strdup (var->value); if (!netcat) goto out_of_memory; var->ignore = 1; + } else if (STRCASEEQ (var->name, "no_sanity")) { + no_sanity = atoi (var->value) != 0; + var->ignore = 1; } else if (STRCASEEQ (var->name, "no_verify")) { - no_verify = atoi (var->value); + no_verify = atoi (var->value) != 0; var->ignore = 1; } else if (STRCASEEQ (var->name, "no_tty")) { - no_tty = atoi (var->value); + no_tty = atoi (var->value) != 0; var->ignore = 1; } else if (STRCASEEQ(var->name, "pkipath")) { VIR_FREE(pkipath); @@ -500,7 +503,7 @@ doRemoteOpen (virConnectPtr conn, case trans_tls: priv->tls = virNetTLSContextNewClientPath(pkipath, geteuid() != 0 ? true : false, - no_verify ? false : true); + no_sanity, no_verify); if (!priv->tls) goto failed; priv->is_secure = 1; diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 37b55ad..9279ded 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -382,7 +382,7 @@ virNetTLSContextCheckCertDN(gnutls_x509_crt_t cert, certFile, gnutls_strerror(ret)); return -1; } - + VIR_DEBUG("Peer DN is %s", name); if (whitelist && virNetTLSContextCheckCertDNWhitelist(name, whitelist) <= 0) return -1; @@ -637,6 +637,7 @@ static virNetTLSContextPtr virNetTLSContextNew(const char *cacert, const char *cert, const char *key, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert, bool isServer) { @@ -644,8 +645,8 @@ static virNetTLSContextPtr virNetTLSContextNew(const char *cacert, char *gnutlsdebug; int err; - VIR_DEBUG("cacert=%s cacrl=%s cert=%s key=%s requireValid=%d isServer=%d", - cacert, NULLSTR(cacrl), cert, key, requireValidCert, isServer); + VIR_DEBUG("cacert=%s cacrl=%s cert=%s key=%s sanityCheckCert=%d requireValid=%d isServer=%d", + cacert, NULLSTR(cacrl), cert, key, sanityCheckCert, requireValidCert, isServer); if (VIR_ALLOC(ctxt) < 0) { virReportOOMError(); @@ -675,7 +676,7 @@ static virNetTLSContextPtr virNetTLSContextNew(const char *cacert, goto error; } - if (requireValidCert && + if (sanityCheckCert && virNetTLSContextSanityCheckCredentials(isServer, cacert, cert) < 0) goto error; @@ -851,6 +852,7 @@ out_of_memory: static virNetTLSContextPtr virNetTLSContextNewPath(const char *pkipath, bool tryUserPkiPath, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert, bool isServer) { @@ -862,7 +864,8 @@ static virNetTLSContextPtr virNetTLSContextNewPath(const char *pkipath, return NULL; ctxt = virNetTLSContextNew(cacert, cacrl, cert, key, - x509dnWhitelist, requireValidCert, isServer); + x509dnWhitelist, sanityCheckCert, + requireValidCert, isServer); VIR_FREE(cacert); VIR_FREE(cacrl); @@ -875,18 +878,20 @@ static virNetTLSContextPtr virNetTLSContextNewPath(const char *pkipath, virNetTLSContextPtr virNetTLSContextNewServerPath(const char *pkipath, bool tryUserPkiPath, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert) { - return virNetTLSContextNewPath(pkipath, tryUserPkiPath, - x509dnWhitelist, requireValidCert, true); + return virNetTLSContextNewPath(pkipath, tryUserPkiPath, x509dnWhitelist, + sanityCheckCert, requireValidCert, true); } virNetTLSContextPtr virNetTLSContextNewClientPath(const char *pkipath, bool tryUserPkiPath, + bool sanityCheckCert, bool requireValidCert) { - return virNetTLSContextNewPath(pkipath, tryUserPkiPath, - NULL, requireValidCert, false); + return virNetTLSContextNewPath(pkipath, tryUserPkiPath, NULL, + sanityCheckCert, requireValidCert, false); } @@ -895,10 +900,11 @@ virNetTLSContextPtr virNetTLSContextNewServer(const char *cacert, const char *cert, const char *key, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert) { - return virNetTLSContextNew(cacert, cacrl, cert, key, - x509dnWhitelist, requireValidCert, true); + return virNetTLSContextNew(cacert, cacrl, cert, key, x509dnWhitelist, + sanityCheckCert, requireValidCert, true); } @@ -906,10 +912,11 @@ virNetTLSContextPtr virNetTLSContextNewClient(const char *cacert, const char *cacrl, const char *cert, const char *key, + bool sanityCheckCert, bool requireValidCert) { - return virNetTLSContextNew(cacert, cacrl, key, cert, - NULL, requireValidCert, false); + return virNetTLSContextNew(cacert, cacrl, cert, key, NULL, + sanityCheckCert, requireValidCert, false); } @@ -1048,10 +1055,12 @@ int virNetTLSContextCheckCertificate(virNetTLSContextPtr ctxt, { if (virNetTLSContextValidCertificate(ctxt, sess) < 0) { if (ctxt->requireValidCert) { - virNetError(VIR_ERR_AUTH_FAILED, "%s", - _("Failed to verify peer's certificate")); + if (0) + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("Failed to verify peer's certificate")); return -1; } + virResetLastError(); VIR_INFO("Ignoring bad certificate at user request"); } return 0; diff --git a/src/rpc/virnettlscontext.h b/src/rpc/virnettlscontext.h index f23667f..641d67e 100644 --- a/src/rpc/virnettlscontext.h +++ b/src/rpc/virnettlscontext.h @@ -33,10 +33,12 @@ typedef virNetTLSSession *virNetTLSSessionPtr; virNetTLSContextPtr virNetTLSContextNewServerPath(const char *pkipath, bool tryUserPkiPath, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert); virNetTLSContextPtr virNetTLSContextNewClientPath(const char *pkipath, bool tryUserPkiPath, + bool sanityCheckCert, bool requireValidCert); virNetTLSContextPtr virNetTLSContextNewServer(const char *cacert, @@ -44,12 +46,14 @@ virNetTLSContextPtr virNetTLSContextNewServer(const char *cacert, const char *cert, const char *key, const char *const*x509dnWhitelist, + bool sanityCheckCert, bool requireValidCert); virNetTLSContextPtr virNetTLSContextNewClient(const char *cacert, const char *cacrl, const char *cert, const char *key, + bool sanityCheckCert, bool requireValidCert); void virNetTLSContextRef(virNetTLSContextPtr ctxt); -- 1.7.6

On 07/21/2011 06:30 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
When libvirtd starts it it will sanity check its own certs, and before libvirt clients connect to a remote server they will sanity check their own certs. This patch allows such sanity checking to be skipped. There is no strong reason to need todo this, other than to bypass possible libvirt bugs
s/todo/to do/
in sanity checking, or for testing purposes.
libvirt.conf gains tls_no_sanity_certificate parameter to go along with tls_no_verify_certificate. The remote driver client URIs gain a no_sanity URI parameter
Makes sense.
+++ b/src/remote/remote_driver.c @@ -342,7 +342,7 @@ doRemoteOpen (virConnectPtr conn, */ char *name = NULL, *command = NULL, *sockname = NULL, *netcat = NULL; char *port = NULL, *authtype = NULL, *username = NULL; - int no_verify = 0, no_tty = 0; + bool no_sanity = false, no_verify = false, no_tty = false;
Double negatives. Yuck. Can we instead go with: bool sanity = true, ...
char *pkipath = NULL;
/* Return code from this function, and the private data. */ @@ -416,11 +416,14 @@ doRemoteOpen (virConnectPtr conn, netcat = strdup (var->value); if (!netcat) goto out_of_memory; var->ignore = 1; + } else if (STRCASEEQ (var->name, "no_sanity")) { + no_sanity = atoi (var->value) != 0; + var->ignore = 1;
sanity = atoi(var->value) == 0; ...
@@ -500,7 +503,7 @@ doRemoteOpen (virConnectPtr conn, case trans_tls: priv->tls = virNetTLSContextNewClientPath(pkipath, geteuid() != 0 ? true : false, - no_verify ? false : true); + no_sanity, no_verify);
..., !sanity, !verify) Oops - logic bug. Here, you passed no_sanity (true to skip sanity checking)...
@@ -851,6 +852,7 @@ out_of_memory: static virNetTLSContextPtr virNetTLSContextNewPath(const char *pkipath, bool tryUserPkiPath, const char *const*x509dnWhitelist, + bool sanityCheckCert,
but here, you accept sanityCheckCert (true to perform sanity checking). See why I hate double negatives?
@@ -1048,10 +1055,12 @@ int virNetTLSContextCheckCertificate(virNetTLSContextPtr ctxt, { if (virNetTLSContextValidCertificate(ctxt, sess)< 0) { if (ctxt->requireValidCert) { - virNetError(VIR_ERR_AUTH_FAILED, "%s", - _("Failed to verify peer's certificate")); + if (0) + virNetError(VIR_ERR_AUTH_FAILED, "%s", + _("Failed to verify peer's certificate"));
Debugging hunk? Why are we leaving if(0) in? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> This test case checks certification validation rules for - Basic constraints - Key purpose - Key usage - Start/expiry times It checks initial context creation sanity checks, and live session validation --- tests/.gitignore | 1 + tests/Makefile.am | 8 +- tests/pkix_asn1_tab.c | 567 +++++++++++++++++++ tests/virnettlscontexttest.c | 1224 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1799 insertions(+), 1 deletions(-) create mode 100644 tests/pkix_asn1_tab.c create mode 100644 tests/virnettlscontexttest.c diff --git a/tests/.gitignore b/tests/.gitignore index 32457ab..7159c37 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -34,6 +34,7 @@ utiltest virbuftest virnetmessagetest virnetsockettest +virnettlscontexttest virshtest vmx2xmltest xencapstest diff --git a/tests/Makefile.am b/tests/Makefile.am index e9a445e..080e5ca 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -85,7 +85,7 @@ check_PROGRAMS = virshtest conftest sockettest \ nodeinfotest qparamtest virbuftest \ commandtest commandhelper seclabeltest \ hashtest virnetmessagetest virnetsockettest ssh \ - utiltest + utiltest virnettlscontexttest # This is a fake SSH we use from virnetsockettest ssh_SOURCES = ssh.c @@ -202,6 +202,7 @@ TESTS = virshtest \ hashtest \ virnetmessagetest \ virnetsockettest \ + virnettlscontexttest \ utiltest \ $(test_scripts) @@ -445,6 +446,11 @@ virnetsockettest_SOURCES = \ virnetsockettest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) virnetsockettest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) +virnettlscontexttest_SOURCES = \ + virnettlscontexttest.c testutils.h testutils.c pkix_asn1_tab.c +virnettlscontexttest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virnettlscontexttest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) -ltasn1 + seclabeltest_SOURCES = \ seclabeltest.c diff --git a/tests/pkix_asn1_tab.c b/tests/pkix_asn1_tab.c new file mode 100644 index 0000000..5d5ca3d --- /dev/null +++ b/tests/pkix_asn1_tab.c @@ -0,0 +1,567 @@ +/* + * This file comes from gnutls, licensed under the GPLv3+ + */ + +#include <config.h> +#include <libtasn1.h> + +const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "id-pkix", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1, "7"}, + { "AuthorityKeyIdentifier", 1610612741, NULL }, + { "keyIdentifier", 1610637314, "KeyIdentifier"}, + { NULL, 4104, "0"}, + { "authorityCertIssuer", 1610637314, "GeneralNames"}, + { NULL, 4104, "1"}, + { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + { NULL, 4104, "2"}, + { "KeyIdentifier", 1073741831, NULL }, + { "SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + { "KeyUsage", 1073741830, NULL }, + { "DirectoryString", 1610612754, NULL }, + { "teletexString", 1612709890, "TeletexString"}, + { "MAX", 524298, "1"}, + { "printableString", 1612709890, "PrintableString"}, + { "MAX", 524298, "1"}, + { "universalString", 1612709890, "UniversalString"}, + { "MAX", 524298, "1"}, + { "utf8String", 1612709890, "UTF8String"}, + { "MAX", 524298, "1"}, + { "bmpString", 1612709890, "BMPString"}, + { "MAX", 524298, "1"}, + { "ia5String", 538968066, "IA5String"}, + { "MAX", 524298, "1"}, + { "SubjectAltName", 1073741826, "GeneralNames"}, + { "GeneralNames", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralName"}, + { "GeneralName", 1610612754, NULL }, + { "otherName", 1610620930, "AnotherName"}, + { NULL, 4104, "0"}, + { "rfc822Name", 1610620930, "IA5String"}, + { NULL, 4104, "1"}, + { "dNSName", 1610620930, "IA5String"}, + { NULL, 4104, "2"}, + { "x400Address", 1610620941, NULL }, + { NULL, 4104, "3"}, + { "directoryName", 1610620930, "RDNSequence"}, + { NULL, 2056, "4"}, + { "ediPartyName", 1610620941, NULL }, + { NULL, 4104, "5"}, + { "uniformResourceIdentifier", 1610620930, "IA5String"}, + { NULL, 4104, "6"}, + { "iPAddress", 1610620935, NULL }, + { NULL, 4104, "7"}, + { "registeredID", 536879116, NULL }, + { NULL, 4104, "8"}, + { "AnotherName", 1610612741, NULL }, + { "type-id", 1073741836, NULL }, + { "value", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "type-id", 1, NULL }, + { "IssuerAltName", 1073741826, "GeneralNames"}, + { "BasicConstraints", 1610612741, NULL }, + { "cA", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "pathLenConstraint", 537411587, NULL }, + { "0", 10, "MAX"}, + { "CRLDistributionPoints", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "DistributionPoint"}, + { "DistributionPoint", 1610612741, NULL }, + { "distributionPoint", 1610637314, "DistributionPointName"}, + { NULL, 2056, "0"}, + { "reasons", 1610637314, "ReasonFlags"}, + { NULL, 4104, "1"}, + { "cRLIssuer", 536895490, "GeneralNames"}, + { NULL, 4104, "2"}, + { "DistributionPointName", 1610612754, NULL }, + { "fullName", 1610620930, "GeneralNames"}, + { NULL, 4104, "0"}, + { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + { NULL, 4104, "1"}, + { "ReasonFlags", 1073741830, NULL }, + { "ExtKeyUsageSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "KeyPurposeId"}, + { "KeyPurposeId", 1073741836, NULL }, + { "CRLNumber", 1611137027, NULL }, + { "0", 10, "MAX"}, + { "CertificateIssuer", 1073741826, "GeneralNames"}, + { "NumericString", 1610620935, NULL }, + { NULL, 4360, "18"}, + { "IA5String", 1610620935, NULL }, + { NULL, 4360, "22"}, + { "TeletexString", 1610620935, NULL }, + { NULL, 4360, "20"}, + { "PrintableString", 1610620935, NULL }, + { NULL, 4360, "19"}, + { "UniversalString", 1610620935, NULL }, + { NULL, 4360, "28"}, + { "BMPString", 1610620935, NULL }, + { NULL, 4360, "30"}, + { "UTF8String", 1610620935, NULL }, + { NULL, 4360, "12"}, + { "Attribute", 1610612741, NULL }, + { "type", 1073741826, "AttributeType"}, + { "values", 536870927, NULL }, + { NULL, 2, "AttributeValue"}, + { "AttributeType", 1073741836, NULL }, + { "AttributeValue", 1614807053, NULL }, + { "type", 1, NULL }, + { "AttributeTypeAndValue", 1610612741, NULL }, + { "type", 1073741826, "AttributeType"}, + { "value", 2, "AttributeValue"}, + { "id-at", 1879048204, NULL }, + { "joint-iso-ccitt", 1073741825, "2"}, + { "ds", 1073741825, "5"}, + { NULL, 1, "4"}, + { "id-at-initials", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "43"}, + { "X520initials", 1073741826, "DirectoryString"}, + { "id-at-generationQualifier", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "44"}, + { "X520generationQualifier", 1073741826, "DirectoryString"}, + { "id-at-surname", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "4"}, + { "X520surName", 1073741826, "DirectoryString"}, + { "id-at-givenName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "42"}, + { "X520givenName", 1073741826, "DirectoryString"}, + { "id-at-name", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "41"}, + { "X520name", 1073741826, "DirectoryString"}, + { "id-at-commonName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "3"}, + { "X520CommonName", 1073741826, "DirectoryString"}, + { "id-at-localityName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "7"}, + { "X520LocalityName", 1073741826, "DirectoryString"}, + { "id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "8"}, + { "X520StateOrProvinceName", 1073741826, "DirectoryString"}, + { "id-at-organizationName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "10"}, + { "X520OrganizationName", 1073741826, "DirectoryString"}, + { "id-at-organizationalUnitName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "11"}, + { "X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + { "id-at-title", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "12"}, + { "X520Title", 1073741826, "DirectoryString"}, + { "id-at-description", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "13"}, + { "X520Description", 1073741826, "DirectoryString"}, + { "id-at-dnQualifier", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "46"}, + { "X520dnQualifier", 1073741826, "PrintableString"}, + { "id-at-countryName", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "6"}, + { "X520countryName", 1612709890, "PrintableString"}, + { NULL, 1048586, "2"}, + { "id-at-serialNumber", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "5"}, + { "X520serialNumber", 1073741826, "PrintableString"}, + { "id-at-telephoneNumber", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "20"}, + { "X520telephoneNumber", 1073741826, "PrintableString"}, + { "id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "23"}, + { "X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + { "id-at-pseudonym", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "65"}, + { "X520pseudonym", 1073741826, "DirectoryString"}, + { "id-at-name", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "41"}, + { "X520name", 1073741826, "DirectoryString"}, + { "id-at-streetAddress", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "9"}, + { "X520streetAddress", 1073741826, "DirectoryString"}, + { "id-at-postalAddress", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "16"}, + { "X520postalAddress", 1073741826, "PostalAddress"}, + { "PostalAddress", 1610612747, NULL }, + { NULL, 2, "DirectoryString"}, + { "id-at-postalCode", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-at"}, + { NULL, 1, "17"}, + { "X520postalCode", 1073741826, "DirectoryString"}, + { "emailAddress", 1880096780, "AttributeType"}, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "1"}, + { "Pkcs9email", 1612709890, "IA5String"}, + { "ub-emailaddress-length", 524298, "1"}, + { "Name", 1610612754, NULL }, + { "rdnSequence", 2, "RDNSequence"}, + { "RDNSequence", 1610612747, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "DistinguishedName", 1073741826, "RDNSequence"}, + { "RelativeDistinguishedName", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AttributeTypeAndValue"}, + { "Certificate", 1610612741, NULL }, + { "tbsCertificate", 1073741826, "TBSCertificate"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertificate", 1610612741, NULL }, + { "version", 1610653698, "Version"}, + { NULL, 1073741833, "v1"}, + { NULL, 2056, "0"}, + { "serialNumber", 1073741826, "CertificateSerialNumber"}, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "validity", 1073741826, "Validity"}, + { "subject", 1073741826, "Name"}, + { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "issuerUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "1"}, + { "subjectUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "2"}, + { "extensions", 536895490, "Extensions"}, + { NULL, 2056, "3"}, + { "Version", 1610874883, NULL }, + { "v1", 1073741825, "0"}, + { "v2", 1073741825, "1"}, + { "v3", 1, "2"}, + { "CertificateSerialNumber", 1073741827, NULL }, + { "Validity", 1610612741, NULL }, + { "notBefore", 1073741826, "Time"}, + { "notAfter", 2, "Time"}, + { "Time", 1610612754, NULL }, + { "utcTime", 1090519057, NULL }, + { "generalTime", 8388625, NULL }, + { "UniqueIdentifier", 1073741830, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "Extensions", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Extension"}, + { "Extension", 1610612741, NULL }, + { "extnID", 1073741836, NULL }, + { "critical", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "extnValue", 7, NULL }, + { "CertificateList", 1610612741, NULL }, + { "tbsCertList", 1073741826, "TBSCertList"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertList", 1610612741, NULL }, + { "version", 1073758210, "Version"}, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "thisUpdate", 1073741826, "Time"}, + { "nextUpdate", 1073758210, "Time"}, + { "revokedCertificates", 1610629131, NULL }, + { NULL, 536870917, NULL }, + { "userCertificate", 1073741826, "CertificateSerialNumber"}, + { "revocationDate", 1073741826, "Time"}, + { "crlEntryExtensions", 16386, "Extensions"}, + { "crlExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "Dss-Sig-Value", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "DomainParameters", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "g", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "j", 1073758211, NULL }, + { "validationParms", 16386, "ValidationParms"}, + { "ValidationParms", 1610612741, NULL }, + { "seed", 1073741830, NULL }, + { "pgenCounter", 3, NULL }, + { "Dss-Parms", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "CountryName", 1610620946, NULL }, + { NULL, 1073746952, "1"}, + { "x121-dcc-code", 1612709890, "NumericString"}, + { NULL, 1048586, "ub-country-name-numeric-length"}, + { "iso-3166-alpha2-code", 538968066, "PrintableString"}, + { NULL, 1048586, "ub-country-name-alpha-length"}, + { "OrganizationName", 1612709890, "PrintableString"}, + { "ub-organization-name-length", 524298, "1"}, + { "NumericUserIdentifier", 1612709890, "NumericString"}, + { "ub-numeric-user-id-length", 524298, "1"}, + { "OrganizationalUnitNames", 1612709899, NULL }, + { "ub-organizational-units", 1074266122, "1"}, + { NULL, 2, "OrganizationalUnitName"}, + { "OrganizationalUnitName", 1612709890, "PrintableString"}, + { "ub-organizational-unit-name-length", 524298, "1"}, + { "CommonName", 1073741826, "PrintableString"}, + { "pkcs-7-ContentInfo", 1610612741, NULL }, + { "contentType", 1073741826, "pkcs-7-ContentType"}, + { "content", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "contentType", 1, NULL }, + { "pkcs-7-DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + { "digest", 2, "pkcs-7-Digest"}, + { "pkcs-7-Digest", 1073741831, NULL }, + { "pkcs-7-ContentType", 1073741836, NULL }, + { "pkcs-7-SignedData", 1610612741, NULL }, + { "version", 1073741826, "pkcs-7-CMSVersion"}, + { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + { "certificates", 1610637314, "pkcs-7-CertificateSet"}, + { NULL, 4104, "0"}, + { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + { NULL, 4104, "1"}, + { "signerInfos", 2, "pkcs-7-SignerInfos"}, + { "pkcs-7-CMSVersion", 1610874883, NULL }, + { "v0", 1073741825, "0"}, + { "v1", 1073741825, "1"}, + { "v2", 1073741825, "2"}, + { "v3", 1073741825, "3"}, + { "v4", 1, "4"}, + { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL }, + { NULL, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + { "pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, + { "eContentType", 1073741826, "pkcs-7-ContentType"}, + { "eContent", 536895495, NULL }, + { NULL, 2056, "0"}, + { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, + { NULL, 13, NULL }, + { "pkcs-7-CertificateChoices", 1610612754, NULL }, + { "certificate", 13, NULL }, + { "pkcs-7-CertificateSet", 1610612751, NULL }, + { NULL, 2, "pkcs-7-CertificateChoices"}, + { "pkcs-7-SignerInfos", 1610612751, NULL }, + { NULL, 13, NULL }, + { "pkcs-10-CertificationRequestInfo", 1610612741, NULL }, + { "version", 1610874883, NULL }, + { "v1", 1, "0"}, + { "subject", 1073741826, "Name"}, + { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "attributes", 536879106, "Attributes"}, + { NULL, 4104, "0"}, + { "Attributes", 1610612751, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-10-CertificationRequest", 1610612741, NULL }, + { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "pkcs-9-at-challengePassword", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "7"}, + { "pkcs-9-challengePassword", 1610612754, NULL }, + { "printableString", 1073741826, "PrintableString"}, + { "utf8String", 2, "UTF8String"}, + { "pkcs-9-at-localKeyId", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "21"}, + { "pkcs-9-localKeyId", 1073741831, NULL }, + { "pkcs-9-at-friendlyName", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "20"}, + { "pkcs-9-friendlyName", 1612709890, "BMPString"}, + { "255", 524298, "1"}, + { "pkcs-8-PrivateKeyInfo", 1610612741, NULL }, + { "version", 1073741826, "pkcs-8-Version"}, + { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "privateKey", 1073741826, "pkcs-8-PrivateKey"}, + { "attributes", 536895490, "Attributes"}, + { NULL, 4104, "0"}, + { "pkcs-8-Version", 1610874883, NULL }, + { "v1", 1, "0"}, + { "pkcs-8-PrivateKey", 1073741831, NULL }, + { "pkcs-8-Attributes", 1610612751, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL }, + { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "encryptedData", 2, "pkcs-8-EncryptedData"}, + { "pkcs-8-EncryptedData", 1073741831, NULL }, + { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-aes128-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes192-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes256-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-PBES2-params", 1610612741, NULL }, + { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + { "encryptionScheme", 2, "AlgorithmIdentifier"}, + { "pkcs-5-PBKDF2-params", 1610612741, NULL }, + { "salt", 1610612754, NULL }, + { "specified", 1073741831, NULL }, + { "otherSource", 2, "AlgorithmIdentifier"}, + { "iterationCount", 1611137027, NULL }, + { "1", 10, "MAX"}, + { "keyLength", 1611153411, NULL }, + { "1", 10, "MAX"}, + { "prf", 16386, "AlgorithmIdentifier"}, + { "pkcs-12-PFX", 1610612741, NULL }, + { "version", 1610874883, NULL }, + { "v3", 1, "3"}, + { "authSafe", 1073741826, "pkcs-7-ContentInfo"}, + { "macData", 16386, "pkcs-12-MacData"}, + { "pkcs-12-PbeParams", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterations", 3, NULL }, + { "pkcs-12-MacData", 1610612741, NULL }, + { "mac", 1073741826, "pkcs-7-DigestInfo"}, + { "macSalt", 1073741831, NULL }, + { "iterations", 536903683, NULL }, + { NULL, 9, "1"}, + { "pkcs-12-AuthenticatedSafe", 1610612747, NULL }, + { NULL, 2, "pkcs-7-ContentInfo"}, + { "pkcs-12-SafeContents", 1610612747, NULL }, + { NULL, 2, "pkcs-12-SafeBag"}, + { "pkcs-12-SafeBag", 1610612741, NULL }, + { "bagId", 1073741836, NULL }, + { "bagValue", 1614815245, NULL }, + { NULL, 1073743880, "0"}, + { "badId", 1, NULL }, + { "bagAttributes", 536887311, NULL }, + { NULL, 2, "pkcs-12-PKCS12Attribute"}, + { "pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + { "pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, + { "pkcs-12-CertBag", 1610612741, NULL }, + { "certId", 1073741836, NULL }, + { "certValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "certId", 1, NULL }, + { "pkcs-12-CRLBag", 1610612741, NULL }, + { "crlId", 1073741836, NULL }, + { "crlValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "crlId", 1, NULL }, + { "pkcs-12-SecretBag", 1610612741, NULL }, + { "secretTypeId", 1073741836, NULL }, + { "secretValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "secretTypeId", 1, NULL }, + { "pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + { "pkcs-7-Data", 1073741831, NULL }, + { "pkcs-7-EncryptedData", 1610612741, NULL }, + { "version", 1073741826, "pkcs-7-CMSVersion"}, + { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + { NULL, 4104, "1"}, + { "pkcs-7-EncryptedContentInfo", 1610612741, NULL }, + { "contentType", 1073741826, "pkcs-7-ContentType"}, + { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + { "encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + { NULL, 4104, "0"}, + { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "pkcs-7-EncryptedContent", 1073741831, NULL }, + { "pkcs-7-UnprotectedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "id-at-ldap-DC", 1880096780, "AttributeType"}, + { NULL, 1073741825, "0"}, + { NULL, 1073741825, "9"}, + { NULL, 1073741825, "2342"}, + { NULL, 1073741825, "19200300"}, + { NULL, 1073741825, "100"}, + { NULL, 1073741825, "1"}, + { NULL, 1, "25"}, + { "ldap-DC", 1073741826, "IA5String"}, + { "id-at-ldap-UID", 1880096780, "AttributeType"}, + { NULL, 1073741825, "0"}, + { NULL, 1073741825, "9"}, + { NULL, 1073741825, "2342"}, + { NULL, 1073741825, "19200300"}, + { NULL, 1073741825, "100"}, + { NULL, 1073741825, "1"}, + { NULL, 1, "1"}, + { "ldap-UID", 1073741826, "DirectoryString"}, + { "id-pda-dateOfBirth", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "1"}, + { "DateOfBirth", 1082130449, NULL }, + { "id-pda-placeOfBirth", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "2"}, + { "PlaceOfBirth", 1073741826, "DirectoryString"}, + { "id-pda-gender", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "3"}, + { "Gender", 1612709890, "PrintableString"}, + { NULL, 1048586, "1"}, + { "id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "4"}, + { "CountryOfCitizenship", 1612709890, "PrintableString"}, + { NULL, 1048586, "2"}, + { "id-pda-countryOfResidence", 1880096780, "AttributeType"}, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "5"}, + { "CountryOfResidence", 1612709890, "PrintableString"}, + { NULL, 1048586, "2"}, + { "ProxyCertInfo", 1610612741, NULL }, + { "pCPathLenConstraint", 1611153411, NULL }, + { "0", 10, "MAX"}, + { "proxyPolicy", 2, "ProxyPolicy"}, + { "ProxyPolicy", 1610612741, NULL }, + { "policyLanguage", 1073741836, NULL }, + { "policy", 16391, NULL }, + { "id-on-xmppAddr", 1879048204, NULL }, + { NULL, 1073741825, "id-pkix"}, + { NULL, 1073741825, "8"}, + { NULL, 1, "5"}, + { "XmppAddr", 2, "UTF8String"}, + { NULL, 0, NULL } +}; diff --git a/tests/virnettlscontexttest.c b/tests/virnettlscontexttest.c new file mode 100644 index 0000000..c6c6d3b --- /dev/null +++ b/tests/virnettlscontexttest.c @@ -0,0 +1,1224 @@ +/* + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdlib.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include "testutils.h" +#include "util.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" +#include "files.h" +#include "command.h" +#include "network.h" + +#include <libtasn1.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include "gnutls_1_0_compat.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 entroy to generate keys, + * here's one we prepared earlier :-) + */ +gnutls_x509_privkey_t privkey; +#define PRIVATE_KEY \ + "-----BEGIN PRIVATE KEY-----\n" \ + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr" \ + "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE" \ + "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9" \ + "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc" \ + "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL" \ + "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H" \ + "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn" \ + "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO" \ + "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J" \ + "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK" \ + "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA" \ + "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa" \ + "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd" \ + "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W" \ + "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp" \ + "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; + + /* if zero, then the current time will be used */ + time_t start; + time_t expire; +}; + + +/* + * 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_crq_t crq; + 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 = { (unsigned char *)buffer, size }; + time_t start = req->start; + time_t expire = req->expire; + + if (!start) + start = time(NULL); + if (!expire) + expire = time(NULL) + (60*60*24); + + /* + * First up generate a certificate request with some basic + * data. This seems a little pointless. We can probably + * just set this all on the certifivate object directly + * and avoid CRQs entirely. Another day.... + */ + if ((err = gnutls_x509_crq_init(&crq)) < 0) + goto error; + if ((err = gnutls_x509_crq_set_key(crq, privkey)) < 0) + goto error; + if ((err = gnutls_x509_crq_set_version(crq, 3)) < 0) + goto error; + + if (req->country) { + if ((err = gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COUNTRY_NAME, 0, + req->country, strlen(req->country))) < 0) + goto error; + } + if (req->cn) { + if ((err = gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COMMON_NAME, 0, + req->cn, strlen(req->cn))) < 0) + goto error; + } + + if ((err = gnutls_x509_crq_sign(crq, privkey)) < 0) + goto error; + + + /* + * Prepare our new certificate object + */ + if ((err = gnutls_x509_crt_init(&crt)) < 0) + goto error; + + /* + * 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 ((err = gnutls_x509_crt_set_crq(crt, crq)) < 0) + goto error; + + + /* + * 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))) + goto error; + } + if (req->altname2) { + if ((err = gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, + req->altname2, + strlen(req->altname2), + GNUTLS_FSAN_APPEND))) + goto error; + } + + /* + * 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 (virSocketParseAddr(req->ipaddr1, &addr, 0) < 0) + goto error; + + 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))) + goto error; + } + if (req->ipaddr2) { + virSocketAddr addr; + char *data; + int len; + if (virSocketParseAddr(req->ipaddr2, &addr, 0) < 0) + goto error; + + 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))) + goto error; + } + + + /* + * 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); + gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.19", + der.data, + der.size, + req->basicConstraintsCritical); + + 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); + gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.15", + der.data, + der.size, + req->keyUsageCritical); + + 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); + gnutls_x509_crt_set_extension_by_oid(crt, + "2.5.29.37", + der.data, + der.size, + req->keyPurposeCritical); + + 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) + goto error; + + if ((err = gnutls_x509_crt_set_activation_time(crt, start)) < 0) + goto error; + if ((err = gnutls_x509_crt_set_expiration_time(crt, expire)) < 0) + goto error; + + + /* + * 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)) + goto error; + + /* + * Finally write the new cert out to disk + */ + if ((err = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) + goto error; + + ignore_value(virFileWriteStr(req->filename, buffer, 0600)); + + gnutls_x509_crq_deinit(crq); + + req->crt = crt; + return; + +error: + VIR_ERROR("Failed %s", gnutls_strerror(err)); + abort(); +} + + +/* + * 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) }; + + gnutls_x509_privkey_init(&key); + + gnutls_x509_privkey_import(key, &data, GNUTLS_X509_FMT_PEM); + + return key; +} + + +struct testTLSContextData { + bool isServer; + struct testTLSCertReq careq; + struct testTLSCertReq certreq; + bool expectFail; +}; + + +/* + * This tests sanity checking of our own certificates + * + * This code is done when libvirtd starts up, or before + * a libvirt client connects. The test is ensuring that + * the creation of virNetTLSContextPtr fails if we + * give bogus certs, or suceeds for good certs + */ +static int testTLSContextInit(const void *opaque) +{ + struct testTLSContextData *data = (struct testTLSContextData *)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, + data->certreq.filename, + keyfile, + NULL, + true, + true); + } else { + ctxt = virNetTLSContextNewClient(data->careq.filename, + NULL, + data->certreq.filename, + keyfile, + true, + true); + } + + if (ctxt) { + if (data->expectFail) { + VIR_ERROR("Expected failure %s against %s", + data->careq.filename, data->certreq.filename); + goto cleanup; + } + } else { + virErrorPtr err = virGetLastError(); + if (!data->expectFail) { + VIR_ERROR("Unexpected failure %s against %s", + data->careq.filename, data->certreq.filename); + goto cleanup; + } + VIR_DEBUG("Got error %s", err ? err->message : "<unknown>"); + } + + ret = 0; + +cleanup: + virNetTLSContextFree(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); + } + 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_ERROR("Unexpected failure loading %s against %s", + data->careq.filename, data->serverreq.filename); + goto cleanup; + } + if (!clientCtxt) { + VIR_ERROR("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_ERROR("Unexpected failure using %s against %s", + data->careq.filename, data->serverreq.filename); + goto cleanup; + } + if (!clientSess) { + VIR_ERROR("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) + serverShake = true; + } + } while (!clientShake && !serverShake); + + + /* Finally make sure the server validation does what + * we were expecting + */ + if (virNetTLSContextCheckCertificate(serverCtxt, + serverSess) < 0) { + if (!data->expectServerFail) { + VIR_ERROR("Unexpected server cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Got expected server cert fail"); + } + } else { + if (data->expectServerFail) { + VIR_ERROR("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_ERROR("Unexpected client cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Got expected client cert fail"); + } + } else { + if (data->expectClientFail) { + VIR_ERROR("Expected client cert check fail"); + goto cleanup; + } else { + VIR_DEBUG("Not unexpected client cert fail"); + } + } + + ret = 0; + +cleanup: + virNetTLSContextFree(serverCtxt); + virNetTLSContextFree(clientCtxt); + 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; + +#define DO_CTX_TEST(isServer, caReq, certReq, expectFail) \ + do { \ + struct testTLSContextData data = { \ + isServer, caReq, certReq, expectFail, \ + }; \ + if (virtTestRun("TLS Context", 1, testTLSContextInit, &data) < 0) \ + ret = -1; \ + } while (0) + +#define DO_SESS_TEST(caReq, serverReq, clientReq, expectServerFail, expectClientFail, hostname, wildcards) \ + do { \ + struct testTLSSessionData data = { \ + caReq, { 0 }, serverReq, clientReq, \ + expectServerFail, expectClientFail, hostname, 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 { \ + struct testTLSSessionData data = { \ + caReq, othercaReq, serverReq, clientReq, \ + expectServerFail, expectClientFail, hostname, wildcards \ + }; \ + if (virtTestRun("TLS Session", 1, testTLSSessionInit, &data) < 0) \ + ret = -1; \ + } while (0) + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + 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, + }; + 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, + }; + 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_CTX_TEST(true, cacertreq, servercertreq, false); + DO_CTX_TEST(false, cacertreq, clientcertreq, false); + + + /* Some other CAs which are good */ + + /* Basic:CA:critical */ + 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, + }; + /* Basic:CA:not-critical */ + 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, + }; + /* Basic:not-CA:not-critical */ +#if 0 + /* Default GNUTLS session config forbids use of CAs without + * basic constraints, so skip this otherwise valid test + */ + 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 */ + 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, + 0, 0, + }; + /* Key usage:dig-sig:not-critical */ + struct testTLSCertReq cacert5req = { + NULL, NULL, "cacert5.pem", "UK", + "libvirt CA 5", NULL, NULL, NULL, NULL, + true, true, true, + true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0, + }; + + 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); + DO_CTX_TEST(true, cacert5req, servercertreq, false); + + /* Now some bad certs */ + + /* no-basic */ + struct testTLSCertReq cacert6req = { + NULL, NULL, "cacert6.pem", "UK", + "libvirt CA 6", NULL, NULL, NULL, NULL, + false, false, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0, + }; + /* Key usage:dig-sig:critical */ + struct testTLSCertReq cacert7req = { + NULL, NULL, "cacert7.pem", "UK", + "libvirt CA 7", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0, + }; + + DO_CTX_TEST(true, cacert6req, servercertreq, true); + DO_CTX_TEST(true, cacert7req, servercertreq, true); + + + /* Various good servers */ + /* no usage or purpose */ + 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, + }; + /* usage:cert-sign+dig-sig+encipher:critical */ + 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, + }; + /* usage:cert-sign:not-critical */ + 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, + }; + /* purpose:server:critical */ + 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, + }; + /* purpose:server:not-critical */ + 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, + }; + /* purpose:client+server:critical */ + 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, + }; + /* purpose:client+server:not-critical */ + 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); + DO_CTX_TEST(true, cacertreq, servercert7req, false); + /* Bad servers */ + + /* usage:cert-sign:critical */ + 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, + }; + /* purpose:client:critical */ + 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, + }; + /* usage: none:critical */ + 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, + }; + + DO_CTX_TEST(true, cacertreq, servercert8req, true); + DO_CTX_TEST(true, cacertreq, servercert9req, true); + DO_CTX_TEST(true, cacertreq, servercert10req, true); + + + + /* Various good clients */ + /* no usage or purpose */ + 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, + }; + /* usage:cert-sign+dig-sig+encipher:critical */ + 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, + }; + /* usage:cert-sign:not-critical */ + 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, + }; + /* purpose:client:critical */ + 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, + }; + /* purpose:client:not-critical */ + 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, + }; + /* purpose:client+client:critical */ + 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, + }; + /* purpose:client+client:not-critical */ + 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, + }; + + 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); + /* Bad clients */ + + /* usage:cert-sign:critical */ + 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, + }; + /* purpose:client:critical */ + 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, + }; + /* usage: none:critical */ + 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, + }; + + DO_CTX_TEST(false, cacertreq, clientcert8req, true); + DO_CTX_TEST(false, cacertreq, clientcert9req, true); + DO_CTX_TEST(false, cacertreq, clientcert10req, true); + + + + /* Expired stuff */ + + 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, + }; + 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, + }; + 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); + + + /* Not activated stuff */ + + 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, + time(NULL)+(60*60), time(NULL)+(60*60*2), + }; + 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, + time(NULL)+(60*60), time(NULL)+(60*60*2), + }; + 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, + time(NULL)+(60*60), time(NULL)+(60*60*2), + }; + + DO_CTX_TEST(true, cacertnewreq, servercertreq, true); + 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 */ + 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 */ + 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); + + return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +VIRT_TEST_MAIN(mymain) -- 1.7.6

On 07/21/2011 06:30 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
This test case checks certification validation rules for
- Basic constraints - Key purpose - Key usage - Start/expiry times
It checks initial context creation sanity checks, and live session validation --- tests/.gitignore | 1 +
We've got half our tests excluded in libvirt/.gitignore, the other half in libvirt/tests/.gitignore. Someday I should follow through with my threat to consolidate all .gitignore into the top level file. But that's a separate patch, so don't worry about it in the context of this patch.
+virnettlscontexttest_SOURCES = \ + virnettlscontexttest.c testutils.h testutils.c pkix_asn1_tab.c +virnettlscontexttest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virnettlscontexttest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) -ltasn1
Is -tasn1 available everywhere, or do we need to make compilation of this test conditional? Also, I don't see tasn mentioned anywhere else in the current libvirt.git tree - does this require some configure.ac magic?
+ * This file comes from gnutls, licensed under the GPLv3+
I guess that's okay, since our test is not installed.
+ */ + +#include<config.h> +#include<libtasn1.h> + +const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL },
How grungy. And no comments to tell you what it is actually testing. Oh well; it's copied from elsewhere, so hopefully gnutls knows what it is doing.
+ +/* + * To avoid consuming random entroy to generate keys,
s/entroy/entropy/
+ * here's one we prepared earlier :-)
Thanks. That would be a shame if running 'make check' ate entropy.
+ + /* + * First up generate a certificate request with some basic + * data. This seems a little pointless. We can probably + * just set this all on the certifivate object directly
s/certifivate/certificate/
+ /* We'll use this for our fake client-server connection */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel)< 0) + abort();
Won't compile on Win32, so you definitely need conditional compilation of this test. Overall the idea is nice. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Thu, Jul 21, 2011 at 04:51:37PM -0600, Eric Blake wrote:
On 07/21/2011 06:30 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
This test case checks certification validation rules for
- Basic constraints - Key purpose - Key usage - Start/expiry times
It checks initial context creation sanity checks, and live session validation --- tests/.gitignore | 1 +
We've got half our tests excluded in libvirt/.gitignore, the other half in libvirt/tests/.gitignore.
Someday I should follow through with my threat to consolidate all .gitignore into the top level file. But that's a separate patch, so don't worry about it in the context of this patch.
+virnettlscontexttest_SOURCES = \ + virnettlscontexttest.c testutils.h testutils.c pkix_asn1_tab.c +virnettlscontexttest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virnettlscontexttest_LDADD = ../src/libvirt-net-rpc.la $(LDADDS) -ltasn1
Is -tasn1 available everywhere, or do we need to make compilation of this test conditional? Also, I don't see tasn mentioned anywhere else in the current libvirt.git tree - does this require some configure.ac magic?
GNUTLS links to libtasn1, so if we have GNUTLS we can expect to have libtasn1 too. We have a hard dep on GNUTLS, so there's no need for a conditional for that.
+ */ + +#include<config.h> +#include<libtasn1.h> + +const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL },
How grungy. And no comments to tell you what it is actually testing. Oh well; it's copied from elsewhere, so hopefully gnutls knows what it is doing.
This isn't actually testing stuff. This is a data structure that defines fields/values for x509 certificates as per the relevant RFC. If you want to generate various fields for x509 certs, you simply need this data to be able todo so.
+ /* We'll use this for our fake client-server connection */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel)< 0) + abort();
Won't compile on Win32, so you definitely need conditional compilation of this test.
Oh good point, should fix that. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Eric Blake