summaryrefslogtreecommitdiffstats
path: root/net/cert
diff options
context:
space:
mode:
authorrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-17 02:09:08 +0000
committerrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-17 02:09:08 +0000
commitff35321f8eb52f5f6ae54a89a07a0c729854e548 (patch)
tree876fa0f34fdf64781e7296cb1c4693c0ce96b94f /net/cert
parente806cd78897dfcb7122bc5a69993f2c6d0c1f7e2 (diff)
downloadchromium_src-ff35321f8eb52f5f6ae54a89a07a0c729854e548.zip
chromium_src-ff35321f8eb52f5f6ae54a89a07a0c729854e548.tar.gz
chromium_src-ff35321f8eb52f5f6ae54a89a07a0c729854e548.tar.bz2
Warn if a well-known/"public" CA issues a certificate for a non-TLD
In preparation for new gTLDs being issued, begin phasing out the process of permitting publicly-trusted, well-known CAs to issue certificates for names that the CA cannot verify exclusive control over, such as "webmail" or "intranet.corp". Instead, require all publicly-trusted certificates be issued for domains that chain to an ICANN-recognized root zone (registry controlled domain). For certs that fail to meet this basic criteria, do not display the page as secure, as an attacker may be able to go to another CA (or even the same CA as the 'legitimate' site) and get a valid, publicly-trusted certificate for the same name. This does not cause an interstitial to be shown, but represents the first step to phasing out the practice. BUG=119212 TEST=[to be filled in] Review URL: https://chromiumcodereview.appspot.com/15203007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@200704 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/cert')
-rw-r--r--net/cert/cert_verify_proc.cc46
-rw-r--r--net/cert/cert_verify_proc.h20
-rw-r--r--net/cert/cert_verify_proc_unittest.cc138
-rw-r--r--net/cert/x509_certificate.cc2
4 files changed, 200 insertions, 6 deletions
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index a7f56b7..9022651 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -7,7 +7,10 @@
#include "base/metrics/histogram.h"
#include "base/sha1.h"
#include "build/build_config.h"
+#include "googleurl/src/url_canon.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
@@ -150,6 +153,14 @@ int CertVerifyProc::Verify(X509Certificate* cert,
rv = MapCertStatusToNetError(verify_result->cert_status);
}
+ // Flag certificates from publicly-trusted CAs that are issued to intranet
+ // hosts. While the CA/Browser Forum Baseline Requirements (v1.1) permit
+ // these to be issued until 1 November 2015, they represent a real risk for
+ // the deployment of gTLDs and are being phased out.
+ if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) {
+ verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME;
+ }
+
return rv;
}
@@ -286,4 +297,39 @@ bool CertVerifyProc::IsPublicKeyBlacklisted(
return false;
}
+// static
+bool CertVerifyProc::IsHostnameNonUnique(const std::string& hostname) {
+ // CanonicalizeHost requires surrounding brackets to parse an IPv6 address.
+ const std::string host_or_ip = hostname.find(':') != std::string::npos ?
+ "[" + hostname + "]" : hostname;
+ url_canon::CanonHostInfo host_info;
+ std::string canonical_name = CanonicalizeHost(host_or_ip, &host_info);
+
+ // If canonicalization fails, then the input is truly malformed. However,
+ // to avoid mis-reporting bad inputs as "non-unique", treat them as unique.
+ if (canonical_name.empty())
+ return false;
+
+ // If |hostname| is an IP address, presume it's unique.
+ // TODO(rsleevi): In the future, this should also reject IP addresses in
+ // IANA-reserved ranges, since those are also non-unique among publicly
+ // trusted CAs.
+ if (host_info.IsIPAddress())
+ return false;
+
+ // Check for a registry controlled portion of |hostname|, ignoring private
+ // registries, as they already chain to ICANN-administered registries,
+ // and explicitly ignoring unknown registries.
+ //
+ // Note: This means that as new gTLDs are introduced on the Internet, they
+ // will be treated as non-unique until the registry controlled domain list
+ // is updated. However, because gTLDs are expected to provide significant
+ // advance notice to deprecate older versions of this code, this an
+ // acceptable tradeoff.
+ return 0 == registry_controlled_domains::GetRegistryLength(
+ canonical_name,
+ registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
+ registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+}
+
} // namespace net
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index c85bf75..4feae19 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -67,13 +67,14 @@ class NET_EXPORT CertVerifyProc
virtual bool SupportsAdditionalTrustAnchors() const = 0;
protected:
- friend class base::RefCountedThreadSafe<CertVerifyProc>;
- FRIEND_TEST_ALL_PREFIXES(CertVerifyProcTest, DigiNotarCerts);
-
CertVerifyProc();
virtual ~CertVerifyProc();
private:
+ friend class base::RefCountedThreadSafe<CertVerifyProc>;
+ friend class CertVerifyProcNonUniqueNameTest;
+ FRIEND_TEST_ALL_PREFIXES(CertVerifyProcTest, DigiNotarCerts);
+
// Performs the actual verification using the desired underlying
// cryptographic library.
virtual int VerifyInternal(X509Certificate* cert,
@@ -89,6 +90,19 @@ class NET_EXPORT CertVerifyProc
// IsPublicKeyBlacklisted returns true iff one of |public_key_hashes| (which
// are hashes of SubjectPublicKeyInfo structures) is explicitly blocked.
static bool IsPublicKeyBlacklisted(const HashValueVector& public_key_hashes);
+
+ // Returns true if |hostname| contains a name that is non-unique among
+ // certificates (eg: an "internal server name").
+ //
+ // While such names are not scheduled to be deprecated until 1 November 2015
+ // according to the CA/Browser Forum Baseline Requirements (v1.1), they
+ // represent a real risk for the deployment of new gTLDs, and thus being
+ // phased out ahead of the hard deadline.
+ // TODO(rsleevi): http://crbug.com/119212 - Also match internal IP address
+ // ranges.
+ static bool IsHostnameNonUnique(const std::string& hostname);
+
+ DISALLOW_COPY_AND_ASSIGN(CertVerifyProc);
};
} // namespace net
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index c1722d5..d5bc7db 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -43,6 +43,45 @@ unsigned char paypal_null_fingerprint[] = {
0x1f, 0xe8, 0x1b, 0xd6, 0xab, 0x7b, 0xe8, 0xd7
};
+// Mock CertVerifyProc that will set |verify_result->is_issued_by_known_root|
+// for all certificates that are Verified.
+class WellKnownCaCertVerifyProc : public CertVerifyProc {
+ public:
+ // Initialize a CertVerifyProc that will set
+ // |verify_result->is_issued_by_known_root| to |is_well_known|.
+ explicit WellKnownCaCertVerifyProc(bool is_well_known)
+ : is_well_known_(is_well_known) {}
+
+ // CertVerifyProc implementation:
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE { return false; }
+
+ protected:
+ virtual ~WellKnownCaCertVerifyProc() {}
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+
+ const bool is_well_known_;
+
+ DISALLOW_COPY_AND_ASSIGN(WellKnownCaCertVerifyProc);
+};
+
+int WellKnownCaCertVerifyProc::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ verify_result->is_issued_by_known_root = is_well_known_;
+ return OK;
+}
+
} // namespace
class CertVerifyProcTest : public testing::Test {
@@ -68,8 +107,6 @@ class CertVerifyProcTest : public testing::Test {
}
const CertificateList empty_cert_list_;
-
- private:
scoped_refptr<CertVerifyProc> verify_proc_;
};
@@ -590,6 +627,35 @@ TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
certs[2]->os_cert_handle()));
}
+// Test that certificates issued for 'intranet' names (that is, containing no
+// known public registry controlled domain information) issued by well-known
+// CAs are flagged appropriately, while certificates that are issued by
+// internal CAs are not flagged.
+TEST_F(CertVerifyProcTest, IntranetHostsRejected) {
+ CertificateList cert_list = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ CertVerifyResult verify_result;
+ int error = 0;
+
+ // Intranet names for public CAs should be flagged:
+ verify_proc_ = new WellKnownCaCertVerifyProc(true);
+ error = Verify(cert, "intranet", 0, NULL, empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
+
+ // However, if the CA is not well known, these should not be flagged:
+ verify_proc_ = new WellKnownCaCertVerifyProc(false);
+ error = Verify(cert, "intranet", 0, NULL, empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
+}
+
// Test that the certificate returned in CertVerifyResult is able to reorder
// certificates that are not ordered from end-entity to root. While this is
// a protocol violation if sent during a TLS handshake, if multiple sources
@@ -1079,4 +1145,72 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
CertVerifyProcWeakDigestTest,
testing::ValuesIn(kVerifyMixedTestData));
+struct NonUniqueNameTestData {
+ bool is_unique;
+ const char* hostname;
+};
+
+// Google Test pretty-printer.
+void PrintTo(const NonUniqueNameTestData& data, std::ostream* os) {
+ ASSERT_TRUE(data.hostname);
+ *os << " hostname: " << testing::PrintToString(data.hostname)
+ << "; is_unique: " << testing::PrintToString(data.is_unique);
+}
+
+const NonUniqueNameTestData kNonUniqueNameTestData[] = {
+ // Domains under ICANN-assigned domains.
+ { true, "google.com" },
+ { true, "google.co.uk" },
+ // Domains under private registries.
+ { true, "appspot.com" },
+ { true, "test.appspot.com" },
+ // IPv4 addresses (in various forms).
+ { true, "8.8.8.8" },
+ { true, "1.2.3" },
+ { true, "14.15" },
+ { true, "676768" },
+ // IPv6 addresses.
+ { true, "FEDC:ba98:7654:3210:FEDC:BA98:7654:3210" },
+ { true, "::192.9.5.5" },
+ { true, "FEED::BEEF" },
+ // 'internal'/non-IANA assigned domains.
+ { false, "intranet" },
+ { false, "intranet." },
+ { false, "intranet.example" },
+ { false, "host.intranet.example" },
+ // gTLDs under discussion, but not yet assigned.
+ { false, "intranet.corp" },
+ { false, "example.tech" },
+ { false, "intranet.internal" },
+ // Invalid host names are treated as unique - but expected to be
+ // filtered out before then.
+ { true, "junk)(£)$*!@~#" },
+ { true, "w$w.example.com" },
+ { true, "nocolonsallowed:example" },
+ { true, "[::4.5.6.9]" },
+};
+
+class CertVerifyProcNonUniqueNameTest
+ : public testing::TestWithParam<NonUniqueNameTestData> {
+ public:
+ virtual ~CertVerifyProcNonUniqueNameTest() {}
+
+ protected:
+ bool IsUnique(const std::string& hostname) {
+ return !CertVerifyProc::IsHostnameNonUnique(hostname);
+ }
+};
+
+// Test that internal/non-unique names are properly identified as such, but
+// that IP addresses and hosts beneath registry-controlled domains are flagged
+// as unique names.
+TEST_P(CertVerifyProcNonUniqueNameTest, IsHostnameNonUnique) {
+ const NonUniqueNameTestData& test_data = GetParam();
+
+ EXPECT_EQ(test_data.is_unique, IsUnique(test_data.hostname));
+}
+
+INSTANTIATE_TEST_CASE_P(, CertVerifyProcNonUniqueNameTest,
+ testing::ValuesIn(kNonUniqueNameTestData));
+
} // namespace net
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index 382be8e..1b431ce 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -22,7 +22,7 @@
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
-#include "googleurl/src/url_canon_ip.h"
+#include "googleurl/src/url_canon.h"
#include "net/base/net_util.h"
#include "net/cert/pem_tokenizer.h"