From ee973ae4b5fdaff2f2246c96d9d060db23a0ac7c Mon Sep 17 00:00:00 2001 From: Nicolas Thill Date: Mon, 24 Aug 2009 10:02:14 +0000 Subject: [PATCH] [8.09] [packages] gnutls: fix 2 vulnerabilities allowing Man-in-the-Middle attacks - 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 | 4 +- libs/gnutls/patches/901-cve-2008-4989.patch | 196 +++++++++++++++++ libs/gnutls/patches/902-cve-2009-2730.patch | 230 ++++++++++++++++++++ 3 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 libs/gnutls/patches/901-cve-2008-4989.patch create mode 100644 libs/gnutls/patches/902-cve-2009-2730.patch diff --git a/libs/gnutls/Makefile b/libs/gnutls/Makefile index 68a79b859b..0f747b647e 100644 --- a/libs/gnutls/Makefile +++ b/libs/gnutls/Makefile @@ -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 index 0000000000..e3ef49525c --- /dev/null +++ b/libs/gnutls/patches/901-cve-2008-4989.patch @@ -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 index 0000000000..cecc5de148 --- /dev/null +++ b/libs/gnutls/patches/902-cve-2009-2730.patch @@ -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; + } -- 2.30.2