#
# Upstream: http://lists.gnu.org/archive/html/help-gnutls/2009-08/msg00011.html
# Patch: adapted from upstream commits:
#        http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=177e7ddb761999cd8b439e14a2bf43590756e230
#        http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=a431be86124f900c4082e82d32917f86fcce461a
#        http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=74b6d92f9675ce4e03642c4d6ced4a3a614b07f6
#        http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=40081594e3de518b998f3e5177ed5a9f7707f2e8
#        http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=5a58e9d33448235377afd5fbfcee1683dc70eae3
#        http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=1ea190d216767dd4ab93b87361cbcb9d4fb3aafc
# Description: fix improper handling of '\0' in Common Name (CN) and Subject
#              Alternative Name (SAN) in X.509 certificates. Also backported
#              177e7ddb761999cd8b439e14a2bf43590756e230 for added wide wildcard
#              hostname matching so _gnutls_hostname_compare() is up to date
#              with upstream GnuTLS.
#
diff -Nur -x '*.orig' -x '*~' gnutls12-1.2.9/lib/x509/common.c gnutls12-1.2.9.new/lib/x509/common.c
--- gnutls12-1.2.9/lib/x509/common.c	2005-05-26 10:21:36.000000000 -0500
+++ gnutls12-1.2.9.new/lib/x509/common.c	2009-08-14 15:45:54.126511370 -0500
@@ -220,6 +220,10 @@
     if (CHOICE == 0) {
 	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;
@@ -265,20 +269,23 @@
 		non_printable = 0;
 	}
 
-	if (res) {
-	    if (non_printable == 0) {
-		str[len] = 0;
-		_gnutls_str_cpy(res, *res_size, str);
-		*res_size = len;
-	    } else {
-		result = _gnutls_x509_data2hex(str, len, res, res_size);
-		if (result < 0) {
-		    gnutls_assert();
-		    return result;
-		}
+	if (non_printable == 0) {
+	    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) {
+		gnutls_assert();
+		return result;
 	    }
 	}
-
     }
 
     return 0;
diff -Nur -x '*.orig' -x '*~' gnutls12-1.2.9/lib/x509/rfc2818.h gnutls12-1.2.9.new/lib/x509/rfc2818.h
--- gnutls12-1.2.9/lib/x509/rfc2818.h	2005-05-26 10:21:36.000000000 -0500
+++ gnutls12-1.2.9.new/lib/x509/rfc2818.h	2009-08-14 15:45:54.158505664 -0500
@@ -22,5 +22,5 @@
  *
  */
 
-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
diff -Nur -x '*.orig' -x '*~' gnutls12-1.2.9/lib/x509/rfc2818_hostname.c gnutls12-1.2.9.new/lib/x509/rfc2818_hostname.c
--- gnutls12-1.2.9/lib/x509/rfc2818_hostname.c	2005-05-26 10:21:36.000000000 -0500
+++ gnutls12-1.2.9.new/lib/x509/rfc2818_hostname.c	2009-08-14 15:46:20.416107102 -0500
@@ -30,39 +30,43 @@
 #include <gnutls_errors.h>
 
 /* compare hostname against certificate, taking account of wildcards
- * return 1 on success or 0 on error 
+ * 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)
 {
-    const char *cmpstr1, *cmpstr2;
+    /* find the first different character */
+    for (; *certname && *hostname && toupper(*certname) == toupper(*hostname); certname++, hostname++, certnamesize--)
+      ;
+
+    /* the strings are the same */
+    if (certnamesize == 0 && *hostname == '\0')
+      return 1;
 
