# # 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 /* 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);