diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-08 21:22:45 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-08 21:24:05 +0000 |
commit | 8d60aa54abe0517d756c9d625ece75feabed613a (patch) | |
tree | 2f37ad4a17f64323835d78ce33cb756b82c7326b | |
parent | eb7b4aac673321aee0731cf5fecfeda34ac07317 (diff) | |
download | chromium_src-8d60aa54abe0517d756c9d625ece75feabed613a.zip chromium_src-8d60aa54abe0517d756c9d625ece75feabed613a.tar.gz chromium_src-8d60aa54abe0517d756c9d625ece75feabed613a.tar.bz2 |
Centralize the logic for checking public key pins from ClientSocketNSS
and ProofVerifierChromium to TransportSecurityState::CheckPublicKeyPins.
This required adding an is_issued_by_known_root argument to this method.
In addition, CheckPublicKeyPins now only checks static pins if the
TransportSecurityState's enable_static_pins_ member is true. This defaults
to true only for official desktop builds. This also means that dynamic
pins are now checked on mobile and on non-official builds.
BUG=398925,391033
Review URL: https://codereview.chromium.org/433123003
Cr-Commit-Position: refs/heads/master@{#288435}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288435 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/http/http_security_headers_unittest.cc | 32 | ||||
-rw-r--r-- | net/http/transport_security_state.cc | 124 | ||||
-rw-r--r-- | net/http/transport_security_state.h | 23 | ||||
-rw-r--r-- | net/http/transport_security_state_unittest.cc | 123 | ||||
-rw-r--r-- | net/quic/crypto/proof_verifier_chromium.cc | 54 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_nss.cc | 51 |
6 files changed, 233 insertions, 174 deletions
diff --git a/net/http/http_security_headers_unittest.cc b/net/http/http_security_headers_unittest.cc index ce919ff..240e76d 100644 --- a/net/http/http_security_headers_unittest.cc +++ b/net/http/http_security_headers_unittest.cc @@ -506,6 +506,7 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { // docs.google.com has preloaded pins. const bool sni_enabled = true; std::string domain = "docs.google.com"; + state.enable_static_pins_ = true; EXPECT_TRUE( state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); @@ -554,8 +555,9 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { HashValueVector hashes; hashes.push_back(good_hash); std::string failure_log; - EXPECT_TRUE( - state.CheckPublicKeyPins(domain, sni_enabled, hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_TRUE(state.CheckPublicKeyPins( + domain, sni_enabled, is_issued_by_known_root, hashes, &failure_log)); TransportSecurityState::DomainState new_dynamic_domain_state; EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); @@ -585,6 +587,7 @@ TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { // docs.google.com has preloaded pins. const bool sni_enabled = true; std::string domain = "docs.google.com"; + state.enable_static_pins_ = true; ASSERT_TRUE( state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); @@ -648,8 +651,13 @@ TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { // Damage the hashes to cause a pin validation failure. new_static_domain_state2.pkp.spki_hashes[0].data()[0] ^= 0x80; new_static_domain_state2.pkp.spki_hashes[1].data()[0] ^= 0x80; - EXPECT_FALSE(state.CheckPublicKeyPins( - domain, true, new_static_domain_state2.pkp.spki_hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_FALSE( + state.CheckPublicKeyPins(domain, + true, + is_issued_by_known_root, + new_static_domain_state2.pkp.spki_hashes, + &failure_log)); EXPECT_NE(0UL, failure_log.length()); } #undef MAYBE_UpdateDynamicPKPMaxAge0 @@ -663,6 +671,7 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { // accounts.google.com has preloaded pins. std::string domain = "accounts.google.com"; + state.enable_static_pins_ = true; // Retrieve the DomainState as it is by default, including its known good // pins. @@ -680,8 +689,12 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { EXPECT_TRUE(state.AddHSTSHeader(domain, "includesubdomains; max-age=10000")); EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); std::string failure_log; - EXPECT_TRUE(state.CheckPublicKeyPins( - domain, sni_enabled, saved_hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_TRUE(state.CheckPublicKeyPins(domain, + sni_enabled, + is_issued_by_known_root, + saved_hashes, + &failure_log)); // Add an HPKP header, which should only update the dynamic state. HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); @@ -701,8 +714,11 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); // The dynamic pins, which do not match |saved_hashes|, should take // precedence over the static pins and cause the check to fail. - EXPECT_FALSE(state.CheckPublicKeyPins( - domain, sni_enabled, saved_hashes, &failure_log)); + EXPECT_FALSE(state.CheckPublicKeyPins(domain, + sni_enabled, + is_issued_by_known_root, + saved_hashes, + &failure_log)); } }; // namespace net diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc index 0b209b3..508784e 100644 --- a/net/http/transport_security_state.cc +++ b/net/http/transport_security_state.cc @@ -84,7 +84,12 @@ bool AddHash(const char* sha1_hash, } // namespace TransportSecurityState::TransportSecurityState() - : delegate_(NULL) { + : delegate_(NULL), enable_static_pins_(true) { +// Static pinning is only enabled for official builds to make sure that +// others don't end up with pins that cannot be easily updated. +#if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS) + enable_static_pins_ = false; +#endif DCHECK(CalledOnValidThread()); } @@ -118,21 +123,36 @@ bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host, return false; } -bool TransportSecurityState::CheckPublicKeyPins(const std::string& host, - bool sni_enabled, - const HashValueVector& hashes, - std::string* failure_log) { - DomainState dynamic_state; - if (GetDynamicDomainState(host, &dynamic_state)) - return dynamic_state.CheckPublicKeyPins(hashes, failure_log); +bool TransportSecurityState::CheckPublicKeyPins( + const std::string& host, + bool sni_available, + bool is_issued_by_known_root, + const HashValueVector& public_key_hashes, + std::string* pinning_failure_log) { + // Perform pin validation if, and only if, all these conditions obtain: + // + // * the server's certificate chain chains up to a known root (i.e. not a + // user-installed trust anchor); and + // * the build is recent (very old builds should fail open so that users + // have some chance to recover). + // * the server actually has public key pins. + // + // TODO(rsleevi): http://crbug.com/391032 - Only disable static HPKP if the + // build is not timely. + if (!is_issued_by_known_root || !IsBuildTimely() || + !HasPublicKeyPins(host, sni_available)) { + return true; + } - DomainState static_state; - if (GetStaticDomainState(host, sni_enabled, &static_state) && - static_state.CheckPublicKeyPins(hashes, failure_log)) { - return true; + bool pins_are_valid = CheckPublicKeyPinsImpl( + host, sni_available, public_key_hashes, pinning_failure_log); + if (!pins_are_valid) { + LOG(ERROR) << *pinning_failure_log; + ReportUMAOnPinFailure(host); } - return false; + UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid); + return pins_are_valid; } bool TransportSecurityState::HasPublicKeyPins(const std::string& host, @@ -549,9 +569,13 @@ struct HSTSPreload { SecondLevelDomainName second_level_domain_name; }; -static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, - const std::string& canonicalized_host, size_t i, - TransportSecurityState::DomainState* out, bool* ret) { +static bool HasPreload(const struct HSTSPreload* entries, + size_t num_entries, + const std::string& canonicalized_host, + size_t i, + bool enable_static_pins, + TransportSecurityState::DomainState* out, + bool* ret) { for (size_t j = 0; j < num_entries; j++) { if (entries[j].length == canonicalized_host.size() - i && memcmp(entries[j].dns_name, &canonicalized_host[i], @@ -561,26 +585,29 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, } else { out->sts.include_subdomains = entries[j].include_subdomains; out->sts.last_observed = base::GetBuildTime(); - out->pkp.include_subdomains = entries[j].include_subdomains; - out->pkp.last_observed = base::GetBuildTime(); *ret = true; out->sts.upgrade_mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS; if (!entries[j].https_required) out->sts.upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; - if (entries[j].pins.required_hashes) { - const char* const* sha1_hash = entries[j].pins.required_hashes; - while (*sha1_hash) { - AddHash(*sha1_hash, &out->pkp.spki_hashes); - sha1_hash++; + + if (enable_static_pins) { + out->pkp.include_subdomains = entries[j].include_subdomains; + out->pkp.last_observed = base::GetBuildTime(); + if (entries[j].pins.required_hashes) { + const char* const* sha1_hash = entries[j].pins.required_hashes; + while (*sha1_hash) { + AddHash(*sha1_hash, &out->pkp.spki_hashes); + sha1_hash++; + } } - } - if (entries[j].pins.excluded_hashes) { - const char* const* sha1_hash = entries[j].pins.excluded_hashes; - while (*sha1_hash) { - AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); - sha1_hash++; + if (entries[j].pins.excluded_hashes) { + const char* const* sha1_hash = entries[j].pins.excluded_hashes; + while (*sha1_hash) { + AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); + sha1_hash++; + } } } } @@ -763,6 +790,24 @@ bool TransportSecurityState::IsBuildTimely() { return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; } +bool TransportSecurityState::CheckPublicKeyPinsImpl( + const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log) { + DomainState dynamic_state; + if (GetDynamicDomainState(host, &dynamic_state)) + return dynamic_state.CheckPublicKeyPins(hashes, failure_log); + + DomainState static_state; + if (GetStaticDomainState(host, sni_enabled, &static_state)) + return static_state.CheckPublicKeyPins(hashes, failure_log); + + // HasPublicKeyPins should have returned true in order for this method + // to have been called, so if we fall through to here, it's an error. + return false; +} + bool TransportSecurityState::GetStaticDomainState(const std::string& host, bool sni_enabled, DomainState* out) const { @@ -781,15 +826,22 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host, canonicalized_host.size() - i); out->domain = DNSDomainToString(host_sub_chunk); bool ret; - if (is_build_timely && - HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, - &ret)) { + if (is_build_timely && HasPreload(kPreloadedSTS, + kNumPreloadedSTS, + canonicalized_host, + i, + enable_static_pins_, + out, + &ret)) { return ret; } - if (sni_enabled && - is_build_timely && - HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, - out, &ret)) { + if (sni_enabled && is_build_timely && HasPreload(kPreloadedSNISTS, + kNumPreloadedSNISTS, + canonicalized_host, + i, + enable_static_pins_, + out, + &ret)) { return ret; } } diff --git a/net/http/transport_security_state.h b/net/http/transport_security_state.h index 3645937..4d49da1 100644 --- a/net/http/transport_security_state.h +++ b/net/http/transport_security_state.h @@ -163,6 +163,7 @@ class NET_EXPORT TransportSecurityState bool ShouldUpgradeToSSL(const std::string& host, bool sni_enabled); bool CheckPublicKeyPins(const std::string& host, bool sni_enabled, + bool is_issued_by_known_root, const HashValueVector& hashes, std::string* failure_log); bool HasPublicKeyPins(const std::string& host, bool sni_enabled); @@ -267,6 +268,14 @@ class NET_EXPORT TransportSecurityState // The maximum number of seconds for which we'll cache an HSTS request. static const long int kMaxHSTSAgeSecs; + private: + friend class TransportSecurityStateTest; + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, UpdateDynamicPKPOnly); + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, UpdateDynamicPKPMaxAge0); + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, NoClobberPins); + + typedef std::map<std::string, DomainState> DomainStateMap; + // Send an UMA report on pin validation failure, if the host is in a // statically-defined list of domains. // @@ -282,12 +291,11 @@ class NET_EXPORT TransportSecurityState // information) is timely. static bool IsBuildTimely(); - private: - friend class TransportSecurityStateTest; - FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, - UpdateDynamicPKPOnly); - - typedef std::map<std::string, DomainState> DomainStateMap; + // Helper method for actually checking pins. + bool CheckPublicKeyPinsImpl(const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log); // If a Delegate is present, notify it that the internal state has // changed. @@ -309,6 +317,9 @@ class NET_EXPORT TransportSecurityState Delegate* delegate_; + // True if static pins should be used. + bool enable_static_pins_; + DISALLOW_COPY_AND_ASSIGN(TransportSecurityState); }; diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc index 476ae94..dfbc753 100644 --- a/net/http/transport_security_state_unittest.cc +++ b/net/http/transport_security_state_unittest.cc @@ -37,6 +37,7 @@ namespace net { class TransportSecurityStateTest : public testing::Test { + public: virtual void SetUp() { #if defined(USE_OPENSSL) crypto::EnsureOpenSSLInit(); @@ -45,6 +46,14 @@ class TransportSecurityStateTest : public testing::Test { #endif } + static void DisableStaticPins(TransportSecurityState* state) { + state->enable_static_pins_ = false; + } + + static void EnableStaticPins(TransportSecurityState* state) { + state->enable_static_pins_ = true; + } + protected: bool GetStaticDomainState(TransportSecurityState* state, const std::string& host, @@ -162,6 +171,27 @@ TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) { EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); } +TEST_F(TransportSecurityStateTest, EnableStaticPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + EnableStaticPins(&state); + + EXPECT_TRUE( + state.GetStaticDomainState("chrome.google.com", true, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); +} + +TEST_F(TransportSecurityStateTest, DisableStaticPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + DisableStaticPins(&state); + EXPECT_TRUE( + state.GetStaticDomainState("chrome.google.com", true, &domain_state)); + EXPECT_TRUE(domain_state.pkp.spki_hashes.empty()); +} + TEST_F(TransportSecurityStateTest, IsPreloaded) { const std::string paypal = "paypal.com"; const std::string www_paypal = "www.paypal.com"; @@ -177,7 +207,6 @@ TEST_F(TransportSecurityStateTest, IsPreloaded) { EXPECT_TRUE(GetStaticDomainState(&state, paypal, true, &domain_state)); EXPECT_TRUE(GetStaticDomainState(&state, www_paypal, true, &domain_state)); EXPECT_FALSE(domain_state.sts.include_subdomains); - EXPECT_FALSE(domain_state.pkp.include_subdomains); EXPECT_FALSE(GetStaticDomainState(&state, a_www_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, abc_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, example, true, &domain_state)); @@ -214,6 +243,7 @@ static bool HasStaticState(const char* hostname) { static bool HasStaticPublicKeyPins(const char* hostname, bool sni_enabled) { TransportSecurityState state; + TransportSecurityStateTest::EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; if (!state.GetStaticDomainState(hostname, sni_enabled, &domain_state)) return false; @@ -227,6 +257,7 @@ static bool HasStaticPublicKeyPins(const char* hostname) { static bool OnlyPinningInStaticState(const char* hostname) { TransportSecurityState state; + TransportSecurityStateTest::EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; if (!state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state)) return false; @@ -285,24 +316,6 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_FALSE(HasStaticState("m.gmail.com")); EXPECT_FALSE(HasStaticState("m.googlemail.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com")); - EXPECT_TRUE(OnlyPinningInStaticState("youtube.com")); - EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com")); - EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com")); - EXPECT_TRUE(OnlyPinningInStaticState("appspot.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com")); - EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net")); - EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com")); - // Tests for domains that don't work without SNI. EXPECT_FALSE(state.GetStaticDomainState("gmail.com", false, &domain_state)); EXPECT_FALSE( @@ -407,28 +420,12 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_TRUE(StaticShouldRedirect("www.dropcam.com")); EXPECT_FALSE(HasStaticState("foo.dropcam.com")); - EXPECT_TRUE( - state.GetStaticDomainState("torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("www.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("check.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("blog.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); EXPECT_TRUE(StaticShouldRedirect("ebanking.indovinabank.com.vn")); EXPECT_TRUE(StaticShouldRedirect("foo.ebanking.indovinabank.com.vn")); EXPECT_TRUE(StaticShouldRedirect("epoxate.com")); EXPECT_FALSE(HasStaticState("foo.epoxate.com")); - EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); EXPECT_FALSE(HasStaticState("foo.torproject.org")); EXPECT_TRUE(StaticShouldRedirect("www.moneybookers.com")); @@ -478,6 +475,57 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_TRUE(StaticShouldRedirect("crate.io")); EXPECT_TRUE(StaticShouldRedirect("foo.crate.io")); +} + +TEST_F(TransportSecurityStateTest, PreloadedPins) { + TransportSecurityState state; + EnableStaticPins(&state); + TransportSecurityState::DomainState domain_state; + + // We do more extensive checks for the first domain. + EXPECT_TRUE( + state.GetStaticDomainState("www.paypal.com", true, &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, + TransportSecurityState::DomainState::MODE_FORCE_HTTPS); + EXPECT_FALSE(domain_state.sts.include_subdomains); + EXPECT_FALSE(domain_state.pkp.include_subdomains); + + EXPECT_TRUE(OnlyPinningInStaticState("www.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com")); + EXPECT_TRUE(OnlyPinningInStaticState("appspot.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com")); + EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net")); + EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com")); + + EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); + EXPECT_FALSE(HasStaticState("foo.torproject.org")); + + EXPECT_TRUE( + state.GetStaticDomainState("torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("www.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("check.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("blog.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); } @@ -495,6 +543,7 @@ TEST_F(TransportSecurityStateTest, LongNames) { TEST_F(TransportSecurityStateTest, BuiltinCertPins) { TransportSecurityState state; + EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; EXPECT_TRUE( @@ -534,7 +583,6 @@ TEST_F(TransportSecurityStateTest, BuiltinCertPins) { EXPECT_TRUE(HasStaticPublicKeyPins("ssl.google-analytics.com")); EXPECT_TRUE(HasStaticPublicKeyPins("www.googleplex.com")); - // Disabled in order to help track down pinning failures --agl EXPECT_TRUE(HasStaticPublicKeyPins("twitter.com")); EXPECT_FALSE(HasStaticPublicKeyPins("foo.twitter.com")); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); @@ -585,6 +633,8 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { } TransportSecurityState state; + EnableStaticPins(&state); + TransportSecurityState::DomainState domain_state; EXPECT_TRUE( state.GetStaticDomainState("blog.torproject.org", true, &domain_state)); @@ -597,6 +647,7 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) { TransportSecurityState state; + EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; EXPECT_FALSE(StaticShouldRedirect("www.google-analytics.com")); diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc index 90e03fe..9ed83db 100644 --- a/net/quic/crypto/proof_verifier_chromium.cc +++ b/net/quic/crypto/proof_verifier_chromium.cc @@ -236,58 +236,20 @@ int ProofVerifierChromium::Job::DoVerifyCert(int result) { int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { verifier_.reset(); -#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS) - // TODO(wtc): The following code was copied from ssl_client_socket_nss.cc. - // Convert it to a new function that can be called by both files. These - // variables simulate the arguments to the new function. const CertVerifyResult& cert_verify_result = verify_details_->cert_verify_result; - bool sni_available = true; - const std::string& host = hostname_; - TransportSecurityState* transport_security_state = transport_security_state_; - std::string* pinning_failure_log = &verify_details_->pinning_failure_log; - - // Take care of any mandates for public key pinning. - // - // Pinning is only enabled for official builds to make sure that others don't - // end up with pins that cannot be easily updated. - // - // TODO(agl): We might have an issue here where a request for foo.example.com - // merges into a SPDY connection to www.example.com, and gets a different - // certificate. - - // Perform pin validation if, and only if, all these conditions obtain: - // - // * a TransportSecurityState object is available; - // * the server's certificate chain is valid (or suffers from only a minor - // error); - // * the server's certificate chain chains up to a known root (i.e. not a - // user-installed trust anchor); and - // * the build is recent (very old builds should fail open so that users - // have some chance to recover). - // const CertStatus cert_status = cert_verify_result.cert_status; - if (transport_security_state && + if (transport_security_state_ && (result == OK || (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && - cert_verify_result.is_issued_by_known_root && - TransportSecurityState::IsBuildTimely()) { - if (transport_security_state->HasPublicKeyPins(host, sni_available)) { - if (!transport_security_state->CheckPublicKeyPins( - host, - sni_available, - cert_verify_result.public_key_hashes, - pinning_failure_log)) { - LOG(ERROR) << *pinning_failure_log; - result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false); - TransportSecurityState::ReportUMAOnPinFailure(host); - } else { - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true); - } - } + !transport_security_state_->CheckPublicKeyPins( + hostname_, + true, /* sni_available */ + cert_verify_result.is_issued_by_known_root, + cert_verify_result.public_key_hashes, + &verify_details_->pinning_failure_log)) { + result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; } -#endif if (result != OK) { std::string error_string = ErrorToString(result); diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 0d3c53d..92af627 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -3427,53 +3427,20 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { if (result == OK) LogConnectionTypeMetrics(); -#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS) - // Take care of any mandates for public key pinning. - // - // Pinning is only enabled for official builds to make sure that others don't - // end up with pins that cannot be easily updated. - // - // TODO(agl): We might have an issue here where a request for foo.example.com - // merges into a SPDY connection to www.example.com, and gets a different - // certificate. - - // Perform pin validation if, and only if, all these conditions obtain: - // - // * a TransportSecurityState object is available; - // * the server's certificate chain is valid (or suffers from only a minor - // error); - // * the server's certificate chain chains up to a known root (i.e. not a - // user-installed trust anchor); and - // * the build is recent (very old builds should fail open so that users - // have some chance to recover). - // + bool sni_available = ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || + ssl_config_.version_fallback; const CertStatus cert_status = server_cert_verify_result_.cert_status; if (transport_security_state_ && (result == OK || (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && - server_cert_verify_result_.is_issued_by_known_root && - TransportSecurityState::IsBuildTimely()) { - bool sni_available = - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || - ssl_config_.version_fallback; - const std::string& host = host_and_port_.host(); - - if (transport_security_state_->HasPublicKeyPins(host, sni_available)) { - if (!transport_security_state_->CheckPublicKeyPins( - host, - sni_available, - server_cert_verify_result_.public_key_hashes, - &pinning_failure_log_)) { - LOG(ERROR) << pinning_failure_log_; - result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false); - TransportSecurityState::ReportUMAOnPinFailure(host); - } else { - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true); - } - } + !transport_security_state_->CheckPublicKeyPins( + host_and_port_.host(), + sni_available, + server_cert_verify_result_.is_issued_by_known_root, + server_cert_verify_result_.public_key_hashes, + &pinning_failure_log_)) { + result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; } -#endif if (result == OK) { // Only check Certificate Transparency if there were no other errors with |