From: Tim Kosse Date: Sun, 20 Dec 2015 14:09:24 +0000 (+0100) Subject: Account the TLSFeature certificate extension in certificate verification X-Git-Tag: gnutls_3_5_1~81 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1eb8b3dcc6c790db88889d67dc23f65aa75341d2;p=thirdparty%2Fgnutls.git Account the TLSFeature certificate extension in certificate verification That is, account for the OCSP-Must staple extension. If we have sent an OCSP status request and have not gotten anything, but the certificate has the Status Request TLSFeature extension present, fail to verify the certificate. --- diff --git a/lib/cert.c b/lib/cert.c index bcd0d2d9da..a43f487d3d 100644 --- a/lib/cert.c +++ b/lib/cert.c @@ -986,6 +986,11 @@ gnutls_certificate_verification_status_print(unsigned int status, _ ("The name in the certificate does not match the expected. ")); + if (status & GNUTLS_CERT_MISSING_OCSP_STATUS) + _gnutls_buffer_append_str(&str, + _ + ("The certificate requires the server to include an OCSP status in its response, but the OCSP status is missing. ")); + return _gnutls_buffer_to_datum(&str, out, 1); } diff --git a/lib/extensions.c b/lib/extensions.c index 988d9a4627..185fd14c73 100644 --- a/lib/extensions.c +++ b/lib/extensions.c @@ -160,7 +160,7 @@ const char *gnutls_ext_get_name(unsigned int ext) /* Checks if the extension we just received is one of the * requested ones. Otherwise it's a fatal error. */ -static int +int _gnutls_extension_list_check(gnutls_session_t session, uint16_t type) { int i; diff --git a/lib/extensions.h b/lib/extensions.h index 7ef1b64782..891c69b3d3 100644 --- a/lib/extensions.h +++ b/lib/extensions.h @@ -35,6 +35,7 @@ int _gnutls_ext_init(void); void _gnutls_ext_deinit(void); void _gnutls_extension_list_add(gnutls_session_t session, uint16_t type); +int _gnutls_extension_list_check(gnutls_session_t session, uint16_t type); void _gnutls_ext_free_session_data(gnutls_session_t session); diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 0f35358d40..65e15fc5c0 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -530,6 +530,7 @@ const char * @GNUTLS_CERT_UNEXPECTED_OWNER: The owner is not the expected one. * @GNUTLS_CERT_MISMATCH: The certificate presented isn't the expected one (TOFU) * @GNUTLS_CERT_PURPOSE_MISMATCH: The certificate or an intermediate does not match the intended purpose (extended key usage). + * @GNUTLS_CERT_MISSING_OCSP_STATUS: The certificate requires the server to send the certifiate status, but no status was received. * * Enumeration of certificate status codes. Note that the status * bits may have different meanings in OpenPGP keys and X.509 @@ -549,7 +550,8 @@ typedef enum { GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE = 1 << 15, GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE = 1 << 16, GNUTLS_CERT_MISMATCH = 1 << 17, - GNUTLS_CERT_PURPOSE_MISMATCH = 1 << 18 + GNUTLS_CERT_PURPOSE_MISMATCH = 1 << 18, + GNUTLS_CERT_MISSING_OCSP_STATUS = 1 << 19 } gnutls_certificate_status_t; /** diff --git a/lib/x509.c b/lib/x509.c index 3d46caecff..f407f74478 100644 --- a/lib/x509.c +++ b/lib/x509.c @@ -24,6 +24,7 @@ #include "gnutls_int.h" #include "auth.h" #include "errors.h" +#include "extensions.h" #include #include "dh.h" #include "num.h" @@ -174,6 +175,52 @@ check_ocsp_response(gnutls_session_t session, gnutls_x509_crt_t cert, } \ gnutls_free( peer_certificate_list) +#ifdef ENABLE_OCSP +static int +_gnutls_ocsp_verify_mandatory_stapling(gnutls_session_t session, + gnutls_x509_crt_t cert, + unsigned int * ocsp_status) +{ + gnutls_x509_tlsfeatures_t tlsfeatures; + int i, ret; + unsigned feature; + + /* RFC 7633: If cert has TLS feature GNUTLS_EXTENSION_STATUS_REQUEST, stapling is mandatory. + * + * At this point, we know that we did not get the certificate status. + * + * To proceed, first check whether we have requested the certificate status + */ + if (_gnutls_extension_list_check(session, GNUTLS_EXTENSION_STATUS_REQUEST) < 0) { + return 0; + } + + /* We have requested the status, now check whether the certificate mandates a response */ + if (gnutls_x509_crt_get_tlsfeatures(cert, &tlsfeatures) == 0) { + for (i = 0;; ++i) { + ret = gnutls_x509_tlsfeatures_get(tlsfeatures, i, &feature); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + break; + } + + if (ret < 0) { + gnutls_assert(); + gnutls_x509_tlsfeatures_deinit(tlsfeatures); + return ret; + } + if (feature == GNUTLS_EXTENSION_STATUS_REQUEST) { + /* We sent a status request, the certificate mandates a reply, but we did not get any. */ + *ocsp_status |= GNUTLS_CERT_MISSING_OCSP_STATUS; + break; + } + } + gnutls_x509_tlsfeatures_deinit(tlsfeatures); + } + + return 0; +} +#endif + /*- * _gnutls_x509_cert_verify_peers - return the peer's certificate status * @session: is a gnutls session @@ -264,8 +311,16 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session, goto skip_ocsp; ret = gnutls_ocsp_status_request_get(session, &resp); - if (ret < 0) + if (ret < 0) { + ret = _gnutls_ocsp_verify_mandatory_stapling(session, peer_certificate_list[0], &ocsp_status); + if (ret < 0) { + gnutls_assert(); + CLEAR_CERTS; + return ret; + } + goto skip_ocsp; + } if (peer_certificate_list_size > 1) { issuer = peer_certificate_list[1];