Honour key usage/purpose criticality flag

If a key purpose or usage field is marked as non-critical in the
certificate, then a data mismatch is not (ordinarily) a cause for
rejecting the connection

* src/rpc/virnettlscontext.c: Honour key usage/purpose criticality
This commit is contained in:
Daniel P. Berrange 2011-07-20 14:08:39 +01:00
parent f53cc36fe8
commit 14800d49cb

View File

@ -111,6 +111,7 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer,
char *buffer = NULL; char *buffer = NULL;
size_t size; size_t size;
unsigned int usage; unsigned int usage;
unsigned int critical;
bool allowClient = false, allowServer = false; bool allowClient = false, allowServer = false;
VIR_DEBUG("isServer %d isCA %d certFile %s", VIR_DEBUG("isServer %d isCA %d certFile %s",
@ -190,9 +191,9 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer,
goto cleanup; goto cleanup;
} }
status = gnutls_x509_crt_get_key_usage(cert, &usage, NULL); status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical);
VIR_DEBUG("Cert %s key usage status %d usage %d", certFile, status, usage); VIR_DEBUG("Cert %s key usage status %d usage %d critical %u", certFile, status, usage, critical);
if (status < 0) { if (status < 0) {
if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN :
@ -207,28 +208,45 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer,
if (isCA) { if (isCA) {
if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
virNetError(VIR_ERR_SYSTEM_ERROR, if (critical) {
_("Certificate %s usage does not permit certificate signing"), virNetError(VIR_ERR_SYSTEM_ERROR,
certFile); _("Certificate %s usage does not permit certificate signing"),
goto cleanup; certFile);
goto cleanup;
} else {
VIR_WARN("Certificate %s usage does not permit certificate signing",
certFile);
}
} }
} else { } else {
if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
virNetError(VIR_ERR_SYSTEM_ERROR, if (critical) {
_("Certificate %s usage does not permit digital signature"), virNetError(VIR_ERR_SYSTEM_ERROR,
certFile); _("Certificate %s usage does not permit digital signature"),
goto cleanup; certFile);
goto cleanup;
} else {
VIR_WARN("Certificate %s usage does not permit digital signature",
certFile);
}
} }
if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) {
virNetError(VIR_ERR_SYSTEM_ERROR, if (critical) {
_("Certificate %s usage does not permit key encipherment"), virNetError(VIR_ERR_SYSTEM_ERROR,
certFile); _("Certificate %s usage does not permit key encipherment"),
goto cleanup; certFile);
goto cleanup;
} else {
VIR_WARN("Certificate %s usage does not permit key encipherment",
certFile);
}
} }
} }
critical = 0;
for (i = 0 ; ; i++) { for (i = 0 ; ; i++) {
size = 0; size = 0;
unsigned int purposeCritical;
status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL);
if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
@ -251,15 +269,17 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer,
goto cleanup; goto cleanup;
} }
status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, NULL); status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, &size, &purposeCritical);
if (status < 0) { if (status < 0) {
virNetError(VIR_ERR_SYSTEM_ERROR, virNetError(VIR_ERR_SYSTEM_ERROR,
_("Unable to query certificate %s key purpose %s"), _("Unable to query certificate %s key purpose %s"),
certFile, gnutls_strerror(status)); certFile, gnutls_strerror(status));
goto cleanup; goto cleanup;
} }
if (purposeCritical)
critical = true;
VIR_DEBUG("Key purpose %d %s", status, buffer); VIR_DEBUG("Key purpose %d %s critical %u", status, buffer, purposeCritical);
if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { if (STREQ(buffer, GNUTLS_KP_TLS_WWW_SERVER)) {
allowServer = true; allowServer = true;
} else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { } else if (STREQ(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) {
@ -273,16 +293,26 @@ static gnutls_x509_crt_t virNetTLSContextSanityCheckCert(bool isServer,
if (!isCA) { /* No purpose checks required for CA certs */ if (!isCA) { /* No purpose checks required for CA certs */
if (isServer && !allowServer) { if (isServer && !allowServer) {
virNetError(VIR_ERR_SYSTEM_ERROR, if (critical) {
_("Certificate %s purpose does not allow use for with a TLS server"), virNetError(VIR_ERR_SYSTEM_ERROR,
certFile); _("Certificate %s purpose does not allow use for with a TLS server"),
goto cleanup; certFile);
goto cleanup;
} else {
VIR_WARN("Certificate %s purpose does not allow use for with a TLS server",
certFile);
}
} }
if (!isServer && !allowClient) { if (!isServer && !allowClient) {
virNetError(VIR_ERR_SYSTEM_ERROR, if (critical) {
_("Certificate %s purpose does not allow use for with a TLS client"), virNetError(VIR_ERR_SYSTEM_ERROR,
certFile); _("Certificate %s purpose does not allow use for with a TLS client"),
goto cleanup; certFile);
goto cleanup;
} else {
VIR_WARN("Certificate %s purpose does not allow use for with a TLS client",
certFile);
}
} }
} }