Certificate expiration not checked by gnutls-cli [GNUTLS-SA-2009-3] [CVE-2009-1417]

Simon Josefsson simon at josefsson.org
Thu Apr 30 12:38:00 CEST 2009


Romain Francoise reported that gnutls-cli does not check the
activation and expiration dates of X.509 certificates.  This is
assumed to apply to all versions of gnutls-cli.

A patch for gnutls-cli to check the activation/expiration times on all
certificates supplied by the server was initially developed to address
this problem.  We then examined some other GnuTLS applications for
similar issues.  It appears as if Exim does not do any time
verification at all.  OpenLDAP and libsoup only checks timestamps on
the end-entity certificate, without checking timestamps on the chain.
Some applications, like Mutt, LFTP and Neon appears to do more
complete checks.

The GnuTLS APIs should make it easy to catch expired certificates.
Applications should not have to iterate through the certificates
supplied by the server and check expiration dates.  The
gnutls_certificate_activation_time_peers and
gnutls_certificate_expiration_time_peers functions can lead to a false
sense of security since they only check the peers' certificate without
checking expiration times of other certificates in the chain.

Today the documentation for gnutls_certificate_verify_peers* [3]
implies that the application has to check activation/expiration dates
themselves.  Thus, the reported problem is a bug in the command line
tool gnutls-cli, but is not a security problem in libgnutls.  However
we believe the Right Way to solve the problem is to modify libgnutls.

Checking expiration/activation times for certificate chains in every
application leads to duplication of complicated code.  Realizing this,
we believe it makes more sense for libgnutls, in
gnutls_certificate_verify_peers*, to check activation/expiration
times.  This leads to a default-secure behaviour.

The patch to implement time checking in libgnutls is in [PATCH] below.

The patch solves the original problem, but it also cause the library
to reject expired chains which the library did not use to do: it will
reject chains containing untrusted expired certificates.

We are concerned that changing the semantics of an existing function
in this way may be seen as backwards incompatible, but we believe
having a default-secure mode should carry more weight here.  We are
open to explore alternatives going forward.

Applications that use gnutls_certificate_verify_peers* and need to
accept expired chains have to modified to use the
GNUTLS_VERIFY_DISABLE_TIME_CHECKS verification flag.  We hope that
there are no such examples in real-world code, and that if there are,
the changed semantics that require a code change is an acceptable
price to pay in order to make more common application secure.

We have set up three demo URLs with expired certificates for testing
purposes:

https://expired.demo.gnutls.org/ - Expired server certificate

https://expired-subca.demo.gnutls.org/ - Expire intermediate certificate,
                               server return intermediate CA

https://expired-subca2.demo.gnutls.org/ - Expire intermediate certificate
                                server does not return intermediate CA

You can test them like this:

jas at mocca:~$ gnutls-cli expired.demo.gnutls.org
Resolving 'expired.demo.gnutls.org'...
Connecting to '207.192.75.61:443'...
- Ephemeral Diffie-Hellman parameters
 - Using prime: 2056 bits
 - Secret key: 2047 bits
 - Peer's public key: 2048 bits
- Certificate type: X.509
 - Got a certificate list of 1 certificates.

 - Certificate[0] info:
 # The hostname in the certificate matches 'expired.demo.gnutls.org'.
 # valid since: Wed Apr 22 00:00:58 CEST 2009
 # expires at: Thu Apr 23 00:00:58 CEST 2009
 # fingerprint: 97:B9:94:8C:4F:29:31:56:CD:85:9F:8D:D5:4E:D2:4E
 # Subject's DN: CN=expired.demo.gnutls.org
 # Issuer's DN: O=CA for expired.demo.gnutls.org
 # error: certificate has expired
jas at mocca:~$ 

The expected behaviour is that gnutls-cli should complain that the
certificate has expired for all URLs.  If you specify a --x509cafile
parameter, it should also refuse to connect to the server.

This problem can be identified with GNUTLS-SA-2009-3 and
CVE-2009-1417.

/Simon

[1] http://lists.gnu.org/archive/html/help-gnutls/2009-04/msg00021.html
[2] http://www.gnu.org/software/gnutls/lists.html
[3] http://www.gnu.org/software/gnutls/manual/html_node/Core-functions.html#gnutls_005fcertificate_005fverify_005fpeers2

