| 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..5cbf4bec5bed7ce089f333d7b4ed0b15800d0614 100644
|
| --- a/chrome/browser/ssl/ssl_error_handler.cc
|
| +++ b/chrome/browser/ssl/ssl_error_handler.cc
|
| @@ -5,12 +5,15 @@
|
| #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/memory/ptr_util.h"
|
| #include "base/metrics/histogram_macros.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "base/threading/non_thread_safe.h"
|
| @@ -21,22 +24,27 @@
|
| #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"
|
| #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
|
| #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
|
| #include "chrome/browser/ssl/captive_portal_blocking_page.h"
|
| +#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
|
| #endif
|
|
|
| namespace {
|
| @@ -44,13 +52,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 +69,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 +137,28 @@ void RecordUMA(SSLErrorHandler::UMAEvent event) {
|
| bool IsCaptivePortalInterstitialEnabled() {
|
| return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
|
| }
|
| +
|
| +// Reads the SSL error assistant configuration from the resource bundle.
|
| +bool ReadErrorAssistantProtoFromResourceBundle(
|
| + chrome_browser_ssl::SSLErrorAssistantConfig* proto) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + DCHECK(proto);
|
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
|
| + base::StringPiece data =
|
| + bundle.GetRawDataResource(IDR_SSL_ERROR_ASSISTANT_PB);
|
| + google::protobuf::io::ArrayInputStream stream(data.data(), data.size());
|
| + return proto->ParseFromZeroCopyStream(&stream);
|
| +}
|
| +
|
| +std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
|
| + const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
|
| + auto hashes = base::MakeUnique<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 +175,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 +191,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 +208,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_;
|
| +#endif
|
| };
|
|
|
| ConfigSingleton::ConfigSingleton()
|
| @@ -187,6 +241,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 +272,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);
|
| +}
|
| +
|
| +bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (!captive_portal_spki_hashes_) {
|
| + chrome_browser_ssl::SSLErrorAssistantConfig proto;
|
| + CHECK(ReadErrorAssistantProtoFromResourceBundle(&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) {
|
| + continue;
|
| + }
|
| + if (captive_portal_spki_hashes_->find(hash_value.ToString()) !=
|
| + captive_portal_spki_hashes_->end()) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +#endif
|
| +
|
| class SSLErrorHandlerDelegateImpl : public SSLErrorHandler::Delegate {
|
| public:
|
| SSLErrorHandlerDelegateImpl(
|
| @@ -365,6 +460,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 +496,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 +533,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());
|
|
|