From 637711cbdfb83b915eefb0178135244f7d96fe47 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 20 Jul 2011 15:18:51 +0100 Subject: [PATCH] Refactor the certification validation code 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 | 728 +++++++++++++++++++++---------------- 1 file changed, 414 insertions(+), 314 deletions(-) diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 10cfa51984..5c94df637f 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -97,32 +97,392 @@ 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) +{ + time_t now; + + if ((now = time(NULL)) == ((time_t)-1)) { + virReportSystemError(errno, "%s", + _("cannot get current time")); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + 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); + return -1; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + 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); + 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); + + if (status > 0) { /* It is a CA cert */ + if (!isCA) { + virNetError(VIR_ERR_SYSTEM_ERROR, 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); + 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); + 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); + return -1; + } + } else { /* General error */ + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %s basic constraints %s"), + certFile, gnutls_strerror(status)); + 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); + if (status < 0) { + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : + GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; + } else { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %s key usage %s"), + certFile, gnutls_strerror(status)); + return -1; + } + } + + if (isCA) { + if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + if (critical) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %s usage does not permit certificate signing"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit certificate signing", + certFile); + } + } + } else { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + if (critical) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %s usage does not permit digital signature"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit digital signature", + certFile); + } + } + if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + if (critical) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %s usage does not permit key encipherment"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s usage does not permit key encipherment", + certFile); + } + } + } + + 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; + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); + + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + VIR_DEBUG("No key purpose data available at slot %d", i); + + /* If there is no data at all, then we must allow client/server to pass */ + if (i == 0) + allowServer = allowClient = true; + break; + } + if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Unable to query certificate %s key purpose %s"), + certFile, gnutls_strerror(status)); + return -1; + } + + if (VIR_ALLOC_N(buffer, size) < 0) { + virReportOOMError(); + 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)); + return -1; + } + if (purposeCritical) + critical = true; + + VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical); + if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { + allowServer = true; + } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { + allowClient = true; + } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) { + allowServer = allowClient = true; + } + + VIR_FREE(buffer); + } + + if (isServer) { + if (!allowServer) { + if (critical) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %s purpose does not allow use for with a TLS server"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s purpose does not allow use for with a TLS server", + certFile); + } + } + } else { + if (!allowClient) { + if (critical) { + virNetError(VIR_ERR_SYSTEM_ERROR, + _("Certificate %s purpose does not allow use for with a TLS client"), + certFile); + return -1; + } else { + VIR_WARN("Certificate %s purpose does not allow use for with a TLS client", + certFile); + } + } + } + + 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; - 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")); @@ -143,180 +503,6 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer, goto cleanup; } - 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"), - certFile); - goto cleanup; - } - - 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"), - certFile); - goto cleanup; - } - - status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); - VIR_DEBUG("Cert %s basic constraints %d", certFile, status); - - if (status > 0) { /* It is a CA cert */ - if (!isCA) { - virNetError(VIR_ERR_SYSTEM_ERROR, 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; - } - } 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; - } - } 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; - } - } else { /* General error */ - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %s basic constraints %s"), - certFile, gnutls_strerror(status)); - goto cleanup; - } - - 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); - if (status < 0) { - if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : - GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; - } else { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %s key usage %s"), - certFile, gnutls_strerror(status)); - goto cleanup; - } - } - - if (isCA) { - if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { - if (critical) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %s usage does not permit certificate signing"), - certFile); - goto cleanup; - } else { - VIR_WARN("Certificate %s usage does not permit certificate signing", - certFile); - } - } - } else { - if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { - if (critical) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %s usage does not permit digital signature"), - certFile); - goto cleanup; - } else { - VIR_WARN("Certificate %s usage does not permit digital signature", - certFile); - } - } - if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { - if (critical) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %s usage does not permit key encipherment"), - certFile); - goto cleanup; - } else { - VIR_WARN("Certificate %s usage does not permit key encipherment", - certFile); - } - } - } - - 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) { - VIR_DEBUG("No key purpose data available at slot %d", i); - - /* If there is no data at all, then we must allow client/server to pass */ - if (i == 0) - allowServer = allowClient = true; - break; - } - if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %s key purpose %s"), - certFile, gnutls_strerror(status)); - goto cleanup; - } - - if (VIR_ALLOC_N(buffer, size) < 0) { - virReportOOMError(); - goto cleanup; - } - - status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical); - if (status < 0) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Unable to query certificate %s key purpose %s"), - certFile, gnutls_strerror(status)); - goto cleanup; - } - if (purposeCritical) - critical = true; - - VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical); - if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { - allowServer = true; - } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { - allowClient = true; - } else if (STRNEQ(buffer, GNUTLS_KP_ANY)) { - allowServer = allowClient = true; - } - - VIR_FREE(buffer); - } - - if (!isCA) { /* No purpose checks required for CA certs */ - if (isServer && !allowServer) { - if (critical) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %s purpose does not allow use for with a TLS server"), - certFile); - goto cleanup; - } else { - VIR_WARN("Certificate %s purpose does not allow use for with a TLS server", - certFile); - } - } - if (!isServer && !allowClient) { - if (critical) { - virNetError(VIR_ERR_SYSTEM_ERROR, - _("Certificate %s purpose does not allow use for with a TLS client"), - certFile); - goto cleanup; - } else { - VIR_WARN("Certificate %s purpose does not allow use for with a TLS client", - certFile); - } - } - } - - ret = 0; cleanup: @@ -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 ((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 (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 (cert && + virNetTLSContextCheckCert(cert, certFile, isServer, false) < 0) + goto cleanup; - if (status != 0) { - const char *reason = _("Invalid certificate"); + if (cacert && + virNetTLSContextCheckCert(cacert, cacertFile, isServer, true) < 0) + goto cleanup; - 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); - 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)); - gnutls_x509_crt_deinit(cert); - goto authfail; - } - - if (virNetTLSContextCheckDN(ctxt, name) <= 0) { + if (virNetTLSContextCheckCertDN(cert, "[session]", sess->hostname, + ctxt->x509dnWhitelist) < 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, 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 (virNetTLSContextCheckCertKeyUsage(cert, "[session]", + false) < 0) { + gnutls_x509_crt_deinit(cert); + goto authdeny; + } + + /* !sess->isServer - as above */ + if (virNetTLSContextCheckCertKeyPurpose(cert, "[session]", + !sess->isServer) < 0) { gnutls_x509_crt_deinit(cert); goto authdeny; }