Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / ssl / ssl_error_handler.cc
blob91c451082b005b9eddb9d4cd6fb4a41928f16fc2
1 // 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.
5 #include "chrome/browser/ssl/ssl_error_handler.h"
7 #include "base/callback_helpers.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/time/clock.h"
12 #include "base/time/time.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
15 #include "chrome/browser/ssl/ssl_blocking_page.h"
16 #include "chrome/browser/ssl/ssl_cert_reporter.h"
17 #include "chrome/browser/ssl/ssl_error_classification.h"
18 #include "chrome/browser/ssl/ssl_error_info.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/render_frame_host.h"
22 #include "content/public/browser/web_contents.h"
23 #include "net/base/net_errors.h"
25 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
26 #include "chrome/browser/captive_portal/captive_portal_service.h"
27 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
28 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
29 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
30 #endif
32 namespace {
34 // The delay in milliseconds before displaying the SSL interstitial.
35 // This can be changed in tests.
36 // - If there is a name mismatch and a suggested URL available result arrives
37 // during this time, the user is redirected to the suggester URL.
38 // - If a "captive portal detected" result arrives during this time,
39 // a captive portal interstitial is displayed.
40 // - Otherwise, an SSL interstitial is displayed.
41 int64 g_interstitial_delay_in_milliseconds = 2000;
43 // Callback to call when the interstitial timer is started. Used for testing.
44 SSLErrorHandler::TimerStartedCallback* g_timer_started_callback = nullptr;
46 // The clock to use when deciding which error type to display. Used for testing.
47 base::Clock* g_testing_clock = nullptr;
49 // Events for UMA.
50 enum SSLErrorHandlerEvent {
51 HANDLE_ALL,
52 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE,
53 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE,
54 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE,
55 SHOW_SSL_INTERSTITIAL_OVERRIDABLE,
56 WWW_MISMATCH_FOUND,
57 WWW_MISMATCH_URL_AVAILABLE,
58 WWW_MISMATCH_URL_NOT_AVAILABLE,
59 SHOW_BAD_CLOCK,
60 SSL_ERROR_HANDLER_EVENT_COUNT
63 // Adds a message to console after navigation commits and then, deletes itself.
64 // Also deletes itself if the navigation is stopped.
65 class CommonNameMismatchRedirectObserver
66 : public content::WebContentsObserver,
67 public content::WebContentsUserData<CommonNameMismatchRedirectObserver> {
68 public:
69 static void AddToConsoleAfterNavigation(
70 content::WebContents* web_contents,
71 const std::string& request_url_hostname,
72 const std::string& suggested_url_hostname) {
73 web_contents->SetUserData(
74 UserDataKey(),
75 new CommonNameMismatchRedirectObserver(
76 web_contents, request_url_hostname, suggested_url_hostname));
79 private:
80 CommonNameMismatchRedirectObserver(content::WebContents* web_contents,
81 const std::string& request_url_hostname,
82 const std::string& suggested_url_hostname)
83 : WebContentsObserver(web_contents),
84 web_contents_(web_contents),
85 request_url_hostname_(request_url_hostname),
86 suggested_url_hostname_(suggested_url_hostname) {}
87 ~CommonNameMismatchRedirectObserver() override {}
89 // WebContentsObserver:
90 void NavigationStopped() override {
91 // Deletes |this|.
92 web_contents_->RemoveUserData(UserDataKey());
95 void NavigationEntryCommitted(
96 const content::LoadCommittedDetails& /* load_details */) override {
97 web_contents_->GetMainFrame()->AddMessageToConsole(
98 content::CONSOLE_MESSAGE_LEVEL_LOG,
99 base::StringPrintf(
100 "Redirecting navigation %s -> %s because the server presented a "
101 "certificate valid for %s but not for %s.",
102 request_url_hostname_.c_str(), suggested_url_hostname_.c_str(),
103 suggested_url_hostname_.c_str(), request_url_hostname_.c_str()));
104 web_contents_->RemoveUserData(UserDataKey());
107 void WebContentsDestroyed() override {
108 web_contents_->RemoveUserData(UserDataKey());
111 content::WebContents* web_contents_;
112 const std::string request_url_hostname_;
113 const std::string suggested_url_hostname_;
115 DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchRedirectObserver);
118 void RecordUMA(SSLErrorHandlerEvent event) {
119 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler", event,
120 SSL_ERROR_HANDLER_EVENT_COUNT);
123 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
124 bool IsCaptivePortalInterstitialEnabled() {
125 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") ==
126 "Enabled";
128 #endif
130 bool IsSSLCommonNameMismatchHandlingEnabled() {
131 return base::FieldTrialList::FindFullName("SSLCommonNameMismatchHandling") ==
132 "Enabled";
135 bool IsErrorDueToBadClock(const base::Time& now, int error) {
136 if (SSLErrorInfo::NetErrorToErrorType(error) !=
137 SSLErrorInfo::CERT_DATE_INVALID) {
138 return false;
140 return SSLErrorClassification::IsUserClockInThePast(now) ||
141 SSLErrorClassification::IsUserClockInTheFuture(now);
144 } // namespace
146 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler);
147 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CommonNameMismatchRedirectObserver);
149 void SSLErrorHandler::HandleSSLError(
150 content::WebContents* web_contents,
151 int cert_error,
152 const net::SSLInfo& ssl_info,
153 const GURL& request_url,
154 int options_mask,
155 scoped_ptr<SSLCertReporter> ssl_cert_reporter,
156 const base::Callback<void(bool)>& callback) {
157 DCHECK(!FromWebContents(web_contents));
158 SSLErrorHandler* error_handler =
159 new SSLErrorHandler(web_contents, cert_error, ssl_info, request_url,
160 options_mask, ssl_cert_reporter.Pass(), callback);
161 web_contents->SetUserData(UserDataKey(), error_handler);
162 error_handler->StartHandlingError();
165 // static
166 void SSLErrorHandler::SetInterstitialDelayForTest(base::TimeDelta delay) {
167 g_interstitial_delay_in_milliseconds = delay.InMilliseconds();
170 // static
171 void SSLErrorHandler::SetInterstitialTimerStartedCallbackForTest(
172 TimerStartedCallback* callback) {
173 DCHECK(!callback || !callback->is_null());
174 g_timer_started_callback = callback;
177 // static
178 void SSLErrorHandler::SetClockForTest(base::Clock* testing_clock) {
179 g_testing_clock = testing_clock;
182 SSLErrorHandler::SSLErrorHandler(content::WebContents* web_contents,
183 int cert_error,
184 const net::SSLInfo& ssl_info,
185 const GURL& request_url,
186 int options_mask,
187 scoped_ptr<SSLCertReporter> ssl_cert_reporter,
188 const base::Callback<void(bool)>& callback)
189 : content::WebContentsObserver(web_contents),
190 web_contents_(web_contents),
191 cert_error_(cert_error),
192 ssl_info_(ssl_info),
193 request_url_(request_url),
194 options_mask_(options_mask),
195 callback_(callback),
196 profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
197 ssl_cert_reporter_(ssl_cert_reporter.Pass()) {}
199 SSLErrorHandler::~SSLErrorHandler() {
202 void SSLErrorHandler::StartHandlingError() {
203 RecordUMA(HANDLE_ALL);
205 const base::Time now = g_testing_clock == nullptr
206 ? base::Time::NowFromSystemTime()
207 : g_testing_clock->Now();
208 if (IsErrorDueToBadClock(now, cert_error_)) {
209 ShowBadClockInterstitial(now);
210 return; // |this| is deleted after showing the interstitial.
213 std::vector<std::string> dns_names;
214 ssl_info_.cert->GetDNSNames(&dns_names);
215 DCHECK(!dns_names.empty());
216 GURL suggested_url;
217 if (IsSSLCommonNameMismatchHandlingEnabled() &&
218 cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
219 IsErrorOverridable() && GetSuggestedUrl(dns_names, &suggested_url)) {
220 RecordUMA(WWW_MISMATCH_FOUND);
221 net::CertStatus extra_cert_errors =
222 ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
224 // Show the SSL intersitial if |CERT_STATUS_COMMON_NAME_INVALID| is not
225 // the only error. Need not check for captive portal in this case.
226 // (See the comment below).
227 if (net::IsCertStatusError(extra_cert_errors) &&
228 !net::IsCertStatusMinorError(ssl_info_.cert_status)) {
229 ShowSSLInterstitial();
230 return;
232 CheckSuggestedUrl(suggested_url);
233 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
234 g_interstitial_delay_in_milliseconds),
235 this, &SSLErrorHandler::ShowSSLInterstitial);
236 if (g_timer_started_callback)
237 g_timer_started_callback->Run(web_contents_);
239 // Do not check for a captive portal in this case, because a captive
240 // portal most likely cannot serve a valid certificate which passes the
241 // similarity check.
242 return;
245 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
246 CaptivePortalTabHelper* captive_portal_tab_helper =
247 CaptivePortalTabHelper::FromWebContents(web_contents_);
248 if (captive_portal_tab_helper) {
249 captive_portal_tab_helper->OnSSLCertError(ssl_info_);
252 registrar_.Add(this, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
253 content::Source<Profile>(profile_));
255 if (IsCaptivePortalInterstitialEnabled()) {
256 CheckForCaptivePortal();
257 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
258 g_interstitial_delay_in_milliseconds),
259 this, &SSLErrorHandler::ShowSSLInterstitial);
260 if (g_timer_started_callback)
261 g_timer_started_callback->Run(web_contents_);
262 return;
264 #endif
265 // Display an SSL interstitial.
266 ShowSSLInterstitial();
269 void SSLErrorHandler::CheckForCaptivePortal() {
270 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
271 CaptivePortalService* captive_portal_service =
272 CaptivePortalServiceFactory::GetForProfile(profile_);
273 captive_portal_service->DetectCaptivePortal();
274 #else
275 NOTREACHED();
276 #endif
279 bool SSLErrorHandler::GetSuggestedUrl(const std::vector<std::string>& dns_names,
280 GURL* suggested_url) const {
281 return CommonNameMismatchHandler::GetSuggestedUrl(request_url_, dns_names,
282 suggested_url);
285 void SSLErrorHandler::CheckSuggestedUrl(const GURL& suggested_url) {
286 scoped_refptr<net::URLRequestContextGetter> request_context(
287 profile_->GetRequestContext());
288 common_name_mismatch_handler_.reset(
289 new CommonNameMismatchHandler(request_url_, request_context));
291 common_name_mismatch_handler_->CheckSuggestedUrl(
292 suggested_url,
293 base::Bind(&SSLErrorHandler::CommonNameMismatchHandlerCallback,
294 base::Unretained(this)));
297 void SSLErrorHandler::NavigateToSuggestedURL(const GURL& suggested_url) {
298 content::NavigationController::LoadURLParams load_params(suggested_url);
299 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
300 web_contents()->GetController().LoadURLWithParams(load_params);
303 bool SSLErrorHandler::IsErrorOverridable() const {
304 return SSLBlockingPage::IsOverridable(options_mask_, profile_);
307 void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL& landing_url) {
308 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
309 // Show captive portal blocking page. The interstitial owns the blocking page.
310 RecordUMA(IsErrorOverridable()
311 ? SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE
312 : SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE);
313 (new CaptivePortalBlockingPage(web_contents_, request_url_, landing_url,
314 ssl_cert_reporter_.Pass(), ssl_info_,
315 callback_))->Show();
316 // Once an interstitial is displayed, no need to keep the handler around.
317 // This is the equivalent of "delete this". It also destroys the timer.
318 web_contents_->RemoveUserData(UserDataKey());
319 #else
320 NOTREACHED();
321 #endif
324 void SSLErrorHandler::ShowSSLInterstitial() {
325 // Show SSL blocking page. The interstitial owns the blocking page.
326 RecordUMA(IsErrorOverridable() ? SHOW_SSL_INTERSTITIAL_OVERRIDABLE
327 : SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE);
329 (new SSLBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_,
330 options_mask_, base::Time::NowFromSystemTime(),
331 ssl_cert_reporter_.Pass(), callback_))
332 ->Show();
333 // Once an interstitial is displayed, no need to keep the handler around.
334 // This is the equivalent of "delete this".
335 web_contents_->RemoveUserData(UserDataKey());
338 void SSLErrorHandler::ShowBadClockInterstitial(const base::Time& now) {
339 RecordUMA(SHOW_BAD_CLOCK);
340 (new BadClockBlockingPage(web_contents_, cert_error_, ssl_info_, request_url_,
341 now, callback_))
342 ->Show();
343 // Once an interstitial is displayed, no need to keep the handler around.
344 // This is the equivalent of "delete this".
345 web_contents_->RemoveUserData(UserDataKey());
348 void SSLErrorHandler::CommonNameMismatchHandlerCallback(
349 const CommonNameMismatchHandler::SuggestedUrlCheckResult& result,
350 const GURL& suggested_url) {
351 timer_.Stop();
352 if (result == CommonNameMismatchHandler::SuggestedUrlCheckResult::
353 SUGGESTED_URL_AVAILABLE) {
354 RecordUMA(WWW_MISMATCH_URL_AVAILABLE);
355 CommonNameMismatchRedirectObserver::AddToConsoleAfterNavigation(
356 web_contents(), request_url_.host(), suggested_url.host());
357 NavigateToSuggestedURL(suggested_url);
358 } else {
359 RecordUMA(WWW_MISMATCH_URL_NOT_AVAILABLE);
360 ShowSSLInterstitial();
364 void SSLErrorHandler::Observe(
365 int type,
366 const content::NotificationSource& source,
367 const content::NotificationDetails& details) {
368 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
369 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) {
370 timer_.Stop();
371 CaptivePortalService::Results* results =
372 content::Details<CaptivePortalService::Results>(details).ptr();
373 if (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL)
374 ShowCaptivePortalInterstitial(results->landing_url);
375 else
376 ShowSSLInterstitial();
378 #endif
381 void SSLErrorHandler::DidStartNavigationToPendingEntry(
382 const GURL& /* url */,
383 content::NavigationController::ReloadType /* reload_type */) {
384 // Destroy the error handler on all new navigations. This ensures that the
385 // handler is properly recreated when a hanging page is navigated to an SSL
386 // error, even when the tab's WebContents doesn't change.
387 DeleteSSLErrorHandler();
390 void SSLErrorHandler::NavigationStopped() {
391 // Destroy the error handler when the page load is stopped.
392 DeleteSSLErrorHandler();
395 void SSLErrorHandler::DeleteSSLErrorHandler() {
396 // Need to explicity deny the certificate via the callback, otherwise memory
397 // is leaked.
398 if (!callback_.is_null()) {
399 base::ResetAndReturn(&callback_).Run(false);
401 if (common_name_mismatch_handler_) {
402 common_name_mismatch_handler_->Cancel();
403 common_name_mismatch_handler_.reset();
405 // Deletes |this| and also destroys the timer.
406 web_contents_->RemoveUserData(UserDataKey());