]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Account the TLSFeature certificate extension in certificate verification
authorTim Kosse <tim.kosse@filezilla-project.org>
Sun, 20 Dec 2015 14:09:24 +0000 (15:09 +0100)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 30 May 2016 12:06:38 +0000 (14:06 +0200)
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.

lib/cert.c
lib/extensions.c
lib/extensions.h
lib/includes/gnutls/gnutls.h.in
lib/x509.c

index bcd0d2d9da224cb184a333014661e53f7f6f0f4f..a43f487d3d80e32593dac2a752844336e284d736 100644 (file)
@@ -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);
 }
 
index 988d9a4627230f6f61d472b8c96bffbc69cf5cf4..185fd14c73bf6ead682ecfe2fd43a856fc393eaf 100644 (file)
@@ -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;
index 7ef1b647829683903cadf5490c156e7cade800dc..891c69b3d3533f1990183a46545c59e5459996bd 100644 (file)
@@ -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);
 
index 0f35358d4066010b30b38923fb78ed5faa244258..65e15fc5c0342d8e21d74f5609f20c60c091bb6f 100644 (file)
@@ -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;
 
 /**
index 3d46caecff9f771d324a486b7d0dd7afe80ad6f2..f407f7447894526eede47d10e921082b7ce85937 100644 (file)
@@ -24,6 +24,7 @@
 #include "gnutls_int.h"
 #include "auth.h"
 #include "errors.h"
+#include "extensions.h"
 #include <auth/cert.h>
 #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];