CVE-2014-8155 (published 2015-08-14T18:59Z)

GnuTLS before 2.9.10 does not verify the activation and expiration dates of CA certificates, which allows man-in-the-middle attackers to spoof servers via a certificate issued by a CA certificate that is (1) not yet valid or (2) no longer valid.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:P/A:N)

Weakness information:
cwe_id: CWE-17
cwe_name: DEPRECATED: Code
description: This entry has been deprecated.  It was originally used for organizing the Development View (CWE-699) and some other views, but it introduced unnecessary complexity and depth to the resulting tree.

Fix information:
repo_url: https://gitlab.com/gnutls/gnutls
commit_details: repo_url: https://gitlab.com/gnutls/gnutls
author: Nikos Mavrogiannopoulos
author_date: 2010-03-14 22:57:55+01:00
msg: Extended time verification to trusted certificate list as well. Introduced
the flag GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS that will prevent the
trusted certificate list verification.
repository: repo_url: https://gitlab.com/gnutls/gnutls
repo_name: visit repo url
description: visit repo url
repo_language: visit repo url
owner: gnutls
file_changes: filename: NEWS
change_type: ModificationType.MODIFY
diff: @@ -5,6 +5,9 @@ See the end for copying conditions.
 
 * Version 2.9.10 (unreleased)
 
+** libgnutls: Time verification extended to trusted certificate list,
+unless GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS flag is specified.
+
 ** libgnutls: Added Steve Dispensa's patch for safe renegotiation (RFC 5746)
 Solves the issue discussed in:
 <http://www.ietf.org/mail-archive/web/tls/current/msg03928.html> and

programming_language: Markdown
filename: x509.h
change_type: ModificationType.MODIFY
diff: @@ -500,6 +500,8 @@ extern "C"
  * @GNUTLS_VERIFY_DISABLE_CA_SIGN: If set a signer does not have to be
  *   a certificate authority. This flag should normaly be disabled,
  *   unless you know what this means.
+ * @GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS: If set a signer in the trusted
+ *   list is never checked for expiration or activation.
  * @GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT: Allow only trusted CA
  *   certificates that have version 1.  This is safer than
  *   %GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT, and should be used
@@ -531,7 +533,8 @@ extern "C"
       GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT = 8,
       GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2 = 16,
       GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 = 32,
-      GNUTLS_VERIFY_DISABLE_TIME_CHECKS = 64
+      GNUTLS_VERIFY_DISABLE_TIME_CHECKS = 64,
+      GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS = 128,
     } gnutls_certificate_verify_flags;
 
   int gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert,

programming_language: C
filename: verify.c
change_type: ModificationType.MODIFY
diff: @@ -42,7 +42,7 @@
 static int _gnutls_verify_certificate2 (gnutls_x509_crt_t cert,
 					const gnutls_x509_crt_t * trusted_cas,
 					int tcas_size, unsigned int flags,
-					unsigned int *output);
+					unsigned int *output, gnutls_x509_crt_t* issuer);
 
 static int is_crl_issuer (gnutls_x509_crl_t crl,
 			  gnutls_x509_crt_t issuer_cert);
