[8.09] [packages] gnutls: fix 2 vulnerabilities allowing Man-in-the-Middle attacks
authorNicolas Thill <nico@openwrt.org>
Mon, 24 Aug 2009 10:02:14 +0000 (10:02 +0000)
committerNicolas Thill <nico@openwrt.org>
Mon, 24 Aug 2009 10:02:14 +0000 (10:02 +0000)
 - CVE-2008-4989: fix x509 certificate chain verification
 - CVE-2009-2730: fix handling of '\0' character in domain names of x509 certificates

SVN-Revision: 17360

libs/gnutls/Makefile
libs/gnutls/patches/901-cve-2008-4989.patch [new file with mode: 0644]
libs/gnutls/patches/902-cve-2009-2730.patch [new file with mode: 0644]

index 68a79b8..0f747b6 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2005-2008 OpenWrt.org
+# Copyright (C) 2005-2009 OpenWrt.org
 #
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=gnutls
 PKG_VERSION:=2.4.1
-PKG_RELEASE:=2
+PKG_RELEASE:=2.1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=http://www.mirrors.wiretapped.net/security/network-security/gnutls/ \
diff --git a/libs/gnutls/patches/901-cve-2008-4989.patch b/libs/gnutls/patches/901-cve-2008-4989.patch
new file mode 100644 (file)
index 0000000..e3ef495
--- /dev/null
@@ -0,0 +1,196 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=2008-4989
+
+--- a/lib/x509/verify.c
++++ b/lib/x509/verify.c
+@@ -42,17 +42,47 @@ static int _gnutls_verify_certificate2 (
+                                       const gnutls_x509_crt_t * trusted_cas,
+                                       int tcas_size, unsigned int flags,
+                                       unsigned int *output);
+-int _gnutls_x509_verify_signature (const gnutls_datum_t * signed_data,
+-                                 const gnutls_datum_t * signature,
+-                                 gnutls_x509_crt_t issuer);
+-static
+-  int is_crl_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert);
++static int is_crl_issuer (gnutls_x509_crl_t crl,
++                        gnutls_x509_crt_t issuer_cert);
++
+ static int _gnutls_verify_crl2 (gnutls_x509_crl_t crl,
+                               const gnutls_x509_crt_t * trusted_cas,
+                               int tcas_size, unsigned int flags,
+                               unsigned int *output);
++/* Checks if two certs are identical.  Return 0 onn match. */
++static int
++check_if_same_cert (gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2)
++{
++  gnutls_datum_t cert1bin = { NULL, 0 }, cert2bin = { NULL, 0 };
++  int result;
++
++  result = _gnutls_x509_der_encode (cert1->cert, "", &cert1bin, 0);
++  if (result < 0)
++    {
++      gnutls_assert ();
++      goto cleanup;
++    }
++
++  result = _gnutls_x509_der_encode (cert2->cert, "", &cert2bin, 0);
++  if (result < 0)
++    {
++      gnutls_assert ();
++      goto cleanup;
++    }
++
++  if ((cert1bin.size == cert2bin.size) &&
++      (memcmp (cert1bin.data, cert2bin.data, cert1bin.size) == 0))
++    result = 0;
++  else
++    result = 1;
++
++ cleanup:
++  _gnutls_free_datum (&cert1bin);
++  _gnutls_free_datum (&cert2bin);
++  return result;
++}
+ /* Checks if the issuer of a certificate is a
+  * Certificate Authority, or if the certificate is the same
+@@ -127,8 +157,20 @@ check_if_ca (gnutls_x509_crt_t cert, gnu
+         }
+       }
+-  if (gnutls_x509_crt_get_ca_status (issuer, NULL) == 1)
++  result = gnutls_x509_crt_get_ca_status (issuer, NULL);
++  if (result == 1)
++    {
++      result = 1;
++      goto cleanup;
++    }
++  /* Handle V1 CAs that do not have a basicConstraint, but accept
++     these certs only if the appropriate flags are set. */
++  else if ((result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) &&
++         ((flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT) ||
++          ((flags & GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT) &&
++           (gnutls_x509_crt_check_issuer (issuer, issuer) == 1))))
+     {
++      gnutls_assert ();
+       result = 1;
+       goto cleanup;
+     }
+@@ -322,6 +364,7 @@ _gnutls_verify_certificate2 (gnutls_x509
+       {
+         if (output)
+           *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID;
++        ret = 0;
+       }
+     }
+@@ -354,16 +397,12 @@ gnutls_x509_crt_check_issuer (gnutls_x50
+ }
+-/* The algorithm used is:
+- * 1. Check last certificate in the chain. If it is not verified return.
+- * 2. Check if any certificates in the chain are revoked. If yes return.
+- * 3. Try to verify the rest of certificates in the chain. If not verified return.
+- * 4. Return 0.
++/* Verify X.509 certificate chain.
+  *
+  * Note that the return value is an OR of GNUTLS_CERT_* elements.
+  *
+- * This function verifies a X.509 certificate list. The certificate list should
+- * lead to a trusted CA in order to be trusted.
++ * This function verifies a X.509 certificate list. The certificate
++ * list should lead to a trusted certificate in order to be trusted.
+  */
+ static unsigned int
+ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list,
+@@ -376,16 +415,56 @@ _gnutls_x509_verify_certificate (const g
+   int i = 0, ret;
+   unsigned int status = 0, output;
++  if (clist_size > 1)
++    {
++      /* Check if the last certificate in the path is self signed.
++       * In that case ignore it (a certificate is trusted only if it
++       * leads to a trusted party by us, not the server's).
++       *
++       * This prevents from verifying self signed certificates against
++       * themselves. This (although not bad) caused verification
++       * failures on some root self signed certificates that use the
++       * MD2 algorithm.
++       */
++      if (gnutls_x509_crt_check_issuer (certificate_list[clist_size - 1],
++                                  certificate_list[clist_size - 1]) > 0)
++      {
++        clist_size--;
++      }
++    }
++
++  /* We want to shorten the chain by removing the cert that matches
++   * one of the certs we trust and all the certs after that i.e. if
++   * cert chain is A signed-by B signed-by C signed-by D (signed-by
++   * self-signed E but already removed above), and we trust B, remove
++   * B, C and D.  We must leave the first cert on chain. */
++  if (clist_size > 1 && !(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
++    {
++      for (i = 1; i < clist_size; i++)
++      {
++        int j;
++
++        for (j = 0; j < tcas_size; j++)
++          {
++            if (check_if_same_cert (certificate_list[i],
++                                    trusted_cas[j]) == 0)
++              {
++                clist_size = i;
++                break;
++              }
++          }
++        /* clist_size may have been changed which gets out of loop */
++      }
++    }
++
+   /* Verify the last certificate in the certificate path
+    * against the trusted CA certificate list.
+    *
+    * If no CAs are present returns CERT_INVALID. Thus works
+    * in self signed etc certificates.
+    */
+-  ret =
+-    _gnutls_verify_certificate2 (certificate_list[clist_size - 1],
+-                               trusted_cas, tcas_size, flags, &output);
+-
++  ret = _gnutls_verify_certificate2 (certificate_list[clist_size - 1],
++                                   trusted_cas, tcas_size, flags, &output);
+   if (ret == 0)
+     {
+       /* if the last certificate in the certificate
+@@ -414,18 +493,7 @@ _gnutls_x509_verify_certificate (const g
+     }
+ #endif
+-  /* Check if the last certificate in the path is self signed.
+-   * In that case ignore it (a certificate is trusted only if it
+-   * leads to a trusted party by us, not the server's).
+-   */
+-  if (gnutls_x509_crt_check_issuer (certificate_list[clist_size - 1],
+-                                  certificate_list[clist_size - 1]) > 0
+-      && clist_size > 0)
+-    {
+-      clist_size--;
+-    }
+-
+-  /* Verify the certificate path (chain) 
++  /* Verify the certificate path (chain)
+    */
+   for (i = clist_size - 1; i > 0; i--)
+     {
+@@ -1031,6 +1099,7 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t c
+       {
+       if (output)
+         *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID;
++      ret = 0;
+       }
+   }
diff --git a/libs/gnutls/patches/902-cve-2009-2730.patch b/libs/gnutls/patches/902-cve-2009-2730.patch
new file mode 100644 (file)
index 0000000..cecc5de
--- /dev/null
@@ -0,0 +1,230 @@
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-2730
+
+--- a/lib/gnutls_str.c
++++ b/lib/gnutls_str.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (C) 2002, 2004, 2005, 2007, 2008  Free Software Foundation
++ * Copyright (C) 2002, 2004, 2005, 2007, 2008, 2009  Free Software Foundation
+  *
+  * Author: Nikos Mavrogiannopoulos
+  *
+@@ -331,16 +331,21 @@ _gnutls_hex2bin (const opaque * hex_data
+ /* compare hostname against certificate, taking account of wildcards
+  * return 1 on success or 0 on error
++ *
++ * note: certnamesize is required as X509 certs can contain embedded NULs in
++ * the strings such as CN or subjectAltName
+  */
+ int
+-_gnutls_hostname_compare (const char *certname, const char *hostname)
++_gnutls_hostname_compare (const char *certname,
++                        size_t certnamesize,
++                        const char *hostname)
+ {
+    /* find the first different character */
+-  for (; *certname && *hostname && toupper(*certname) == toupper(*hostname); certname++, hostname++)
++  for (; *certname && *hostname && toupper(*certname) == toupper(*hostname); certname++, hostname++, certnamesize--)
+     ;
+  
+    /* the strings are the same */
+-  if (strlen (certname) == 0 && strlen (hostname) == 0)
++  if (certnamesize == 0 && *hostname == '\0')
+     return 1;
+   
+   if (*certname == '*')
+@@ -348,15 +353,16 @@ _gnutls_hostname_compare (const char *ce
+       /* a wildcard certificate */
+       certname++;
+-  
++      certnamesize--;
++
+       while (1)
+               {
+         /* Use a recursive call to allow multiple wildcards */
+-        if (_gnutls_hostname_compare (certname, hostname))
+-          {
+-            return 1;
+-          }
+-        /* wildcards are only allowed to match a single domain component or component fragment */
++        if (_gnutls_hostname_compare (certname, certnamesize, hostname))
++          return 1;
++
++        /* wildcards are only allowed to match a single domain
++           component or component fragment */
+         if (*hostname == '\0' || *hostname == '.')
+           break;
+         hostname++;
+--- a/lib/gnutls_str.h
++++ b/lib/gnutls_str.h
+@@ -62,7 +62,7 @@ char *_gnutls_bin2hex (const void *old, 
+ int _gnutls_hex2bin (const opaque * hex_data, int hex_size, opaque * bin_data,
+                    size_t * bin_size);
+-int _gnutls_hostname_compare (const char *certname, const char *hostname);
++int _gnutls_hostname_compare (const char *certname, size_t certnamesize, const char *hostname);
+ #define MAX_CN 256
+ #endif
+--- a/lib/openpgp/pgp.c
++++ b/lib/openpgp/pgp.c
+@@ -566,7 +566,7 @@ gnutls_openpgp_crt_check_hostname (gnutl
+       
+       if (ret == 0)
+         {
+-          if (_gnutls_hostname_compare (dnsname, hostname))
++          if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname))
+             return 1;
+         }
+     }
+--- a/lib/x509/common.c
++++ b/lib/x509/common.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation
++ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation
+  *
+  * Author: Nikos Mavrogiannopoulos
+  *
+@@ -241,6 +241,10 @@ _gnutls_x509_oid_data2string (const char
+     {
+       str[len] = 0;
++      /* Refuse to deal with strings containing NULs. */
++      if (strlen (str) != len)
++      return GNUTLS_E_ASN1_DER_ERROR;
++
+       if (res)
+       _gnutls_str_cpy (res, *res_size, str);
+       *res_size = len;
+@@ -291,25 +295,27 @@ _gnutls_x509_oid_data2string (const char
+           non_printable = 0;
+       }
+-      if (res)
++      if (non_printable == 0)
+       {
+-        if (non_printable == 0)
+-          {
+-            str[len] = 0;
+-            _gnutls_str_cpy (res, *res_size, str);
+-            *res_size = len;
+-          }
+-        else
++        str[len] = 0;
++
++        /* Refuse to deal with strings containing NULs. */
++        if (strlen (str) != len)
++          return GNUTLS_E_ASN1_DER_ERROR;
++
++        if (res)
++          _gnutls_str_cpy (res, *res_size, str);
++        *res_size = len;
++      }
++      else
++      {
++        result = _gnutls_x509_data2hex (str, len, res, res_size);
++        if (result < 0)
+           {
+-            result = _gnutls_x509_data2hex (str, len, res, res_size);
+-            if (result < 0)
+-              {
+-                gnutls_assert ();
+-                return result;
+-              }
++            gnutls_assert ();
++            return result;
+           }
+       }
+-
+     }
+   return 0;
+--- a/lib/x509/output.c
++++ b/lib/x509/output.c
+@@ -272,6 +272,17 @@ print_crldist (gnutls_string * str, gnut
+         return;
+       }
++      if ((err == GNUTLS_SAN_DNSNAME
++         || err == GNUTLS_SAN_RFC822NAME
++         || err == GNUTLS_SAN_URI) &&
++        strlen (buffer) != size)
++      {
++        adds (str, _("warning: distributionPoint contains an embedded NUL, "
++                     "replacing with '!'\n"));
++        while (strlen (buffer) < size)
++          buffer[strlen (buffer)] = '!';
++      }
++
+       switch (err)
+       {
+       case GNUTLS_SAN_DNSNAME:
+@@ -423,6 +434,17 @@ print_san (gnutls_string * str, gnutls_x
+         return;
+       }
++      if ((err == GNUTLS_SAN_DNSNAME
++         || err == GNUTLS_SAN_RFC822NAME
++         || err == GNUTLS_SAN_URI) &&
++        strlen (buffer) != size)
++      {
++        adds (str, _("warning: SAN contains an embedded NUL, "
++                     "replacing with '!'\n"));
++        while (strlen (buffer) < size)
++          buffer[strlen (buffer)] = '!';
++      }
++
+       switch (err)
+       {
+       case GNUTLS_SAN_DNSNAME:
+@@ -481,7 +503,17 @@ print_san (gnutls_string * str, gnutls_x
+             }
+           if (err == GNUTLS_SAN_OTHERNAME_XMPP)
+-            addf (str, _("\t\t\tXMPP Address: %.*s\n"), size, buffer);
++            {
++              if (strlen (buffer) != size)
++                {
++                  adds (str, _("warning: SAN contains an embedded NUL, "
++                               "replacing with '!'\n"));
++                  while (strlen (buffer) < size)
++                    buffer[strlen (buffer)] = '!';
++                }
++
++              addf (str, _("\t\t\tXMPP Address: %.*s\n"), size, buffer);
++            }
+           else
+             {
+               addf (str, _("\t\t\totherName OID: %.*s\n"), oidsize, oid);
+--- a/lib/x509/rfc2818_hostname.c
++++ b/lib/x509/rfc2818_hostname.c
+@@ -74,7 +74,7 @@ gnutls_x509_crt_check_hostname (gnutls_x
+       if (ret == GNUTLS_SAN_DNSNAME)
+       {
+         found_dnsname = 1;
+-        if (_gnutls_hostname_compare (dnsname, hostname))
++        if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname))
+           {
+             return 1;
+           }
+@@ -84,7 +84,7 @@ gnutls_x509_crt_check_hostname (gnutls_x
+         found_dnsname = 1; /* RFC 2818 is unclear whether the CN
+                               should be compared for IP addresses
+                               too, but we won't do it.  */
+-        if (_gnutls_hostname_compare (dnsname, hostname))
++        if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname))
+           {
+             return 1;
+           }
+@@ -104,7 +104,7 @@ gnutls_x509_crt_check_hostname (gnutls_x
+         return 0;
+       }
+-      if (_gnutls_hostname_compare (dnsname, hostname))
++      if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname))
+       {
+         return 1;
+       }