Log captive portal status when an SSL interstitial is displayed.
[chromium-blink-merge.git] / chrome / browser / ssl / ssl_blocking_page.cc
blobfcab016e74494698a84148b99f4176deffa45c4e
1 // Copyright (c) 2012 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_blocking_page.h"
7 #include "base/i18n/rtl.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/history/history_service_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/renderer_preferences_util.h"
18 #include "chrome/browser/ssl/ssl_error_info.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_finder.h"
21 #include "content/public/browser/cert_store.h"
22 #include "content/public/browser/interstitial_page.h"
23 #include "content/public/browser/navigation_controller.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/ssl_status.h"
31 #include "grit/app_locale_settings.h"
32 #include "grit/browser_resources.h"
33 #include "grit/generated_resources.h"
34 #include "net/base/hash_value.h"
35 #include "net/base/net_errors.h"
36 #include "net/base/net_util.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/base/webui/jstemplate_builder.h"
41 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
42 #include "chrome/browser/captive_portal/captive_portal_service.h"
43 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
44 #endif
46 #if defined(OS_WIN)
47 #include "base/win/windows_version.h"
48 #endif
50 using base::ASCIIToUTF16;
51 using base::TimeTicks;
52 using content::InterstitialPage;
53 using content::NavigationController;
54 using content::NavigationEntry;
56 namespace {
58 // These represent the commands sent by ssl_roadblock.html.
59 enum SSLBlockingPageCommands {
60 CMD_DONT_PROCEED,
61 CMD_PROCEED,
62 CMD_MORE,
63 CMD_RELOAD
66 // Events for UMA.
67 enum SSLBlockingPageEvent {
68 SHOW_ALL,
69 SHOW_OVERRIDABLE,
70 PROCEED_OVERRIDABLE,
71 PROCEED_NAME,
72 PROCEED_DATE,
73 PROCEED_AUTHORITY,
74 DONT_PROCEED_OVERRIDABLE,
75 DONT_PROCEED_NAME,
76 DONT_PROCEED_DATE,
77 DONT_PROCEED_AUTHORITY,
78 MORE,
79 SHOW_UNDERSTAND, // Used by the summer 2013 Finch trial. Deprecated.
80 SHOW_INTERNAL_HOSTNAME,
81 PROCEED_INTERNAL_HOSTNAME,
82 SHOW_NEW_SITE,
83 PROCEED_NEW_SITE,
84 PROCEED_MANUAL_NONOVERRIDABLE,
85 CAPTIVE_PORTAL_DETECTION_ENABLED,
86 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE,
87 CAPTIVE_PORTAL_PROBE_COMPLETED,
88 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE,
89 CAPTIVE_PORTAL_NO_RESPONSE,
90 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE,
91 CAPTIVE_PORTAL_DETECTED,
92 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE,
93 UNUSED_BLOCKING_PAGE_EVENT,
96 void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) {
97 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl",
98 event,
99 UNUSED_BLOCKING_PAGE_EVENT);
102 void RecordSSLBlockingPageDetailedStats(
103 bool proceed,
104 int cert_error,
105 bool overridable,
106 bool internal,
107 int num_visits,
108 bool captive_portal_detection_enabled,
109 bool captive_portal_probe_completed,
110 bool captive_portal_no_response,
111 bool captive_portal_detected) {
112 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type",
113 SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM);
114 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
115 if (captive_portal_detection_enabled)
116 RecordSSLBlockingPageEventStats(
117 overridable ?
118 CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE :
119 CAPTIVE_PORTAL_DETECTION_ENABLED);
120 if (captive_portal_probe_completed)
121 RecordSSLBlockingPageEventStats(
122 overridable ?
123 CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE :
124 CAPTIVE_PORTAL_PROBE_COMPLETED);
125 // Log only one of portal detected and no response results.
126 if (captive_portal_detected)
127 RecordSSLBlockingPageEventStats(
128 overridable ?
129 CAPTIVE_PORTAL_DETECTED_OVERRIDABLE :
130 CAPTIVE_PORTAL_DETECTED);
131 else if (captive_portal_no_response)
132 RecordSSLBlockingPageEventStats(
133 overridable ?
134 CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE :
135 CAPTIVE_PORTAL_NO_RESPONSE);
136 #endif
137 if (!overridable) {
138 if (proceed) {
139 RecordSSLBlockingPageEventStats(PROCEED_MANUAL_NONOVERRIDABLE);
141 // Overridable is false if the user didn't have any option except to turn
142 // back. If that's the case, don't record some of the metrics.
143 return;
145 if (num_visits == 0)
146 RecordSSLBlockingPageEventStats(SHOW_NEW_SITE);
147 if (proceed) {
148 RecordSSLBlockingPageEventStats(PROCEED_OVERRIDABLE);
149 if (internal)
150 RecordSSLBlockingPageEventStats(PROCEED_INTERNAL_HOSTNAME);
151 if (num_visits == 0)
152 RecordSSLBlockingPageEventStats(PROCEED_NEW_SITE);
153 } else if (!proceed) {
154 RecordSSLBlockingPageEventStats(DONT_PROCEED_OVERRIDABLE);
156 SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error);
157 switch (type) {
158 case SSLErrorInfo::CERT_COMMON_NAME_INVALID: {
159 if (proceed)
160 RecordSSLBlockingPageEventStats(PROCEED_NAME);
161 else
162 RecordSSLBlockingPageEventStats(DONT_PROCEED_NAME);
163 break;
165 case SSLErrorInfo::CERT_DATE_INVALID: {
166 if (proceed)
167 RecordSSLBlockingPageEventStats(PROCEED_DATE);
168 else
169 RecordSSLBlockingPageEventStats(DONT_PROCEED_DATE);
170 break;
172 case SSLErrorInfo::CERT_AUTHORITY_INVALID: {
173 if (proceed)
174 RecordSSLBlockingPageEventStats(PROCEED_AUTHORITY);
175 else
176 RecordSSLBlockingPageEventStats(DONT_PROCEED_AUTHORITY);
177 break;
179 default: {
180 break;
185 } // namespace
187 // Note that we always create a navigation entry with SSL errors.
188 // No error happening loading a sub-resource triggers an interstitial so far.
189 SSLBlockingPage::SSLBlockingPage(
190 content::WebContents* web_contents,
191 int cert_error,
192 const net::SSLInfo& ssl_info,
193 const GURL& request_url,
194 bool overridable,
195 bool strict_enforcement,
196 const base::Callback<void(bool)>& callback)
197 : callback_(callback),
198 web_contents_(web_contents),
199 cert_error_(cert_error),
200 ssl_info_(ssl_info),
201 request_url_(request_url),
202 overridable_(overridable),
203 strict_enforcement_(strict_enforcement),
204 internal_(false),
205 num_visits_(-1),
206 captive_portal_detection_enabled_(false),
207 captive_portal_probe_completed_(false),
208 captive_portal_no_response_(false),
209 captive_portal_detected_(false) {
210 Profile* profile = Profile::FromBrowserContext(
211 web_contents->GetBrowserContext());
212 // For UMA stats.
213 if (net::IsHostnameNonUnique(request_url_.HostNoBrackets()))
214 internal_ = true;
215 RecordSSLBlockingPageEventStats(SHOW_ALL);
216 if (overridable_ && !strict_enforcement_) {
217 RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE);
218 if (internal_)
219 RecordSSLBlockingPageEventStats(SHOW_INTERNAL_HOSTNAME);
220 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
221 profile, Profile::EXPLICIT_ACCESS);
222 if (history_service) {
223 history_service->GetVisibleVisitCountToHost(
224 request_url_,
225 &request_consumer_,
226 base::Bind(&SSLBlockingPage::OnGotHistoryCount,
227 base::Unretained(this)));
231 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
232 captive_portal::CaptivePortalService* captive_portal_service =
233 captive_portal::CaptivePortalServiceFactory::GetForProfile(profile);
234 captive_portal_detection_enabled_ = captive_portal_service ->enabled();
235 captive_portal_service ->DetectCaptivePortal();
236 registrar_.Add(this,
237 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
238 content::Source<Profile>(profile));
239 #endif
241 interstitial_page_ = InterstitialPage::Create(
242 web_contents_, true, request_url, this);
243 interstitial_page_->Show();
246 SSLBlockingPage::~SSLBlockingPage() {
247 if (!callback_.is_null()) {
248 RecordSSLBlockingPageDetailedStats(false,
249 cert_error_,
250 overridable_ && !strict_enforcement_,
251 internal_,
252 num_visits_,
253 captive_portal_detection_enabled_,
254 captive_portal_probe_completed_,
255 captive_portal_no_response_,
256 captive_portal_detected_);
257 // The page is closed without the user having chosen what to do, default to
258 // deny.
259 NotifyDenyCertificate();
263 std::string SSLBlockingPage::GetHTMLContents() {
264 base::DictionaryValue strings;
265 int resource_id;
266 if (overridable_ && !strict_enforcement_) {
267 // Let's build the overridable error page.
268 SSLErrorInfo error_info =
269 SSLErrorInfo::CreateError(
270 SSLErrorInfo::NetErrorToErrorType(cert_error_),
271 ssl_info_.cert.get(),
272 request_url_);
274 resource_id = IDR_SSL_ROAD_BLOCK_HTML;
275 strings.SetString("headLine", error_info.title());
276 strings.SetString("description", error_info.details());
277 strings.SetString("moreInfoTitle",
278 l10n_util::GetStringUTF16(IDS_CERT_ERROR_EXTRA_INFO_TITLE));
279 SetExtraInfo(&strings, error_info.extra_information());
281 strings.SetString(
282 "exit", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_EXIT));
283 strings.SetString(
284 "title", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_TITLE));
285 strings.SetString(
286 "proceed", l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_PAGE_PROCEED));
287 strings.SetString(
288 "reasonForNotProceeding", l10n_util::GetStringUTF16(
289 IDS_SSL_OVERRIDABLE_PAGE_SHOULD_NOT_PROCEED));
290 strings.SetString("errorType", "overridable");
291 strings.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
292 } else {
293 // Let's build the blocking error page.
294 resource_id = IDR_SSL_BLOCKING_HTML;
296 // Strings that are not dependent on the URL.
297 strings.SetString(
298 "title", l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TITLE));
299 strings.SetString(
300 "reloadMsg", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_RELOAD));
301 strings.SetString(
302 "more", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_MORE));
303 strings.SetString(
304 "less", l10n_util::GetStringUTF16(IDS_ERRORPAGES_BUTTON_LESS));
305 strings.SetString(
306 "moreTitle",
307 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TITLE));
308 strings.SetString(
309 "techTitle",
310 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_TECH_TITLE));
312 // Strings that are dependent on the URL.
313 base::string16 url(ASCIIToUTF16(request_url_.host()));
314 bool rtl = base::i18n::IsRTL();
315 strings.SetString("textDirection", rtl ? "rtl" : "ltr");
316 if (rtl)
317 base::i18n::WrapStringWithLTRFormatting(&url);
318 strings.SetString(
319 "headline", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HEADLINE,
320 url.c_str()));
321 strings.SetString(
322 "message", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_BODY_TEXT,
323 url.c_str()));
324 strings.SetString(
325 "moreMessage",
326 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_MORE_TEXT,
327 url.c_str()));
328 strings.SetString("reloadUrl", request_url_.spec());
330 // Strings that are dependent on the error type.
331 SSLErrorInfo::ErrorType type =
332 SSLErrorInfo::NetErrorToErrorType(cert_error_);
333 base::string16 errorType;
334 if (type == SSLErrorInfo::CERT_REVOKED) {
335 errorType = base::string16(ASCIIToUTF16("Key revocation"));
336 strings.SetString(
337 "failure",
338 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_REVOKED));
339 } else if (type == SSLErrorInfo::CERT_INVALID) {
340 errorType = base::string16(ASCIIToUTF16("Malformed certificate"));
341 strings.SetString(
342 "failure",
343 l10n_util::GetStringUTF16(IDS_SSL_BLOCKING_PAGE_FORMATTED));
344 } else if (type == SSLErrorInfo::CERT_PINNED_KEY_MISSING) {
345 errorType = base::string16(ASCIIToUTF16("Certificate pinning failure"));
346 strings.SetString(
347 "failure",
348 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_PINNING,
349 url.c_str()));
350 } else if (type == SSLErrorInfo::CERT_WEAK_KEY_DH) {
351 errorType = base::string16(ASCIIToUTF16("Weak DH public key"));
352 strings.SetString(
353 "failure",
354 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_WEAK_DH,
355 url.c_str()));
356 } else {
357 // HSTS failure.
358 errorType = base::string16(ASCIIToUTF16("HSTS failure"));
359 strings.SetString(
360 "failure",
361 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HSTS, url.c_str()));
363 if (rtl)
364 base::i18n::WrapStringWithLTRFormatting(&errorType);
365 strings.SetString(
366 "errorType", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ERROR,
367 errorType.c_str()));
369 // Strings that display the invalid cert.
370 base::string16 subject(
371 ASCIIToUTF16(ssl_info_.cert->subject().GetDisplayName()));
372 base::string16 issuer(
373 ASCIIToUTF16(ssl_info_.cert->issuer().GetDisplayName()));
374 std::string hashes;
375 for (std::vector<net::HashValue>::const_iterator it =
376 ssl_info_.public_key_hashes.begin();
377 it != ssl_info_.public_key_hashes.end();
378 ++it) {
379 base::StringAppendF(&hashes, "%s ", it->ToString().c_str());
381 base::string16 fingerprint(ASCIIToUTF16(hashes));
382 if (rtl) {
383 // These are always going to be LTR.
384 base::i18n::WrapStringWithLTRFormatting(&subject);
385 base::i18n::WrapStringWithLTRFormatting(&issuer);
386 base::i18n::WrapStringWithLTRFormatting(&fingerprint);
388 strings.SetString(
389 "subject", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_SUBJECT,
390 subject.c_str()));
391 strings.SetString(
392 "issuer", l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_ISSUER,
393 issuer.c_str()));
394 strings.SetString(
395 "fingerprint",
396 l10n_util::GetStringFUTF16(IDS_SSL_BLOCKING_PAGE_HASHES,
397 fingerprint.c_str()));
400 base::StringPiece html(
401 ResourceBundle::GetSharedInstance().GetRawDataResource(
402 resource_id));
403 return webui::GetI18nTemplateHtml(html, &strings);
406 void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
407 int cert_id = content::CertStore::GetInstance()->StoreCert(
408 ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID());
409 DCHECK(cert_id);
411 entry->GetSSL().security_style =
412 content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
413 entry->GetSSL().cert_id = cert_id;
414 entry->GetSSL().cert_status = ssl_info_.cert_status;
415 entry->GetSSL().security_bits = ssl_info_.security_bits;
416 #if !defined(OS_ANDROID)
417 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
418 if (browser)
419 browser->VisibleSSLStateChanged(web_contents_);
420 #endif // !defined(OS_ANDROID)
423 // Matches events defined in ssl_error.html and ssl_roadblock.html.
424 void SSLBlockingPage::CommandReceived(const std::string& command) {
425 int cmd = atoi(command.c_str());
426 if (cmd == CMD_DONT_PROCEED) {
427 interstitial_page_->DontProceed();
428 } else if (cmd == CMD_PROCEED) {
429 interstitial_page_->Proceed();
430 } else if (cmd == CMD_MORE) {
431 RecordSSLBlockingPageEventStats(MORE);
432 } else if (cmd == CMD_RELOAD) {
433 // The interstitial can't refresh itself.
434 content::NavigationController* controller = &web_contents_->GetController();
435 controller->Reload(true);
439 void SSLBlockingPage::OverrideRendererPrefs(
440 content::RendererPreferences* prefs) {
441 Profile* profile = Profile::FromBrowserContext(
442 web_contents_->GetBrowserContext());
443 renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
446 void SSLBlockingPage::OnProceed() {
447 RecordSSLBlockingPageDetailedStats(true,
448 cert_error_,
449 overridable_ && !strict_enforcement_,
450 internal_,
451 num_visits_,
452 captive_portal_detection_enabled_,
453 captive_portal_probe_completed_,
454 captive_portal_no_response_,
455 captive_portal_detected_);
456 // Accepting the certificate resumes the loading of the page.
457 NotifyAllowCertificate();
460 void SSLBlockingPage::OnDontProceed() {
461 RecordSSLBlockingPageDetailedStats(false,
462 cert_error_,
463 overridable_ && !strict_enforcement_,
464 internal_,
465 num_visits_,
466 captive_portal_detection_enabled_,
467 captive_portal_probe_completed_,
468 captive_portal_no_response_,
469 captive_portal_detected_);
470 NotifyDenyCertificate();
473 void SSLBlockingPage::NotifyDenyCertificate() {
474 // It's possible that callback_ may not exist if the user clicks "Proceed"
475 // followed by pressing the back button before the interstitial is hidden.
476 // In that case the certificate will still be treated as allowed.
477 if (callback_.is_null())
478 return;
480 callback_.Run(false);
481 callback_.Reset();
484 void SSLBlockingPage::NotifyAllowCertificate() {
485 DCHECK(!callback_.is_null());
487 callback_.Run(true);
488 callback_.Reset();
491 // static
492 void SSLBlockingPage::SetExtraInfo(
493 base::DictionaryValue* strings,
494 const std::vector<base::string16>& extra_info) {
495 DCHECK_LT(extra_info.size(), 5U); // We allow 5 paragraphs max.
496 const char* keys[5] = {
497 "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5"
499 int i;
500 for (i = 0; i < static_cast<int>(extra_info.size()); i++) {
501 strings->SetString(keys[i], extra_info[i]);
503 for (; i < 5; i++) {
504 strings->SetString(keys[i], std::string());
508 void SSLBlockingPage::OnGotHistoryCount(HistoryService::Handle handle,
509 bool success,
510 int num_visits,
511 base::Time first_visit) {
512 num_visits_ = num_visits;
515 void SSLBlockingPage::Observe(
516 int type,
517 const content::NotificationSource& source,
518 const content::NotificationDetails& details) {
519 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
520 if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) {
521 captive_portal_probe_completed_ = true;
522 captive_portal::CaptivePortalService::Results* results =
523 content::Details<captive_portal::CaptivePortalService::Results>(
524 details).ptr();
525 // If a captive portal was detected at any point when the interstitial was
526 // displayed, assume that the interstitial was caused by a captive portal.
527 // Example scenario:
528 // 1- Interstitial displayed and captive portal detected, setting the flag.
529 // 2- Captive portal detection automatically opens portal login page.
530 // 3- User logs in on the portal login page.
531 // A notification will be received here for RESULT_INTERNET_CONNECTED. Make
532 // sure we don't clear the captive portal flag, since the interstitial was
533 // potentially caused by the captive portal.
534 captive_portal_detected_ = captive_portal_detected_ ||
535 (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
536 // Also keep track of non-HTTP portals and error cases.
537 captive_portal_no_response_ = captive_portal_no_response_ ||
538 (results->result == captive_portal::RESULT_NO_RESPONSE);
540 #endif