@@ -261,17 +261,17 @@ find_issuer (gnutls_x509_crt_t cert,
  * 'flags': an OR of the gnutls_certificate_verify_flags enumeration.
  *
  * Output will hold some extra information about the verification
- * procedure.
+ * procedure. Issuer will hold the actual issuer from the trusted list.
  */
 static int
 _gnutls_verify_certificate2 (gnutls_x509_crt_t cert,
 			     const gnutls_x509_crt_t * trusted_cas,
 			     int tcas_size, unsigned int flags,
-			     unsigned int *output)
+			     unsigned int *output, gnutls_x509_crt_t *_issuer)
 {
   gnutls_datum_t cert_signed_data = { NULL, 0 };
   gnutls_datum_t cert_signature = { NULL, 0 };
-  gnutls_x509_crt_t issuer;
+  gnutls_x509_crt_t issuer = NULL;
   int ret, issuer_version, result;
 
   if (output)
@@ -298,6 +298,8 @@ _gnutls_verify_certificate2 (gnutls_x509_crt_t cert,
       return 0;
     }
 
+  if (_issuer != NULL) *_issuer = issuer;
+
   issuer_version = gnutls_x509_crt_get_version (issuer);
   if (issuer_version < 0)
     {
@@ -399,6 +401,29 @@ gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert,
   return is_issuer (cert, issuer);
 }
 
+static unsigned int check_time(gnutls_x509_crt_t crt, time_t now)
+{
+int status = 0;
+time_t t;
+
+  t = gnutls_x509_crt_get_activation_time (crt);
+  if (t == (time_t) - 1 || now < t)
+    {
+      status |= GNUTLS_CERT_NOT_ACTIVATED;
+      status |= GNUTLS_CERT_INVALID;
+      return status;
+    }
+
+  t = gnutls_x509_crt_get_expiration_time (crt);
+  if (t == (time_t) - 1 || now > t)
+    {
+      status |= GNUTLS_CERT_EXPIRED;
+      status |= GNUTLS_CERT_INVALID;
+      return status;
+    }
+  
+  return 0;
+}
 
 /* Verify X.509 certificate chain.
  *
@@ -417,6 +442,8 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
 {
   int i = 0, ret;
   unsigned int status = 0, output;
+  time_t now = time (0);
+  gnutls_x509_crt_t issuer = NULL;
 
   if (clist_size > 1)
     {
@@ -452,6 +479,17 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
 	      if (check_if_same_cert (certificate_list[i],
 				      trusted_cas[j]) == 0)
 		{
+		  /* explicity time check for trusted CA that we remove from
+		   * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS
+		   */
+		  if (!(flags&GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) && !(flags&GNUTLS_VERIFY_DISABLE_TIME_CHECKS))
+		    {
+		      status |= check_time(trusted_cas[j], now);
+		      if (status != 0)
+		        {
+		          return status;
+                        }
+		    }
 		  clist_size = i;
 		  break;
 		}
@@ -472,7 +510,7 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
    * in self signed etc certificates.
    */
   ret = _gnutls_verify_certificate2 (certificate_list[clist_size - 1],
-				     trusted_cas, tcas_size, flags, &output);
+				     trusted_cas, tcas_size, flags, &output, &issuer);
   if (ret == 0)
     {
       /* if the last certificate in the certificate
@@ -501,27 +539,32 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
     }
 #endif
 
+
   /* Check activation/expiration times
    */
   if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS))
     {
-      time_t t, now = time (0);
+      /* check the time of the issuer first */
+      if (!(flags&GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS))
+        {
+          if (issuer == NULL)
+            {
+              gnutls_assert();
+              return GNUTLS_E_INTERNAL_ERROR;
+            }
+
+          status |= check_time(issuer, now);
+          if (status != 0)
+            {
+              return status;
+            }
+        }
 
       for (i = 0; i < clist_size; i++)
 	{
-	  t = gnutls_x509_crt_get_activation_time (certificate_list[i]);
-	  if (t == (time_t) - 1 || now < t)
-	    {
-	      status |= GNUTLS_CERT_NOT_ACTIVATED;
-	      status |= GNUTLS_CERT_INVALID;
-	      return status;
-	    }
-
-	  t = gnutls_x509_crt_get_expiration_time (certificate_list[i]);
-	  if (t == (time_t) - 1 || now > t)
+	  status |= check_time(certificate_list[i], now);
+	  if (status != 0)
 	    {
-	      status |= GNUTLS_CERT_EXPIRED;
-	      status |= GNUTLS_CERT_INVALID;
 	      return status;
 	    }
 	}
@@ -542,7 +585,7 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
       if ((ret =
 	   _gnutls_verify_certificate2 (certificate_list[i - 1],
 					&certificate_list[i], 1, flags,
-					NULL)) == 0)
+					NULL, NULL)) == 0)
 	{
 	  status |= GNUTLS_CERT_INVALID;
 	  return status;
@@ -1009,7 +1052,7 @@ gnutls_x509_crt_verify (gnutls_x509_crt_t cert,
    */
   ret =
     _gnutls_verify_certificate2 (cert, CA_list, CA_list_length, flags,
-				 verify);
+				 verify, NULL);
   if (ret < 0)
     {
       gnutls_assert ();

programming_language: C