-    if (strlen(certname) == 0 || strlen(hostname) == 0)
-	return 0;
-
-    if (strlen(certname) > 2 && strncmp(certname, "*.", 2) == 0) {
+    if (*certname == '*') {
 	/* a wildcard certificate */
 
-	cmpstr1 = certname + 1;
-
-	/* find the first dot in hostname, compare from there on */
-	cmpstr2 = strchr(hostname, '.');
-
-	if (cmpstr2 == NULL) {
-	    /* error, the hostname we're connecting to is only a local part */
-	    return 0;
-	}
+	certname++;
+	certnamesize--;
 
-	if (strcasecmp(cmpstr1, cmpstr2) == 0) {
+	while (1) {
+	  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++;
 	}
 
 	return 0;
     }
 
-    if (strcasecmp(certname, hostname) == 0) {
-	return 1;
-    }
-
     return 0;
 }
 
@@ -113,7 +117,7 @@
 
 	if (ret == GNUTLS_SAN_DNSNAME) {
 	    found_dnsname = 1;
-	    if (_gnutls_hostname_compare(dnsname, hostname)) {
+	    if (_gnutls_hostname_compare(dnsname, dnsnamesize, hostname)) {
 		return 1;
 	    }
 	}
@@ -131,7 +135,7 @@
 	    return 1;
 	}
 
-	if (_gnutls_hostname_compare(dnsname, hostname)) {
+	if (_gnutls_hostname_compare(dnsname, dnsnamesize, hostname)) {
 	    return 1;
 	}
     }
diff -Nur -x '*.orig' -x '*~' gnutls12-1.2.9/libextra/openpgp/pgp.c gnutls12-1.2.9.new/libextra/openpgp/pgp.c
--- gnutls12-1.2.9/libextra/openpgp/pgp.c	2005-05-26 10:18:38.000000000 -0500
+++ gnutls12-1.2.9.new/libextra/openpgp/pgp.c	2009-08-14 15:45:54.158505664 -0500
@@ -498,7 +498,7 @@
 	dnsnamesize = sizeof(dnsname);
 	ret = gnutls_openpgp_key_get_name(key, i, dnsname, &dnsnamesize);
 
-	if (_gnutls_hostname_compare(dnsname, hostname)) {
+	if (_gnutls_hostname_compare(dnsname, dnsnamesize, hostname)) {
 	    return 1;
 	}
     }
diff -Nur -x '*.orig' -x '*~' gnutls12-1.2.9/src/certtool.c gnutls12-1.2.9.new/src/certtool.c
--- gnutls12-1.2.9/src/certtool.c	2005-11-07 12:52:12.000000000 -0600
+++ gnutls12-1.2.9.new/src/certtool.c	2009-08-14 15:45:54.162506490 -0500
@@ -1155,6 +1155,15 @@
 	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
 	    fprintf(out, "\t\tFound unsupported alternative name.\n");
 	} else
+	    if ((ret == GNUTLS_SAN_DNSNAME
+		 || ret == GNUTLS_SAN_RFC822NAME
+		 || ret == GNUTLS_SAN_URI) &&
+		strlen (buffer) != size) {
+		    fprintf(out, "\nwarning: SAN contains an embedded NUL, "
+				   "replacing with '!'\n");
+		while (strlen (buffer) < size)
+		    buffer[strlen (buffer)] = '!';
+	    }
 	    switch (ret) {
 	    case GNUTLS_SAN_DNSNAME:
 		fprintf(out, "\t\tDNSname: %s\n", buffer);
@@ -1190,6 +1199,15 @@
 	if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
 	    fprintf(out, "\t\tError decoding: %s\n", gnutls_strerror(ret));
 	} else
+	    if ((ret == GNUTLS_SAN_DNSNAME
+		 || ret == GNUTLS_SAN_RFC822NAME
+		 || ret == GNUTLS_SAN_URI) &&
+		strlen (buffer) != size) {
+		    fprintf(out, "\nwarning: distributionPoint contains an embedded NUL, "
+				   "replacing with '!'\n");
+		while (strlen (buffer) < size)
+		    buffer[strlen (buffer)] = '!';
+	    }
 	    switch (ret) {
 	    case GNUTLS_SAN_DNSNAME:
 		fprintf(out, "\t\tDNSname: %s\n", buffer);
