blob: 37926352a2c1dc4549f359ded0312c4675b37141 [file] [log] [blame]
meacer4ef065e2015-01-09 03:21:351// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ssl/ssl_error_handler.h"
6
avi664c07b2015-12-26 02:18:317#include <stdint.h>
meacerb0785802017-02-07 23:46:528#include <unordered_set>
dchenge73d8520c2015-12-27 01:19:099#include <utility>
avi664c07b2015-12-26 02:18:3110
meacerd2c91b42015-03-20 18:34:4011#include "base/callback_helpers.h"
meacer80166852016-12-08 01:51:3612#include "base/feature_list.h"
meacerb0785802017-02-07 23:46:5213#include "base/files/file_util.h"
meacer352dc2e2017-01-10 01:27:4114#include "base/lazy_instance.h"
avi664c07b2015-12-26 02:18:3115#include "base/macros.h"
meacerb0785802017-02-07 23:46:5216#include "base/memory/ptr_util.h"
asvitkineaa060312016-09-01 22:44:1317#include "base/metrics/histogram_macros.h"
bhanudevd6bd7782015-08-21 00:01:5418#include "base/strings/stringprintf.h"
felt534cc9e72015-09-01 20:38:5919#include "base/time/clock.h"
meacer4ef065e2015-01-09 03:21:3520#include "base/time/time.h"
Emily Starkf896a372017-07-26 00:08:2421#include "build/build_config.h"
mab740b02b2016-03-17 23:44:2122#include "chrome/browser/browser_process.h"
meacer4ef065e2015-01-09 03:21:3523#include "chrome/browser/profiles/profile.h"
felt534cc9e72015-09-01 20:38:5924#include "chrome/browser/ssl/bad_clock_blocking_page.h"
Mustafa Emre Acer12b5effe2017-09-26 19:26:3525#include "chrome/browser/ssl/captive_portal_blocking_page.h"
Mustafa Acerac3e8812017-09-11 20:31:0126#include "chrome/browser/ssl/mitm_software_blocking_page.h"
meacer4ef065e2015-01-09 03:21:3527#include "chrome/browser/ssl/ssl_blocking_page.h"
estark3ba11132015-04-10 01:38:3328#include "chrome/browser/ssl/ssl_cert_reporter.h"
meacerb0785802017-02-07 23:46:5229#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
brettwab78fef2016-10-12 02:56:0530#include "chrome/common/features.h"
meacerb0785802017-02-07 23:46:5231#include "chrome/grit/browser_resources.h"
estark5057f822016-11-08 21:34:5432#include "components/network_time/network_time_tracker.h"
felt70127b42015-11-10 21:57:2333#include "components/ssl_errors/error_classification.h"
felt2493b4452015-09-17 20:33:5934#include "components/ssl_errors/error_info.h"
meacerb0785802017-02-07 23:46:5235#include "content/public/browser/browser_thread.h"
jam6625f122017-01-20 06:37:3336#include "content/public/browser/navigation_handle.h"
meacer4ef065e2015-01-09 03:21:3537#include "content/public/browser/notification_service.h"
38#include "content/public/browser/notification_source.h"
bhanudevd6bd7782015-08-21 00:01:5439#include "content/public/browser/render_frame_host.h"
meacer4ef065e2015-01-09 03:21:3540#include "content/public/browser/web_contents.h"
bhanudev3999e372015-08-21 07:23:1041#include "net/base/net_errors.h"
Sasha Perigo2bfbbe902017-08-10 21:11:0342#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
Mustafa Acerac3e8812017-09-11 20:31:0143#include "third_party/re2/src/re2/re2.h"
meacerb0785802017-02-07 23:46:5244#include "ui/base/resource/resource_bundle.h"
meacer4ef065e2015-01-09 03:21:3545
brettwab78fef2016-10-12 02:56:0546#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
meacer4ef065e2015-01-09 03:21:3547#include "chrome/browser/captive_portal/captive_portal_service.h"
48#include "chrome/browser/captive_portal/captive_portal_service_factory.h"
49#include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
meacer4ef065e2015-01-09 03:21:3550#endif
51
Sasha Perigo232e21aa2017-08-19 01:45:4052#if defined(OS_WIN)
53#include "base/win/win_util.h"
54#elif defined(OS_CHROMEOS)
55#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
56#endif // #if defined(OS_WIN)
57
meacer4ef065e2015-01-09 03:21:3558namespace {
59
Sasha Perigo2bfbbe902017-08-10 21:11:0360const base::Feature kMITMSoftwareInterstitial{
61 "MITMSoftwareInterstitial", base::FEATURE_DISABLED_BY_DEFAULT};
Sasha Perigo2bfbbe902017-08-10 21:11:0362
meacer80166852016-12-08 01:51:3663#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
64const base::Feature kCaptivePortalInterstitial{
65 "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
Mustafa Emre Acer12b5effe2017-09-26 19:26:3566#endif
meacerb0785802017-02-07 23:46:5267
68const base::Feature kCaptivePortalCertificateList{
69 "CaptivePortalCertificateList", base::FEATURE_DISABLED_BY_DEFAULT};
meacer80166852016-12-08 01:51:3670
meacer93c0f802016-12-08 19:55:1371const base::Feature kSSLCommonNameMismatchHandling{
72 "SSLCommonNameMismatchHandling", base::FEATURE_ENABLED_BY_DEFAULT};
73
Emily Starkf896a372017-07-26 00:08:2474#if defined(OS_WIN)
75const base::Feature kSuperfishInterstitial{"SuperfishInterstitial",
76 base::FEATURE_ENABLED_BY_DEFAULT};
77#else
estark5cbbc092017-06-23 02:00:5678const base::Feature kSuperfishInterstitial{"SuperfishInterstitial",
79 base::FEATURE_DISABLED_BY_DEFAULT};
Emily Starkf896a372017-07-26 00:08:2480#endif
estark5cbbc092017-06-23 02:00:5681
meacer352dc2e2017-01-10 01:27:4182// Default delay in milliseconds before displaying the SSL interstitial.
meacer8b4cea22015-08-28 02:02:5583// This can be changed in tests.
84// - If there is a name mismatch and a suggested URL available result arrives
85// during this time, the user is redirected to the suggester URL.
86// - If a "captive portal detected" result arrives during this time,
87// a captive portal interstitial is displayed.
88// - Otherwise, an SSL interstitial is displayed.
meacer352dc2e2017-01-10 01:27:4189const int64_t kInterstitialDelayInMilliseconds = 3000;
estark5057f822016-11-08 21:34:5490
meacerb0785802017-02-07 23:46:5291const char kHistogram[] = "interstitial.ssl_error_handler";
92
estark5cbbc092017-06-23 02:00:5693bool IsSuperfish(const scoped_refptr<net::X509Certificate>& cert) {
94 // This is the fingerprint of the well-known Superfish certificate at
95 // https://pastebin.com/WcXv8QcG. Superfish is identified by certificate
96 // fingerprint rather than SPKI because net::SSLInfo does not guarantee
97 // |public_key_hashes| (the SPKIs) to be populated if the certificate doesn't
98 // verify successfully. It so happens that Superfish uses the same certificate
99 // universally (not just the same public key), and calculating the fingerprint
100 // is more convenient here than calculating the SPKI.
101 const net::SHA256HashValue kSuperfishFingerprint{
102 {0xB6, 0xFE, 0x91, 0x51, 0x40, 0x2B, 0xAD, 0x1C, 0x06, 0xD7, 0xE6,
103 0x6D, 0xB6, 0x7A, 0x26, 0xAA, 0x73, 0x56, 0xF2, 0xE6, 0xC6, 0x44,
104 0xDB, 0xCF, 0x9F, 0x98, 0x96, 0x8F, 0xF6, 0x32, 0xE1, 0xB7}};
105 for (const net::X509Certificate::OSCertHandle& intermediate :
106 cert->GetIntermediateCertificates()) {
107 net::SHA256HashValue hash =
108 net::X509Certificate::CalculateFingerprint256(intermediate);
109 if (hash == kSuperfishFingerprint) {
110 return true;
estark6767d1b2017-06-03 17:37:20111 }
112 }
estark5cbbc092017-06-23 02:00:56113 return false;
114}
115
116// Records an UMA histogram for whether the Superfish certificate was present in
117// the certificate chain.
118void RecordSuperfishUMA(const scoped_refptr<net::X509Certificate>& cert) {
estark6767d1b2017-06-03 17:37:20119 UMA_HISTOGRAM_BOOLEAN("interstitial.ssl_error_handler.superfish",
estark5cbbc092017-06-23 02:00:56120 IsSuperfish(cert));
estark6767d1b2017-06-03 17:37:20121}
122
meacer8b4cea22015-08-28 02:02:55123// Adds a message to console after navigation commits and then, deletes itself.
124// Also deletes itself if the navigation is stopped.
125class CommonNameMismatchRedirectObserver
126 : public content::WebContentsObserver,
127 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> {
128 public:
avi284ec612017-05-03 01:52:23129 ~CommonNameMismatchRedirectObserver() override {}
130
meacer8b4cea22015-08-28 02:02:55131 static void AddToConsoleAfterNavigation(
132 content::WebContents* web_contents,
133 const std::string& request_url_hostname,
134 const std::string& suggested_url_hostname) {
135 web_contents->SetUserData(
136 UserDataKey(),
avi284ec612017-05-03 01:52:23137 base::WrapUnique(new CommonNameMismatchRedirectObserver(
138 web_contents, request_url_hostname, suggested_url_hostname)));
meacer4ef065e2015-01-09 03:21:35139 }
meacer8b4cea22015-08-28 02:02:55140
141 private:
142 CommonNameMismatchRedirectObserver(content::WebContents* web_contents,
143 const std::string& request_url_hostname,
144 const std::string& suggested_url_hostname)
145 : WebContentsObserver(web_contents),
146 web_contents_(web_contents),
147 request_url_hostname_(request_url_hostname),
148 suggested_url_hostname_(suggested_url_hostname) {}
meacer8b4cea22015-08-28 02:02:55149
150 // WebContentsObserver:
151 void NavigationStopped() override {
152 // Deletes |this|.
153 web_contents_->RemoveUserData(UserDataKey());
154 }
155
156 void NavigationEntryCommitted(
157 const content::LoadCommittedDetails& /* load_details */) override {
158 web_contents_->GetMainFrame()->AddMessageToConsole(
pfeldman2bcbc1222017-01-21 06:08:54159 content::CONSOLE_MESSAGE_LEVEL_INFO,
meacer8b4cea22015-08-28 02:02:55160 base::StringPrintf(
161 "Redirecting navigation %s -> %s because the server presented a "
meaceree63a4e2016-03-30 23:03:19162 "certificate valid for %s but not for %s. To disable such "
163 "redirects launch Chrome with the following flag: "
meacer93c0f802016-12-08 19:55:13164 "--disable-features=SSLCommonNameMismatchHandling",
meacer8b4cea22015-08-28 02:02:55165 request_url_hostname_.c_str(), suggested_url_hostname_.c_str(),
166 suggested_url_hostname_.c_str(), request_url_hostname_.c_str()));
167 web_contents_->RemoveUserData(UserDataKey());
168 }
169
170 void WebContentsDestroyed() override {
171 web_contents_->RemoveUserData(UserDataKey());
172 }
173
174 content::WebContents* web_contents_;
175 const std::string request_url_hostname_;
176 const std::string suggested_url_hostname_;
177
178 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver);
179};
180
meacerc7693c792017-02-01 01:42:58181void RecordUMA(SSLErrorHandler::UMAEvent event) {
182 UMA_HISTOGRAM_ENUMERATION(kHistogram, event,
183 SSLErrorHandler::SSL_ERROR_HANDLER_EVENT_COUNT);
meacer4ef065e2015-01-09 03:21:35184}
meacer5c777682015-01-09 06:54:53185
brettwab78fef2016-10-12 02:56:05186#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
meacer5c777682015-01-09 06:54:53187bool IsCaptivePortalInterstitialEnabled() {
meacer80166852016-12-08 01:51:36188 return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
meacer5c777682015-01-09 06:54:53189}
Mustafa Emre Acer12b5effe2017-09-26 19:26:35190#endif
meacerb0785802017-02-07 23:46:52191
Sasha Perigo2bfbbe902017-08-10 21:11:03192std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
193 const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
194 auto hashes = base::MakeUnique<std::unordered_set<std::string>>();
195 for (const chrome_browser_ssl::CaptivePortalCert& cert :
196 proto.captive_portal_cert()) {
197 hashes.get()->insert(cert.sha256_hash());
198 }
199 return hashes;
200}
Sasha Perigo2bfbbe902017-08-10 21:11:03201
Sasha Perigo2bfbbe902017-08-10 21:11:03202bool IsMITMSoftwareInterstitialEnabled() {
203 return base::FeatureList::IsEnabled(kMITMSoftwareInterstitial);
204}
205
Sasha Perigobc595cf2017-08-30 19:13:48206// Struct which stores data about a known MITM software pulled from the
207// SSLErrorAssistant proto.
208struct MITMSoftwareType {
209 MITMSoftwareType(const std::string& name,
210 const std::string& issuer_common_name_regex,
211 const std::string& issuer_organization_regex)
212 : name(name),
213 issuer_common_name_regex(issuer_common_name_regex),
214 issuer_organization_regex(issuer_organization_regex) {}
215
216 const std::string name;
217 const std::string issuer_common_name_regex;
218 const std::string issuer_organization_regex;
219};
220
221std::unique_ptr<std::vector<MITMSoftwareType>> LoadMITMSoftwareList(
222 const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
223 auto mitm_software_list = base::MakeUnique<std::vector<MITMSoftwareType>>();
224
225 for (const chrome_browser_ssl::MITMSoftware& proto_entry :
226 proto.mitm_software()) {
227 // The |name| field and at least one of the |issuer_common_name_regex| and
228 // |issuer_organization_regex| fields must be set.
229 DCHECK(!proto_entry.name().empty());
230 DCHECK(!(proto_entry.issuer_common_name_regex().empty() &&
231 proto_entry.issuer_organization_regex().empty()));
232 if (proto_entry.name().empty() ||
233 (proto_entry.issuer_common_name_regex().empty() &&
234 proto_entry.issuer_organization_regex().empty())) {
235 continue;
236 }
237
238 mitm_software_list.get()->push_back(MITMSoftwareType(
239 proto_entry.name(), proto_entry.issuer_common_name_regex(),
240 proto_entry.issuer_organization_regex()));
Sasha Perigo2bfbbe902017-08-10 21:11:03241 }
Sasha Perigobc595cf2017-08-30 19:13:48242 return mitm_software_list;
Sasha Perigo2bfbbe902017-08-10 21:11:03243}
Sasha Perigo2bfbbe902017-08-10 21:11:03244
meacerb0785802017-02-07 23:46:52245// Reads the SSL error assistant configuration from the resource bundle.
meacer4404d452017-02-10 02:30:54246std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
247ReadErrorAssistantProtoFromResourceBundle() {
248 auto proto = base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
meacerb0785802017-02-07 23:46:52249 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
250 DCHECK(proto);
251 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
252 base::StringPiece data =
253 bundle.GetRawDataResource(IDR_SSL_ERROR_ASSISTANT_PB);
254 google::protobuf::io::ArrayInputStream stream(data.data(), data.size());
meacer4404d452017-02-10 02:30:54255 return proto->ParseFromZeroCopyStream(&stream) ? std::move(proto) : nullptr;
meacerb0785802017-02-07 23:46:52256}
257
bhanudev4b280dc2015-08-20 20:43:00258bool IsSSLCommonNameMismatchHandlingEnabled() {
meacer93c0f802016-12-08 19:55:13259 return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling);
bhanudev4b280dc2015-08-20 20:43:00260}
261
meacer352dc2e2017-01-10 01:27:41262// Configuration for SSLErrorHandler.
gab2a481812017-05-30 15:08:50263class ConfigSingleton {
meacer352dc2e2017-01-10 01:27:41264 public:
265 ConfigSingleton();
266
267 base::TimeDelta interstitial_delay() const;
268 SSLErrorHandler::TimerStartedCallback* timer_started_callback() const;
269 base::Clock* clock() const;
270 network_time::NetworkTimeTracker* network_time_tracker() const;
271
meacerb0785802017-02-07 23:46:52272 // Returns true if any of the SHA256 hashes in |ssl_info| is of a captive
273 // portal certificate. The set of captive portal hashes is loaded on first
274 // use.
275 bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info);
meacerb0785802017-02-07 23:46:52276
Sasha Perigo232e21aa2017-08-19 01:45:40277 // Returns the name of a known MITM software provider that matches the
278 // certificate passed in as the |cert| parameter. Returns empty string if
279 // there is no match.
280 const std::string MatchKnownMITMSoftware(
281 const scoped_refptr<net::X509Certificate> cert);
Sasha Perigo2bfbbe902017-08-10 21:11:03282
meacerb0785802017-02-07 23:46:52283 // Testing methods:
284 void ResetForTesting();
meacer352dc2e2017-01-10 01:27:41285 void SetInterstitialDelayForTesting(const base::TimeDelta& delay);
286 void SetTimerStartedCallbackForTesting(
287 SSLErrorHandler::TimerStartedCallback* callback);
288 void SetClockForTesting(base::Clock* clock);
289 void SetNetworkTimeTrackerForTesting(
290 network_time::NetworkTimeTracker* tracker);
Mustafa Emre Acer12b5effe2017-09-26 19:26:35291
meacer4404d452017-02-10 02:30:54292 void SetErrorAssistantProto(
293 std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
294 error_assistant_proto);
Sasha Perigo232e21aa2017-08-19 01:45:40295 void SetEnterpriseManagedForTesting(bool enterprise_managed);
296 bool IsEnterpriseManagedFlagSetForTesting() const;
Mustafa Acer3bc5efb2017-09-19 23:05:46297 int GetErrorAssistantProtoVersionIdForTesting() const;
298
Sasha Perigo232e21aa2017-08-19 01:45:40299 bool IsEnterpriseManaged() const;
meacerb0785802017-02-07 23:46:52300
meacer352dc2e2017-01-10 01:27:41301 private:
302 base::TimeDelta interstitial_delay_;
303
304 // Callback to call when the interstitial timer is started. Used for
305 // testing.
306 SSLErrorHandler::TimerStartedCallback* timer_started_callback_ = nullptr;
307
308 // The clock to use when deciding which error type to display. Used for
309 // testing.
310 base::Clock* testing_clock_ = nullptr;
311
312 network_time::NetworkTimeTracker* network_time_tracker_ = nullptr;
meacerb0785802017-02-07 23:46:52313
meacer4404d452017-02-10 02:30:54314 // Error assistant configuration.
315 std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
316 error_assistant_proto_;
317
Sasha Perigobc595cf2017-08-30 19:13:48318 std::unique_ptr<std::vector<MITMSoftwareType>> mitm_software_list_;
Sasha Perigo232e21aa2017-08-19 01:45:40319
320 enum EnterpriseManaged {
321 ENTERPRISE_MANAGED_STATUS_NOT_SET,
322 ENTERPRISE_MANAGED_STATUS_TRUE,
323 ENTERPRISE_MANAGED_STATUS_FALSE
324 };
325 EnterpriseManaged is_enterprise_managed_for_testing_;
Sasha Perigo2bfbbe902017-08-10 21:11:03326
meacerb0785802017-02-07 23:46:52327 // SPKI hashes belonging to certs treated as captive portals. Null until the
meacer4404d452017-02-10 02:30:54328 // first time IsKnownCaptivePortalCert() or SetErrorAssistantProto()
meacerb0785802017-02-07 23:46:52329 // is called.
330 std::unique_ptr<std::unordered_set<std::string>> captive_portal_spki_hashes_;
meacer352dc2e2017-01-10 01:27:41331};
332
333ConfigSingleton::ConfigSingleton()
334 : interstitial_delay_(
Sasha Perigo232e21aa2017-08-19 01:45:40335 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds)),
336 is_enterprise_managed_for_testing_(ENTERPRISE_MANAGED_STATUS_NOT_SET) {}
meacer352dc2e2017-01-10 01:27:41337
338base::TimeDelta ConfigSingleton::interstitial_delay() const {
339 return interstitial_delay_;
340}
341
342SSLErrorHandler::TimerStartedCallback* ConfigSingleton::timer_started_callback()
343 const {
344 return timer_started_callback_;
345}
346
347network_time::NetworkTimeTracker* ConfigSingleton::network_time_tracker()
348 const {
349 return network_time_tracker_ ? network_time_tracker_
350 : g_browser_process->network_time_tracker();
351}
352
353base::Clock* ConfigSingleton::clock() const {
354 return testing_clock_;
355}
356
meacerb0785802017-02-07 23:46:52357void ConfigSingleton::ResetForTesting() {
358 interstitial_delay_ =
359 base::TimeDelta::FromMilliseconds(kInterstitialDelayInMilliseconds);
360 timer_started_callback_ = nullptr;
361 network_time_tracker_ = nullptr;
362 testing_clock_ = nullptr;
meacer4404d452017-02-10 02:30:54363 error_assistant_proto_.reset();
Sasha Perigo232e21aa2017-08-19 01:45:40364 mitm_software_list_.reset();
365 is_enterprise_managed_for_testing_ = ENTERPRISE_MANAGED_STATUS_NOT_SET;
meacerb0785802017-02-07 23:46:52366 captive_portal_spki_hashes_.reset();
meacerb0785802017-02-07 23:46:52367}
368
meacer352dc2e2017-01-10 01:27:41369void ConfigSingleton::SetInterstitialDelayForTesting(
370 const base::TimeDelta& delay) {
371 interstitial_delay_ = delay;
372}
373
374void ConfigSingleton::SetTimerStartedCallbackForTesting(
375 SSLErrorHandler::TimerStartedCallback* callback) {
376 DCHECK(!callback || !callback->is_null());
377 timer_started_callback_ = callback;
378}
379
380void ConfigSingleton::SetClockForTesting(base::Clock* clock) {
381 testing_clock_ = clock;
382}
383
384void ConfigSingleton::SetNetworkTimeTrackerForTesting(
385 network_time::NetworkTimeTracker* tracker) {
386 network_time_tracker_ = tracker;
387}
388
Sasha Perigo232e21aa2017-08-19 01:45:40389void ConfigSingleton::SetEnterpriseManagedForTesting(bool enterprise_managed) {
390 if (enterprise_managed) {
391 is_enterprise_managed_for_testing_ = ENTERPRISE_MANAGED_STATUS_TRUE;
392 } else {
393 is_enterprise_managed_for_testing_ = ENTERPRISE_MANAGED_STATUS_FALSE;
394 }
395}
396
397bool ConfigSingleton::IsEnterpriseManagedFlagSetForTesting() const {
Sasha Perigo232e21aa2017-08-19 01:45:40398 if (is_enterprise_managed_for_testing_ == ENTERPRISE_MANAGED_STATUS_NOT_SET) {
399 return false;
400 }
401 return true;
Sasha Perigo232e21aa2017-08-19 01:45:40402}
403
Mustafa Acer3bc5efb2017-09-19 23:05:46404int ConfigSingleton::GetErrorAssistantProtoVersionIdForTesting() const {
405 return error_assistant_proto_->version_id();
406}
407
Sasha Perigo232e21aa2017-08-19 01:45:40408bool ConfigSingleton::IsEnterpriseManaged() const {
Sasha Perigo232e21aa2017-08-19 01:45:40409 // Return the value of the testing flag if it's set.
410 if (is_enterprise_managed_for_testing_ == ENTERPRISE_MANAGED_STATUS_TRUE) {
411 return true;
412 }
413 if (is_enterprise_managed_for_testing_ == ENTERPRISE_MANAGED_STATUS_FALSE) {
414 return false;
415 }
416
417#if defined(OS_WIN)
418 if (base::win::IsEnterpriseManaged()) {
419 return true;
420 }
421#elif defined(OS_CHROMEOS)
422 if (g_browser_process->platform_part()->browser_policy_connector_chromeos()) {
423 return true;
424 }
425#endif // #if defined(OS_WIN)
Sasha Perigo232e21aa2017-08-19 01:45:40426 return false;
Sasha Perigo232e21aa2017-08-19 01:45:40427}
428
meacer4404d452017-02-10 02:30:54429void ConfigSingleton::SetErrorAssistantProto(
430 std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig> proto) {
meacerb0785802017-02-07 23:46:52431 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
meacer4404d452017-02-10 02:30:54432 CHECK(proto);
Mustafa Acer3bc5efb2017-09-19 23:05:46433 if (!error_assistant_proto_) {
434 // If the user hasn't seen an SSL error and a component update is available,
435 // the local resource bundle won't have been read and error_assistant_proto_
436 // will be null. It's possible that the local resource bundle has a higher
437 // version_id than the component updater component, so load the local
438 // resource bundle once to compare versions.
439 // TODO(meacer): Ideally, ReadErrorAssistantProtoFromResourceBundle should
440 // only be called once and not on the UI thread. Move the call to the
441 // component updater component.
442 error_assistant_proto_ = ReadErrorAssistantProtoFromResourceBundle();
443 }
meacer4404d452017-02-10 02:30:54444 // Ignore versions that are not new.
445 if (error_assistant_proto_ &&
446 proto->version_id() <= error_assistant_proto_->version_id()) {
447 return;
448 }
449 error_assistant_proto_ = std::move(proto);
Sasha Perigo2bfbbe902017-08-10 21:11:03450
Sasha Perigo232e21aa2017-08-19 01:45:40451 mitm_software_list_ = LoadMITMSoftwareList(*error_assistant_proto_);
Sasha Perigo2bfbbe902017-08-10 21:11:03452
meacerb0785802017-02-07 23:46:52453 captive_portal_spki_hashes_ =
meacer4404d452017-02-10 02:30:54454 LoadCaptivePortalCertHashes(*error_assistant_proto_);
meacerb0785802017-02-07 23:46:52455}
meacerb0785802017-02-07 23:46:52456
meacerb0785802017-02-07 23:46:52457bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
458 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
459 if (!captive_portal_spki_hashes_) {
meacer4404d452017-02-10 02:30:54460 error_assistant_proto_ = ReadErrorAssistantProtoFromResourceBundle();
461 CHECK(error_assistant_proto_);
462 captive_portal_spki_hashes_ =
463 LoadCaptivePortalCertHashes(*error_assistant_proto_);
meacerb0785802017-02-07 23:46:52464 }
465
466 for (const net::HashValue& hash_value : ssl_info.public_key_hashes) {
467 if (hash_value.tag != net::HASH_VALUE_SHA256) {
468 continue;
469 }
470 if (captive_portal_spki_hashes_->find(hash_value.ToString()) !=
471 captive_portal_spki_hashes_->end()) {
472 return true;
473 }
474 }
475 return false;
476}
meacerb0785802017-02-07 23:46:52477
Sasha Perigobc595cf2017-08-30 19:13:48478bool RegexMatchesAny(const std::vector<std::string>& organization_names,
479 const std::string& pattern) {
480 const re2::RE2 regex(pattern);
481 for (const std::string& organization_name : organization_names) {
482 if (re2::RE2::FullMatch(organization_name, regex)) {
483 return true;
484 }
485 }
486 return false;
487}
488
Sasha Perigo232e21aa2017-08-19 01:45:40489const std::string ConfigSingleton::MatchKnownMITMSoftware(
490 const scoped_refptr<net::X509Certificate> cert) {
Sasha Perigobc595cf2017-08-30 19:13:48491 // Ignore if the certificate doesn't have an issuer common name or an
492 // organization name.
493 if (cert->issuer().common_name.empty() &&
494 cert->issuer().organization_names.size() == 0) {
Sasha Perigo232e21aa2017-08-19 01:45:40495 return std::string();
Sasha Perigo2bfbbe902017-08-10 21:11:03496 }
497
Sasha Perigobc595cf2017-08-30 19:13:48498 // Load MITM software data from the SSL error assistant proto.
Sasha Perigo2bfbbe902017-08-10 21:11:03499 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Sasha Perigo232e21aa2017-08-19 01:45:40500 if (!mitm_software_list_) {
Sasha Perigo2bfbbe902017-08-10 21:11:03501 error_assistant_proto_ = ReadErrorAssistantProtoFromResourceBundle();
502 DCHECK(error_assistant_proto_);
Sasha Perigo232e21aa2017-08-19 01:45:40503 mitm_software_list_ = LoadMITMSoftwareList(*error_assistant_proto_);
Sasha Perigo2bfbbe902017-08-10 21:11:03504 }
505
Sasha Perigobc595cf2017-08-30 19:13:48506 for (const MITMSoftwareType& mitm_software : *mitm_software_list_) {
507 // At least one of the common name or organization name fields should be
508 // populated on the MITM software list entry.
509 DCHECK(!(mitm_software.issuer_common_name_regex.empty() &&
510 mitm_software.issuer_organization_regex.empty()));
511 if (mitm_software.issuer_common_name_regex.empty() &&
512 mitm_software.issuer_organization_regex.empty()) {
513 continue;
514 }
515
516 // If both |issuer_common_name_regex| and |issuer_organization_regex| are
517 // set, the certificate should match both regexes.
518 if (!mitm_software.issuer_common_name_regex.empty() &&
519 !mitm_software.issuer_organization_regex.empty()) {
520 if (re2::RE2::FullMatch(
521 cert->issuer().common_name,
522 re2::RE2(mitm_software.issuer_common_name_regex)) &&
523 RegexMatchesAny(cert->issuer().organization_names,
524 mitm_software.issuer_organization_regex)) {
525 return mitm_software.name;
526 }
527
528 // If only |issuer_organization_regex| is set, the certificate's issuer
529 // organization name should match.
530 } else if (!mitm_software.issuer_organization_regex.empty()) {
531 if (RegexMatchesAny(cert->issuer().organization_names,
532 mitm_software.issuer_organization_regex)) {
533 return mitm_software.name;
534 }
535
536 // If only |issuer_common_name_regex| is set, the certificate's issuer
537 // common name should match.
538 } else if (!mitm_software.issuer_common_name_regex.empty()) {
539 if (re2::RE2::FullMatch(
540 cert->issuer().common_name,
541 re2::RE2(mitm_software.issuer_common_name_regex))) {
542 return mitm_software.name;
543 }
Sasha Perigo2bfbbe902017-08-10 21:11:03544 }
545 }
Sasha Perigo232e21aa2017-08-19 01:45:40546 return std::string();
Sasha Perigo2bfbbe902017-08-10 21:11:03547}
Sasha Perigo2bfbbe902017-08-10 21:11:03548
meacerc7693c792017-02-01 01:42:58549class SSLErrorHandlerDelegateImpl : public SSLErrorHandler::Delegate {
550 public:
551 SSLErrorHandlerDelegateImpl(
552 content::WebContents* web_contents,
553 const net::SSLInfo& ssl_info,
554 Profile* const profile,
555 int cert_error,
556 int options_mask,
557 const GURL& request_url,
558 std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
559 const base::Callback<void(content::CertificateRequestResultType)>&
560 callback)
561 : web_contents_(web_contents),
562 ssl_info_(ssl_info),
563 profile_(profile),
564 cert_error_(cert_error),
565 options_mask_(options_mask),
566 request_url_(request_url),
567 ssl_cert_reporter_(std::move(ssl_cert_reporter)),
568 callback_(callback) {}
569 ~SSLErrorHandlerDelegateImpl() override;
570
571 // SSLErrorHandler::Delegate methods:
572 void CheckForCaptivePortal() override;
573 bool GetSuggestedUrl(const std::vector<std::string>& dns_names,
574 GURL* suggested_url) const override;
575 void CheckSuggestedUrl(
576 const GURL& suggested_url,
577 const CommonNameMismatchHandler::CheckUrlCallback& callback) override;
578 void NavigateToSuggestedURL(const GURL& suggested_url) override;
579 bool IsErrorOverridable() const override;
580 void ShowCaptivePortalInterstitial(const GURL& landing_url) override;
Sasha Perigo232e21aa2017-08-19 01:45:40581 void ShowMITMSoftwareInterstitial(const std::string& mitm_software_name,
582 bool is_enterprise_managed) override;
meacerc7693c792017-02-01 01:42:58583 void ShowSSLInterstitial() override;
584 void ShowBadClockInterstitial(const base::Time& now,
585 ssl_errors::ClockState clock_state) override;
586
587 private:
588 content::WebContents* web_contents_;
589 const net::SSLInfo& ssl_info_;
590 Profile* const profile_;
591 const int cert_error_;
592 const int options_mask_;
593 const GURL request_url_;
594 std::unique_ptr<CommonNameMismatchHandler> common_name_mismatch_handler_;
595 std::unique_ptr<SSLCertReporter> ssl_cert_reporter_;
596 const base::Callback<void(content::CertificateRequestResultType)> callback_;
597};
598
599SSLErrorHandlerDelegateImpl::~SSLErrorHandlerDelegateImpl() {
600 if (common_name_mismatch_handler_) {
601 common_name_mismatch_handler_->Cancel();
602 common_name_mismatch_handler_.reset();
603 }
604}
605
606void SSLErrorHandlerDelegateImpl::CheckForCaptivePortal() {
607#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
608 CaptivePortalService* captive_portal_service =
609 CaptivePortalServiceFactory::GetForProfile(profile_);
610 captive_portal_service->DetectCaptivePortal();
611#else
612 NOTREACHED();
613#endif
614}
615
616bool SSLErrorHandlerDelegateImpl::GetSuggestedUrl(
617 const std::vector<std::string>& dns_names,
618 GURL* suggested_url) const {
619 return CommonNameMismatchHandler::GetSuggestedUrl(request_url_, dns_names,
620 suggested_url);
621}
622
623void SSLErrorHandlerDelegateImpl::CheckSuggestedUrl(
624 const GURL& suggested_url,
625 const CommonNameMismatchHandler::CheckUrlCallback& callback) {
626 scoped_refptr<net::URLRequestContextGetter> request_context(
627 profile_->GetRequestContext());
628 common_name_mismatch_handler_.reset(
629 new CommonNameMismatchHandler(request_url_, request_context));
630
631 common_name_mismatch_handler_->CheckSuggestedUrl(suggested_url, callback);
632}
633
634void SSLErrorHandlerDelegateImpl::NavigateToSuggestedURL(
635 const GURL& suggested_url) {
636 content::NavigationController::LoadURLParams load_params(suggested_url);
637 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
638 web_contents_->GetController().LoadURLWithParams(load_params);
639}
640
641bool SSLErrorHandlerDelegateImpl::IsErrorOverridable() const {
642 return SSLBlockingPage::IsOverridable(options_mask_, profile_);
643}
644
645void SSLErrorHandlerDelegateImpl::ShowCaptivePortalInterstitial(
646 const GURL& landing_url) {
meacerc7693c792017-02-01 01:42:58647 // Show captive portal blocking page. The interstitial owns the blocking page.
648 (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url,
649 std::move(ssl_cert_reporter_), ssl_info_,
650 callback_))
651 ->Show();
meacerc7693c792017-02-01 01:42:58652}
653
Sasha Perigo232e21aa2017-08-19 01:45:40654void SSLErrorHandlerDelegateImpl::ShowMITMSoftwareInterstitial(
655 const std::string& mitm_software_name,
656 bool is_enterprise_managed) {
Sasha Perigoe3a63752017-08-15 18:34:32657 // Show MITM software blocking page. The interstitial owns the blocking page.
Sasha Perigo232e21aa2017-08-19 01:45:40658 (new MITMSoftwareBlockingPage(
659 web_contents_, cert_error_, request_url_, std::move(ssl_cert_reporter_),
660 ssl_info_, mitm_software_name, is_enterprise_managed, callback_))
Sasha Perigo2bfbbe902017-08-10 21:11:03661 ->Show();
Sasha Perigo2bfbbe902017-08-10 21:11:03662}
663
meacerc7693c792017-02-01 01:42:58664void SSLErrorHandlerDelegateImpl::ShowSSLInterstitial() {
665 // Show SSL blocking page. The interstitial owns the blocking page.
estark5cbbc092017-06-23 02:00:56666 (SSLBlockingPage::Create(
667 web_contents_, cert_error_, ssl_info_, request_url_, options_mask_,
668 base::Time::NowFromSystemTime(), std::move(ssl_cert_reporter_),
669 base::FeatureList::IsEnabled(kSuperfishInterstitial) &&
670 IsSuperfish(ssl_info_.cert),
671 callback_))
meacerc7693c792017-02-01 01:42:58672 ->Show();
673}
674
675void SSLErrorHandlerDelegateImpl::ShowBadClockInterstitial(
676 const base::Time& now,
677 ssl_errors::ClockState clock_state) {
678 // Show bad clock page. The interstitial owns the blocking page.
679 (new BadClockBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_,
680 now, clock_state, std::move(ssl_cert_reporter_),
681 callback_))
682 ->Show();
683}
meacer352dc2e2017-01-10 01:27:41684
meacer4ef065e2015-01-09 03:21:35685} // namespace
686
687DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler);
meacer8b4cea22015-08-28 02:02:55688DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver);
meacer4ef065e2015-01-09 03:21:35689
meacerc7693c792017-02-01 01:42:58690static base::LazyInstance<ConfigSingleton>::Leaky g_config =
691 LAZY_INSTANCE_INITIALIZER;
692
meacer4ef065e2015-01-09 03:21:35693void SSLErrorHandler::HandleSSLError(
694 content::WebContents* web_contents,
695 int cert_error,
696 const net::SSLInfo& ssl_info,
697 const GURL& request_url,
698 int options_mask,
dcheng4af48582016-04-19 00:29:35699 std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
estark719dde52016-08-09 03:14:27700 const base::Callback<void(content::CertificateRequestResultType)>&
701 callback) {
meacer4ef065e2015-01-09 03:21:35702 DCHECK(!FromWebContents(web_contents));
meacerc7693c792017-02-01 01:42:58703
704 Profile* profile =
705 Profile::FromBrowserContext(web_contents->GetBrowserContext());
706
707 SSLErrorHandler* error_handler = new SSLErrorHandler(
708 std::unique_ptr<SSLErrorHandler::Delegate>(
709 new SSLErrorHandlerDelegateImpl(
710 web_contents, ssl_info, profile, cert_error, options_mask,
711 request_url, std::move(ssl_cert_reporter), callback)),
712 web_contents, profile, cert_error, ssl_info, request_url, callback);
avi284ec612017-05-03 01:52:23713 web_contents->SetUserData(UserDataKey(), base::WrapUnique(error_handler));
meacer4ef065e2015-01-09 03:21:35714 error_handler->StartHandlingError();
715}
716
717// static
meacerb0785802017-02-07 23:46:52718void SSLErrorHandler::ResetConfigForTesting() {
719 g_config.Pointer()->ResetForTesting();
720}
721
722// static
meacer352dc2e2017-01-10 01:27:41723void SSLErrorHandler::SetInterstitialDelayForTesting(
724 const base::TimeDelta& delay) {
725 g_config.Pointer()->SetInterstitialDelayForTesting(delay);
meacer4ef065e2015-01-09 03:21:35726}
727
728// static
meacer352dc2e2017-01-10 01:27:41729void SSLErrorHandler::SetInterstitialTimerStartedCallbackForTesting(
meacer4ef065e2015-01-09 03:21:35730 TimerStartedCallback* callback) {
meacer352dc2e2017-01-10 01:27:41731 g_config.Pointer()->SetTimerStartedCallbackForTesting(callback);
meacer4ef065e2015-01-09 03:21:35732}
733
felt534cc9e72015-09-01 20:38:59734// static
meacer352dc2e2017-01-10 01:27:41735void SSLErrorHandler::SetClockForTesting(base::Clock* testing_clock) {
736 g_config.Pointer()->SetClockForTesting(testing_clock);
felt534cc9e72015-09-01 20:38:59737}
738
estark5057f822016-11-08 21:34:54739// static
meacer352dc2e2017-01-10 01:27:41740void SSLErrorHandler::SetNetworkTimeTrackerForTesting(
estark5057f822016-11-08 21:34:54741 network_time::NetworkTimeTracker* tracker) {
meacer352dc2e2017-01-10 01:27:41742 g_config.Pointer()->SetNetworkTimeTrackerForTesting(tracker);
estark5057f822016-11-08 21:34:54743}
744
meacerc7693c792017-02-01 01:42:58745// static
Sasha Perigo232e21aa2017-08-19 01:45:40746void SSLErrorHandler::SetEnterpriseManagedForTesting(bool enterprise_managed) {
747 g_config.Pointer()->SetEnterpriseManagedForTesting(enterprise_managed);
748}
749
750// static
751bool SSLErrorHandler::IsEnterpriseManagedFlagSetForTesting() {
752 return g_config.Pointer()->IsEnterpriseManagedFlagSetForTesting();
753}
754
755// static
meacerc7693c792017-02-01 01:42:58756std::string SSLErrorHandler::GetHistogramNameForTesting() {
757 return kHistogram;
758}
759
Mustafa Acer3bc5efb2017-09-19 23:05:46760// static
761int SSLErrorHandler::GetErrorAssistantProtoVersionIdForTesting() {
762 return g_config.Pointer()->GetErrorAssistantProtoVersionIdForTesting();
763}
764
meacerc7693c792017-02-01 01:42:58765bool SSLErrorHandler::IsTimerRunningForTesting() const {
766 return timer_.IsRunning();
767}
768
meacer4404d452017-02-10 02:30:54769void SSLErrorHandler::SetErrorAssistantProto(
770 std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig> config_proto) {
meacer4404d452017-02-10 02:30:54771 g_config.Pointer()->SetErrorAssistantProto(std::move(config_proto));
meacerb0785802017-02-07 23:46:52772}
773
dcheng4af48582016-04-19 00:29:35774SSLErrorHandler::SSLErrorHandler(
meacerc7693c792017-02-01 01:42:58775 std::unique_ptr<Delegate> delegate,
dcheng4af48582016-04-19 00:29:35776 content::WebContents* web_contents,
meacerc7693c792017-02-01 01:42:58777 Profile* profile,
dcheng4af48582016-04-19 00:29:35778 int cert_error,
779 const net::SSLInfo& ssl_info,
780 const GURL& request_url,
estark719dde52016-08-09 03:14:27781 const base::Callback<void(content::CertificateRequestResultType)>& callback)
meacerd2c91b42015-03-20 18:34:40782 : content::WebContentsObserver(web_contents),
meacerc7693c792017-02-01 01:42:58783 delegate_(std::move(delegate)),
meacerd2c91b42015-03-20 18:34:40784 web_contents_(web_contents),
meacerc7693c792017-02-01 01:42:58785 profile_(profile),
meacer4ef065e2015-01-09 03:21:35786 cert_error_(cert_error),
787 ssl_info_(ssl_info),
788 request_url_(request_url),
estark93272ab2015-03-25 23:54:01789 callback_(callback),
estark5057f822016-11-08 21:34:54790 weak_ptr_factory_(this) {}
meacer4ef065e2015-01-09 03:21:35791
792SSLErrorHandler::~SSLErrorHandler() {
793}
794
795void SSLErrorHandler::StartHandlingError() {
796 RecordUMA(HANDLE_ALL);
797
estark5cbbc092017-06-23 02:00:56798 RecordSuperfishUMA(ssl_info_.cert);
estark6767d1b2017-06-03 17:37:20799
mab740b02b2016-03-17 23:44:21800 if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) ==
801 ssl_errors::ErrorInfo::CERT_DATE_INVALID) {
estark5057f822016-11-08 21:34:54802 HandleCertDateInvalidError();
803 return;
felt534cc9e72015-09-01 20:38:59804 }
805
meacer4f8acbd2017-03-02 04:23:52806 const bool only_error_is_name_mismatch =
Sasha Perigo2bfbbe902017-08-10 21:11:03807 IsOnlyCertError(net::CERT_STATUS_COMMON_NAME_INVALID);
meacer4f8acbd2017-03-02 04:23:52808
meacer4f8acbd2017-03-02 04:23:52809 // Check known captive portal certificate list if the only error is
810 // name-mismatch. If there are multiple errors, it indicates that the captive
811 // portal landing page itself will have SSL errors, and so it's not a very
812 // helpful place to direct the user to go.
813 if (base::FeatureList::IsEnabled(kCaptivePortalCertificateList) &&
814 only_error_is_name_mismatch &&
meacerb0785802017-02-07 23:46:52815 g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) {
816 RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
Mustafa Emre Acer12b5effe2017-09-26 19:26:35817 ShowCaptivePortalInterstitial(GURL());
meacerb0785802017-02-07 23:46:52818 return;
819 }
meacerb0785802017-02-07 23:46:52820
Sasha Perigo2bfbbe902017-08-10 21:11:03821 // The MITM software interstitial is displayed if and only if:
822 // - the error thrown is not overridable
823 // - the only certificate error is CERT_STATUS_AUTHORITY_INVALID
824 // - the certificate contains a string that indicates it was issued by a
825 // MITM software
826 if (IsMITMSoftwareInterstitialEnabled() && !delegate_->IsErrorOverridable() &&
Sasha Perigo232e21aa2017-08-19 01:45:40827 IsOnlyCertError(net::CERT_STATUS_AUTHORITY_INVALID)) {
828 const std::string found_mitm_software =
829 g_config.Pointer()->MatchKnownMITMSoftware(ssl_info_.cert);
830 if (!found_mitm_software.empty()) {
831 ShowMITMSoftwareInterstitial(found_mitm_software,
832 g_config.Pointer()->IsEnterpriseManaged());
833 return;
834 }
Sasha Perigo2bfbbe902017-08-10 21:11:03835 }
Sasha Perigo2bfbbe902017-08-10 21:11:03836
bhanudev4b280dc2015-08-20 20:43:00837 if (IsSSLCommonNameMismatchHandlingEnabled() &&
bhanudev3999e372015-08-21 07:23:10838 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
elawrencec7484f52017-04-05 21:46:42839 delegate_->IsErrorOverridable()) {
840 std::vector<std::string> dns_names;
841 ssl_info_.cert->GetSubjectAltName(&dns_names, nullptr);
842 GURL suggested_url;
843 if (!dns_names.empty() &&
844 delegate_->GetSuggestedUrl(dns_names, &suggested_url)) {
845 RecordUMA(WWW_MISMATCH_FOUND_IN_SAN);
bhanudev2051ce72015-08-18 22:21:18846
elawrencec7484f52017-04-05 21:46:42847 // Show the SSL interstitial if |CERT_STATUS_COMMON_NAME_INVALID| is not
848 // the only error. Need not check for captive portal in this case.
849 // (See the comment below).
850 if (!only_error_is_name_mismatch) {
851 ShowSSLInterstitial();
852 return;
853 }
854 delegate_->CheckSuggestedUrl(
855 suggested_url,
856 base::Bind(&SSLErrorHandler::CommonNameMismatchHandlerCallback,
857 weak_ptr_factory_.GetWeakPtr()));
858 timer_.Start(FROM_HERE, g_config.Pointer()->interstitial_delay(), this,
859 &SSLErrorHandler::ShowSSLInterstitial);
860
861 if (g_config.Pointer()->timer_started_callback())
862 g_config.Pointer()->timer_started_callback()->Run(web_contents_);
863
864 // Do not check for a captive portal in this case, because a captive
865 // portal most likely cannot serve a valid certificate which passes the
866 // similarity check.
bhanudev2051ce72015-08-18 22:21:18867 return;
868 }
bhanudev2051ce72015-08-18 22:21:18869 }
870
meacerc7693c792017-02-01 01:42:58871 // Always listen to captive portal notifications, otherwise build fails
872 // because profile_ isn't used. This is a no-op on platforms where
873 // captive portal detection is disabled.
874 registrar_.Add(this, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
875 content::Source<Profile>(profile_));
876
brettwab78fef2016-10-12 02:56:05877#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
bhanudev2051ce72015-08-18 22:21:18878 CaptivePortalTabHelper* captive_portal_tab_helper =
879 CaptivePortalTabHelper::FromWebContents(web_contents_);
880 if (captive_portal_tab_helper) {
881 captive_portal_tab_helper->OnSSLCertError(ssl_info_);
882 }
883
meacer5c777682015-01-09 06:54:53884 if (IsCaptivePortalInterstitialEnabled()) {
meacerc7693c792017-02-01 01:42:58885 delegate_->CheckForCaptivePortal();
meacer352dc2e2017-01-10 01:27:41886 timer_.Start(FROM_HERE, g_config.Pointer()->interstitial_delay(), this,
887 &SSLErrorHandler::ShowSSLInterstitial);
888 if (g_config.Pointer()->timer_started_callback())
889 g_config.Pointer()->timer_started_callback()->Run(web_contents_);
meacer5c777682015-01-09 06:54:53890 return;
891 }
892#endif
meacer4ef065e2015-01-09 03:21:35893 // Display an SSL interstitial.
894 ShowSSLInterstitial();
meacer4ef065e2015-01-09 03:21:35895}
896
meacerca64d98192015-01-23 21:34:30897void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) {
meacer4ef065e2015-01-09 03:21:35898 // Show captive portal blocking page. The interstitial owns the blocking page.
meacerc7693c792017-02-01 01:42:58899 RecordUMA(delegate_->IsErrorOverridable()
fahle66e7ff2015-04-23 19:38:30900 ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE
901 : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE);
meacerc7693c792017-02-01 01:42:58902 delegate_->ShowCaptivePortalInterstitial(landing_url);
Mustafa Emre Acer12b5effe2017-09-26 19:26:35903
meacer4ef065e2015-01-09 03:21:35904 // Once an interstitial is displayed, no need to keep the handler around.
meacer8b4cea22015-08-28 02:02:55905 // This is the equivalent of "delete this". It also destroys the timer.
meacer4ef065e2015-01-09 03:21:35906 web_contents_->RemoveUserData(UserDataKey());
meacer4ef065e2015-01-09 03:21:35907}
908
Sasha Perigo232e21aa2017-08-19 01:45:40909void SSLErrorHandler::ShowMITMSoftwareInterstitial(
910 const std::string& mitm_software_name,
911 bool is_enterprise_managed) {
Sasha Perigo2bfbbe902017-08-10 21:11:03912 // Show SSL blocking page. The interstitial owns the blocking page.
913 RecordUMA(SHOW_MITM_SOFTWARE_INTERSTITIAL);
Sasha Perigo232e21aa2017-08-19 01:45:40914 delegate_->ShowMITMSoftwareInterstitial(mitm_software_name,
915 is_enterprise_managed);
Sasha Perigo2bfbbe902017-08-10 21:11:03916 // Once an interstitial is displayed, no need to keep the handler around.
917 // This is the equivalent of "delete this".
918 web_contents_->RemoveUserData(UserDataKey());
Sasha Perigo2bfbbe902017-08-10 21:11:03919}
920
meacer4ef065e2015-01-09 03:21:35921void SSLErrorHandler::ShowSSLInterstitial() {
922 // Show SSL blocking page. The interstitial owns the blocking page.
meacerc7693c792017-02-01 01:42:58923 RecordUMA(delegate_->IsErrorOverridable()
924 ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE
925 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE);
926 delegate_->ShowSSLInterstitial();
meacer4ef065e2015-01-09 03:21:35927 // Once an interstitial is displayed, no need to keep the handler around.
928 // This is the equivalent of "delete this".
929 web_contents_->RemoveUserData(UserDataKey());
930}
931
mab740b02b2016-03-17 23:44:21932void SSLErrorHandler::ShowBadClockInterstitial(
933 const base::Time& now,
934 ssl_errors::ClockState clock_state) {
felt534cc9e72015-09-01 20:38:59935 RecordUMA(SHOW_BAD_CLOCK);
meacerc7693c792017-02-01 01:42:58936 delegate_->ShowBadClockInterstitial(now, clock_state);
felt534cc9e72015-09-01 20:38:59937 // Once an interstitial is displayed, no need to keep the handler around.
938 // This is the equivalent of "delete this".
939 web_contents_->RemoveUserData(UserDataKey());
940}
941
meacer8b4cea22015-08-28 02:02:55942void SSLErrorHandler::CommonNameMismatchHandlerCallback(
meacerc7693c792017-02-01 01:42:58943 CommonNameMismatchHandler::SuggestedUrlCheckResult result,
meacer8b4cea22015-08-28 02:02:55944 const GURL& suggested_url) {
945 timer_.Stop();
946 if (result == CommonNameMismatchHandler::SuggestedUrlCheckResult::
947 SUGGESTED_URL_AVAILABLE) {
948 RecordUMA(WWW_MISMATCH_URL_AVAILABLE);
949 CommonNameMismatchRedirectObserver::AddToConsoleAfterNavigation(
950 web_contents(), request_url_.host(), suggested_url.host());
meacerc7693c792017-02-01 01:42:58951 delegate_->NavigateToSuggestedURL(suggested_url);
meacer8b4cea22015-08-28 02:02:55952 } else {
953 RecordUMA(WWW_MISMATCH_URL_NOT_AVAILABLE);
954 ShowSSLInterstitial();
955 }
956}
957
meacer4ef065e2015-01-09 03:21:35958void SSLErrorHandler::Observe(
959 int type,
960 const content::NotificationSource& source,
961 const content::NotificationDetails& details) {
brettwab78fef2016-10-12 02:56:05962#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
thestig52835792016-06-13 21:08:09963 DCHECK_EQ(chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, type);
964
965 timer_.Stop();
966 CaptivePortalService::Results* results =
967 content::Details<CaptivePortalService::Results>(details).ptr();
968 if (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL)
969 ShowCaptivePortalInterstitial(results->landing_url);
970 else
971 ShowSSLInterstitial();
meacerc7693c792017-02-01 01:42:58972#else
973 NOTREACHED();
meacer4ef065e2015-01-09 03:21:35974#endif
975}
meacerd2c91b42015-03-20 18:34:40976
jam6625f122017-01-20 06:37:33977void SSLErrorHandler::DidStartNavigation(
978 content::NavigationHandle* navigation_handle) {
eugenebuta11672fb2017-03-07 17:13:51979 if (!navigation_handle->IsInMainFrame() ||
980 navigation_handle->IsSameDocument()) {
jam6625f122017-01-20 06:37:33981 return;
eugenebuta11672fb2017-03-07 17:13:51982 }
jam6625f122017-01-20 06:37:33983
toyoshim0df1d3a2016-09-09 09:52:48984 // Destroy the error handler on all new navigations. This ensures that the
985 // handler is properly recreated when a hanging page is navigated to an SSL
986 // error, even when the tab's WebContents doesn't change.
bhanudev85c5b8b52015-07-31 02:00:37987 DeleteSSLErrorHandler();
988}
989
990void SSLErrorHandler::NavigationStopped() {
991// Destroy the error handler when the page load is stopped.
992 DeleteSSLErrorHandler();
993}
994
995void SSLErrorHandler::DeleteSSLErrorHandler() {
meacerd2c91b42015-03-20 18:34:40996 // Need to explicity deny the certificate via the callback, otherwise memory
997 // is leaked.
998 if (!callback_.is_null()) {
estark719dde52016-08-09 03:14:27999 base::ResetAndReturn(&callback_)
1000 .Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
meacerd2c91b42015-03-20 18:34:401001 }
meacerc7693c792017-02-01 01:42:581002 delegate_.reset();
meacer8b4cea22015-08-28 02:02:551003 // Deletes |this| and also destroys the timer.
meacerd2c91b42015-03-20 18:34:401004 web_contents_->RemoveUserData(UserDataKey());
1005}
estark5057f822016-11-08 21:34:541006
1007void SSLErrorHandler::HandleCertDateInvalidError() {
estark8d6c3c72016-12-14 03:55:221008 const base::TimeTicks now = base::TimeTicks::Now();
meacer352dc2e2017-01-10 01:27:411009 timer_.Start(FROM_HERE, g_config.Pointer()->interstitial_delay(),
estark5057f822016-11-08 21:34:541010 base::Bind(&SSLErrorHandler::HandleCertDateInvalidErrorImpl,
estark8d6c3c72016-12-14 03:55:221011 base::Unretained(this), now));
estark5057f822016-11-08 21:34:541012 // Try kicking off a time fetch to get an up-to-date estimate of the
1013 // true time. This will only have an effect if network time is
1014 // unavailable or if there is not already a query in progress.
1015 //
1016 // Pass a weak pointer as the callback; if the timer fires before the
1017 // fetch completes and shows an interstitial, this SSLErrorHandler
1018 // will be deleted.
meacer352dc2e2017-01-10 01:27:411019 network_time::NetworkTimeTracker* tracker =
1020 g_config.Pointer()->network_time_tracker();
estark5057f822016-11-08 21:34:541021 if (!tracker->StartTimeFetch(
1022 base::Bind(&SSLErrorHandler::HandleCertDateInvalidErrorImpl,
estark8d6c3c72016-12-14 03:55:221023 weak_ptr_factory_.GetWeakPtr(), now))) {
1024 HandleCertDateInvalidErrorImpl(now);
estark5057f822016-11-08 21:34:541025 return;
1026 }
1027
meacer352dc2e2017-01-10 01:27:411028 if (g_config.Pointer()->timer_started_callback())
1029 g_config.Pointer()->timer_started_callback()->Run(web_contents_);
estark5057f822016-11-08 21:34:541030}
1031
estark8d6c3c72016-12-14 03:55:221032void SSLErrorHandler::HandleCertDateInvalidErrorImpl(
1033 base::TimeTicks started_handling_error) {
1034 UMA_HISTOGRAM_CUSTOM_TIMES(
1035 "interstitial.ssl_error_handler.cert_date_error_delay",
1036 base::TimeTicks::Now() - started_handling_error,
1037 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(4),
1038 50);
1039
estark5057f822016-11-08 21:34:541040 timer_.Stop();
meacer352dc2e2017-01-10 01:27:411041 base::Clock* testing_clock = g_config.Pointer()->clock();
1042 const base::Time now =
1043 testing_clock ? testing_clock->Now() : base::Time::NowFromSystemTime();
1044
1045 network_time::NetworkTimeTracker* tracker =
1046 g_config.Pointer()->network_time_tracker();
estark5057f822016-11-08 21:34:541047 ssl_errors::ClockState clock_state = ssl_errors::GetClockState(now, tracker);
1048 if (clock_state == ssl_errors::CLOCK_STATE_FUTURE ||
1049 clock_state == ssl_errors::CLOCK_STATE_PAST) {
1050 ShowBadClockInterstitial(now, clock_state);
1051 return; // |this| is deleted after showing the interstitial.
1052 }
1053 ShowSSLInterstitial();
1054}
Sasha Perigo2bfbbe902017-08-10 21:11:031055
1056// Returns true if |only_cert_error_expected| is the only error code present in
1057// the certificate. The parameter |only_cert_error_expected| is a
1058// net::CertStatus code representing the most serious error identified on the
1059// certificate. For example, this could be net::CERT_STATUS_COMMON_NAME_INVALID.
1060// This function is useful for rendering interstitials that are triggered by one
1061// specific error code only.
1062bool SSLErrorHandler::IsOnlyCertError(
1063 net::CertStatus only_cert_error_expected) const {
1064 const net::CertStatus other_errors =
1065 ssl_info_.cert_status ^ only_cert_error_expected;
1066
1067 return cert_error_ ==
1068 net::MapCertStatusToNetError(only_cert_error_expected) &&
1069 (!net::IsCertStatusError(other_errors) ||
1070 net::IsCertStatusMinorError(ssl_info_.cert_status));
1071}