[PATCH]

diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in
index 2ef3e74..1cf255f 100644
--- a/includes/gnutls/gnutls.h.in
+++ b/includes/gnutls/gnutls.h.in
@@ -251,7 +251,13 @@ extern "C"
      */
     GNUTLS_CERT_SIGNER_NOT_FOUND = 64,
     GNUTLS_CERT_SIGNER_NOT_CA = 128,
-    GNUTLS_CERT_INSECURE_ALGORITHM = 256
+    GNUTLS_CERT_INSECURE_ALGORITHM = 256,
+
+    /* Time verification.
+     */
+    GNUTLS_CERT_NOT_ACTIVATED = 512,
+    GNUTLS_CERT_EXPIRED = 1024
+
   } gnutls_certificate_status_t;
 
   typedef enum
diff --git a/includes/gnutls/x509.h b/includes/gnutls/x509.h
index 452247a..c750c87 100644
--- a/includes/gnutls/x509.h
+++ b/includes/gnutls/x509.h
@@ -481,7 +481,13 @@ extern "C"
 
     /* Allow certificates to be signed using the broken MD5 algorithm.
      */
-    GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 = 32
+    GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 = 32,
+
+    /* Disable checking of activation and expiration validity
+     * periods of certificate chains. Don't set this unless you
+     * understand the security implications.
+     */
+    GNUTLS_VERIFY_DISABLE_TIME_CHECKS = 64
   } gnutls_certificate_verify_flags;
 
   int gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert,
diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c
index 94038eb..6b126bc 100644
--- a/lib/gnutls_cert.c
+++ b/lib/gnutls_cert.c
@@ -656,6 +656,8 @@ gnutls_certificate_verify_peers (gnutls_session_t session)
   * This function will return the peer's certificate expiration time.
   *
   * Returns: (time_t)-1 on error.
+  *
+  * Deprecated: gnutls_certificate_verify_peers2() now verifies expiration times.
   **/
 time_t
 gnutls_certificate_expiration_time_peers (gnutls_session_t session)
@@ -701,6 +703,8 @@ gnutls_certificate_expiration_time_peers (gnutls_session_t session)
   * This is the creation time for openpgp keys.
   *
   * Returns: (time_t)-1 on error.
+  *
+  * Deprecated: gnutls_certificate_verify_peers2() now verifies activation times.
   **/
 time_t
 gnutls_certificate_activation_time_peers (gnutls_session_t session)
diff --git a/lib/x509/verify.c b/lib/x509/verify.c
index 4b1252e..538d96e 100644
--- a/lib/x509/verify.c
+++ b/lib/x509/verify.c
@@ -493,6 +493,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);
+
+      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 |= GNUTLS_CERT_EXPIRED;
+	      status |= GNUTLS_CERT_INVALID;
+	      return status;
+	    }
+	}
+    }
+
   /* Verify the certificate path (chain)
    */
   for (i = clist_size - 1; i > 0; i--)
@@ -810,9 +836,6 @@ _gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs,
   * @verify: will hold the certificate verification output.
   *
   * This function will try to verify the given certificate list and return its status.
-  * Note that expiration and activation dates are not checked
-  * by this function, you should check them using the appropriate functions.
-  *
   * If no flags are specified (0), this function will use the 
   * basicConstraints (2.5.29.19) PKIX extension. This means that only a certificate 
   * authority is allowed to sign a certificate.
diff --git a/src/common.c b/src/common.c
index cc50888..c60900b 100644
--- a/src/common.c
+++ b/src/common.c
@@ -427,6 +427,10 @@ print_cert_vrfy (gnutls_session_t session)
     {
       if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
 	printf ("- Peer's certificate issuer is unknown\n");
+      if (status & GNUTLS_CERT_NOT_ACTIVATED)
+	printf ("- Peer's certificate chain uses not yet valid certificate\n");
+      if (status & GNUTLS_CERT_EXPIRED)
+	printf ("- Peer's certificate chain uses expired certificate\n");
       if (status & GNUTLS_CERT_INVALID)
 	printf ("- Peer's certificate is NOT trusted\n");
       else

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 419 bytes
Desc: not available
URL: </pipermail/attachments/20090430/7dd8c332/attachment.pgp>


More information about the Gnutls-devel mailing list