Chromium Code Reviews| Index: chrome/browser/ssl/ssl_error_handler.cc |
| diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc |
| index 8ddc06febd5a7e15807327dca90f85aae9b8429d..48f796796fdffecec601ab1ab2c5eebdc818363c 100644 |
| --- a/chrome/browser/ssl/ssl_error_handler.cc |
| +++ b/chrome/browser/ssl/ssl_error_handler.cc |
| @@ -5,10 +5,12 @@ |
| #include "chrome/browser/ssl/ssl_error_handler.h" |
| #include <stdint.h> |
| +#include <unordered_set> |
| #include <utility> |
| #include "base/callback_helpers.h" |
| #include "base/feature_list.h" |
| +#include "base/files/file_util.h" |
| #include "base/lazy_instance.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| @@ -21,16 +23,20 @@ |
| #include "chrome/browser/ssl/bad_clock_blocking_page.h" |
| #include "chrome/browser/ssl/ssl_blocking_page.h" |
| #include "chrome/browser/ssl/ssl_cert_reporter.h" |
| +#include "chrome/browser/ssl/ssl_error_assistant.pb.h" |
| #include "chrome/common/features.h" |
| +#include "chrome/grit/browser_resources.h" |
| #include "components/network_time/network_time_tracker.h" |
| #include "components/ssl_errors/error_classification.h" |
| #include "components/ssl_errors/error_info.h" |
| +#include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "net/base/net_errors.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| #include "chrome/browser/captive_portal/captive_portal_service.h" |
| @@ -44,13 +50,14 @@ namespace { |
| #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| const base::Feature kCaptivePortalInterstitial{ |
| "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT}; |
| + |
| +const base::Feature kCaptivePortalCertificateList{ |
| + "CaptivePortalCertificateList", base::FEATURE_DISABLED_BY_DEFAULT}; |
| #endif |
| const base::Feature kSSLCommonNameMismatchHandling{ |
| "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT}; |
| -const char kHistogram[] = "interstitial.ssl_error_handler"; |
| - |
| // Default delay in milliseconds before displaying the SSL interstitial. |
| // This can be changed in tests. |
| // - If there is a name mismatch and a suggested URL available result arrives |
| @@ -60,6 +67,8 @@ const char kHistogram[] = "interstitial.ssl_error_handler"; |
| // - Otherwise, an SSL interstitial is displayed. |
| const int64_t kInterstitialDelayInMilliseconds = 3000; |
| +const char kHistogram[] = "interstitial.ssl_error_handler"; |
| + |
| // Adds a message to console after navigation commits and then, deletes itself. |
| // Also deletes itself if the navigation is stopped. |
| class CommonNameMismatchRedirectObserver |
| @@ -126,6 +135,24 @@ void RecordUMA(SSLErrorHandler::UMAEvent event) { |
| bool IsCaptivePortalInterstitialEnabled() { |
| return base::FeatureList::IsEnabled(kCaptivePortalInterstitial); |
| } |
| + |
| +// Reads the SSL error assistant configuration from the resource bundle. |
| +void ReadErrorAssistantProtoFromResourceBundle(std::string* binary_pb) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| + bundle.GetRawDataResource(IDR_SSL_ERROR_ASSISTANT_PB).CopyToString(binary_pb); |
| +} |
| + |
| +std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes( |
| + const chrome_browser_ssl::SSLErrorAssistantConfig& proto) { |
| + std::unique_ptr<std::unordered_set<std::string>> hashes( |
| + new std::unordered_set<std::string>()); |
| + for (const chrome_browser_ssl::CaptivePortalCert& cert : |
| + proto.captive_portal_cert()) { |
| + hashes.get()->insert(cert.sha256_hash()); |
| + } |
| + return hashes; |
| +} |
| #endif |
| bool IsSSLCommonNameMismatchHandlingEnabled() { |
| @@ -142,6 +169,15 @@ class ConfigSingleton : public base::NonThreadSafe { |
| base::Clock* clock() const; |
| network_time::NetworkTimeTracker* network_time_tracker() const; |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + // Returns true if any of the SHA256 hashes in |ssl_info| is of a captive |
| + // portal certificate. The set of captive portal hashes is loaded on first |
| + // use. |
| + bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info); |
| +#endif |
| + |
| + // Testing methods: |
| + void ResetForTesting(); |
| void SetInterstitialDelayForTesting(const base::TimeDelta& delay); |
| void SetTimerStartedCallbackForTesting( |
| SSLErrorHandler::TimerStartedCallback* callback); |
| @@ -149,6 +185,11 @@ class ConfigSingleton : public base::NonThreadSafe { |
| void SetNetworkTimeTrackerForTesting( |
| network_time::NetworkTimeTracker* tracker); |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + void SetErrorAssistantProtoForTesting( |
| + const chrome_browser_ssl::SSLErrorAssistantConfig& error_assistant_proto); |
| +#endif |
| + |
| private: |
| base::TimeDelta interstitial_delay_; |
| @@ -161,6 +202,13 @@ class ConfigSingleton : public base::NonThreadSafe { |
| base::Clock* testing_clock_ = nullptr; |
| network_time::NetworkTimeTracker* network_time_tracker_ = nullptr; |
| + |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + // SPKI hashes belonging to certs treated as captive portals. Null until the |
| + // first time IsKnownCaptivePortalCert() or SetErrorAssistantProtoForTesting() |
| + // is called. |
| + std::unique_ptr<std::unordered_set<std::string>> captive_portal_spki_hashes_; |
|
Ryan Sleevi
2017/02/01 22:06:06
Have you considered making/requiring the PB be sor
meacer
2017/02/02 01:56:05
I wanted to avoid requiring the PB be sorted becau
|
| +#endif |
| }; |
| ConfigSingleton::ConfigSingleton() |
| @@ -187,6 +235,17 @@ base::Clock* ConfigSingleton::clock() const { |
| return testing_clock_; |
| } |
| +void ConfigSingleton::ResetForTesting() { |
| + interstitial_delay_ = |
| + base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds); |
| + timer_started_callback_ = nullptr; |
| + network_time_tracker_ = nullptr; |
| + testing_clock_ = nullptr; |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + captive_portal_spki_hashes_.reset(); |
| +#endif |
| +} |
| + |
| void ConfigSingleton::SetInterstitialDelayForTesting( |
| const base::TimeDelta& delay) { |
| interstitial_delay_ = delay; |
| @@ -207,6 +266,36 @@ void ConfigSingleton::SetNetworkTimeTrackerForTesting( |
| network_time_tracker_ = tracker; |
| } |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| +void ConfigSingleton::SetErrorAssistantProtoForTesting( |
| + const chrome_browser_ssl::SSLErrorAssistantConfig& error_assistant_proto) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + DCHECK(!captive_portal_spki_hashes_); |
| + captive_portal_spki_hashes_ = |
| + LoadCaptivePortalCertHashes(error_assistant_proto); |
|
Ryan Sleevi
2017/02/01 22:06:06
Can resources be loaded on the UI thread? I though
meacer
2017/02/02 01:56:05
This was going to be one of my main questions to y
|
| +} |
| + |
| +bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + if (!captive_portal_spki_hashes_) { |
| + std::string binary_proto; |
| + ReadErrorAssistantProtoFromResourceBundle(&binary_proto); |
| + chrome_browser_ssl::SSLErrorAssistantConfig proto; |
| + proto.ParseFromString(binary_proto); |
| + captive_portal_spki_hashes_ = LoadCaptivePortalCertHashes(proto); |
| + } |
| + |
| + for (const net::HashValue& hash_value : ssl_info.public_key_hashes) { |
| + if (hash_value.tag == net::HASH_VALUE_SHA256 && |
| + captive_portal_spki_hashes_->find(hash_value.ToString()) != |
| + captive_portal_spki_hashes_->end()) { |
|
Ryan Sleevi
2017/02/01 22:06:06
nit: I tend to encourage writing fewer compound co
meacer
2017/02/02 01:56:05
Done.
|
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| +#endif |
| + |
| class SSLErrorHandlerDelegateImpl : public SSLErrorHandler::Delegate { |
| public: |
| SSLErrorHandlerDelegateImpl( |
| @@ -365,6 +454,11 @@ void SSLErrorHandler::HandleSSLError( |
| } |
| // static |
| +void SSLErrorHandler::ResetConfigForTesting() { |
| + g_config.Pointer()->ResetForTesting(); |
| +} |
| + |
| +// static |
| void SSLErrorHandler::SetInterstitialDelayForTesting( |
| const base::TimeDelta& delay) { |
| g_config.Pointer()->SetInterstitialDelayForTesting(delay); |
| @@ -396,6 +490,13 @@ bool SSLErrorHandler::IsTimerRunningForTesting() const { |
| return timer_.IsRunning(); |
| } |
| +void SSLErrorHandler::SetErrorAssistantProtoForTesting( |
| + const chrome_browser_ssl::SSLErrorAssistantConfig& config_proto) { |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + g_config.Pointer()->SetErrorAssistantProtoForTesting(config_proto); |
| +#endif |
| +} |
| + |
| SSLErrorHandler::SSLErrorHandler( |
| std::unique_ptr<Delegate> delegate, |
| content::WebContents* web_contents, |
| @@ -426,6 +527,17 @@ void SSLErrorHandler::StartHandlingError() { |
| return; |
| } |
| +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) |
| + if (base::FeatureList::IsEnabled(kCaptivePortalCertificateList) && |
| + cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID && |
| + g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) { |
| + RecordUMA(CAPTIVE_PORTAL_CERT_FOUND); |
| + ShowCaptivePortalInterstitial( |
| + GURL(captive_portal::CaptivePortalDetector::kDefaultURL)); |
| + return; |
| + } |
| +#endif |
| + |
| std::vector<std::string> dns_names; |
| ssl_info_.cert->GetDNSNames(&dns_names); |
| DCHECK(!dns_names.empty()); |