Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ssl / ssl_blocking_page.cc
blob97148f38d5d3a178c6453f327218e67e353500fe
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/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
10 #include "base/i18n/rtl.h"
11 #include "base/i18n/time_formatting.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/rand_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/interstitials/chrome_metrics_helper.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/renderer_preferences_util.h"
27 #include "chrome/browser/ssl/cert_report_helper.h"
28 #include "chrome/browser/ssl/ssl_cert_reporter.h"
29 #include "chrome/browser/ssl/ssl_error_classification.h"
30 #include "chrome/browser/ssl/ssl_error_info.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/grit/chromium_strings.h"
34 #include "chrome/grit/generated_resources.h"
35 #include "components/google/core/browser/google_util.h"
36 #include "content/public/browser/browser_thread.h"
37 #include "content/public/browser/cert_store.h"
38 #include "content/public/browser/interstitial_page.h"
39 #include "content/public/browser/interstitial_page_delegate.h"
40 #include "content/public/browser/navigation_controller.h"
41 #include "content/public/browser/navigation_entry.h"
42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/notification_types.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/render_view_host.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/common/renderer_preferences.h"
48 #include "content/public/common/ssl_status.h"
49 #include "grit/browser_resources.h"
50 #include "grit/components_strings.h"
51 #include "net/base/hash_value.h"
52 #include "net/base/net_errors.h"
53 #include "net/base/net_util.h"
54 #include "ui/base/l10n/l10n_util.h"
56 using base::ASCIIToUTF16;
57 using base::TimeTicks;
58 using content::InterstitialPage;
59 using content::InterstitialPageDelegate;
60 using content::NavigationController;
61 using content::NavigationEntry;
63 namespace {
65 // URL for help page.
66 const char kHelpURL[] = "https://support.google.com/chrome/answer/4454607";
68 // Constants for the Experience Sampling instrumentation.
69 const char kEventNameBase[] = "ssl_interstitial_";
70 const char kEventNotOverridable[] = "notoverridable_";
71 const char kEventOverridable[] = "overridable_";
73 // Events for UMA. Do not reorder or change!
74 enum SSLExpirationAndDecision {
75 EXPIRED_AND_PROCEED,
76 EXPIRED_AND_DO_NOT_PROCEED,
77 NOT_EXPIRED_AND_PROCEED,
78 NOT_EXPIRED_AND_DO_NOT_PROCEED,
79 END_OF_SSL_EXPIRATION_AND_DECISION,
82 // Rappor prefix, which is used for both overridable and non-overridable
83 // interstitials so we don't leak the "overridable" bit.
84 const char kSSLRapporPrefix[] = "ssl2";
86 void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed,
87 bool proceed,
88 bool overridable) {
89 SSLExpirationAndDecision event;
90 if (expired_but_previously_allowed && proceed)
91 event = EXPIRED_AND_PROCEED;
92 else if (expired_but_previously_allowed && !proceed)
93 event = EXPIRED_AND_DO_NOT_PROCEED;
94 else if (!expired_but_previously_allowed && proceed)
95 event = NOT_EXPIRED_AND_PROCEED;
96 else
97 event = NOT_EXPIRED_AND_DO_NOT_PROCEED;
99 if (overridable) {
100 UMA_HISTOGRAM_ENUMERATION(
101 "interstitial.ssl.expiration_and_decision.overridable",
102 event,
103 END_OF_SSL_EXPIRATION_AND_DECISION);
104 } else {
105 UMA_HISTOGRAM_ENUMERATION(
106 "interstitial.ssl.expiration_and_decision.nonoverridable",
107 event,
108 END_OF_SSL_EXPIRATION_AND_DECISION);
112 } // namespace
114 // static
115 InterstitialPageDelegate::TypeID SSLBlockingPage::kTypeForTesting =
116 &SSLBlockingPage::kTypeForTesting;
118 // Note that we always create a navigation entry with SSL errors.
119 // No error happening loading a sub-resource triggers an interstitial so far.
120 SSLBlockingPage::SSLBlockingPage(content::WebContents* web_contents,
121 int cert_error,
122 const net::SSLInfo& ssl_info,
123 const GURL& request_url,
124 int options_mask,
125 const base::Time& time_triggered,
126 scoped_ptr<SSLCertReporter> ssl_cert_reporter,
127 const base::Callback<void(bool)>& callback)
128 : SecurityInterstitialPage(web_contents, request_url),
129 callback_(callback),
130 cert_error_(cert_error),
131 ssl_info_(ssl_info),
132 overridable_(IsOverridable(
133 options_mask,
134 Profile::FromBrowserContext(web_contents->GetBrowserContext()))),
135 danger_overridable_(DoesPolicyAllowDangerOverride(
136 Profile::FromBrowserContext(web_contents->GetBrowserContext()))),
137 strict_enforcement_((options_mask & STRICT_ENFORCEMENT) != 0),
138 expired_but_previously_allowed_(
139 (options_mask & EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0),
140 time_triggered_(time_triggered) {
141 security_interstitials::MetricsHelper::ReportDetails reporting_info;
142 reporting_info.metric_prefix = GetUmaHistogramPrefix();
143 reporting_info.rappor_prefix = kSSLRapporPrefix;
144 reporting_info.rappor_report_type = rappor::UMA_RAPPOR_TYPE;
145 set_metrics_helper(new ChromeMetricsHelper(
146 web_contents, request_url, reporting_info, GetSamplingEventName()));
147 metrics_helper()->RecordUserDecision(
148 security_interstitials::MetricsHelper::SHOW);
149 metrics_helper()->RecordUserInteraction(
150 security_interstitials::MetricsHelper::TOTAL_VISITS);
152 cert_report_helper_.reset(new CertReportHelper(
153 ssl_cert_reporter.Pass(), web_contents, request_url, ssl_info,
154 certificate_reporting::ErrorReport::INTERSTITIAL_SSL, overridable_,
155 metrics_helper()));
157 ssl_error_classification_.reset(new SSLErrorClassification(
158 web_contents,
159 time_triggered_,
160 request_url,
161 cert_error_,
162 *ssl_info_.cert.get()));
163 ssl_error_classification_->RecordUMAStatistics(overridable_);
165 // Creating an interstitial without showing (e.g. from chrome://interstitials)
166 // it leaks memory, so don't create it here.
169 bool SSLBlockingPage::ShouldCreateNewNavigation() const {
170 return true;
173 InterstitialPageDelegate::TypeID SSLBlockingPage::GetTypeForTesting() const {
174 return SSLBlockingPage::kTypeForTesting;
177 SSLBlockingPage::~SSLBlockingPage() {
178 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
179 // Captive portal detection results can arrive anytime during the interstitial
180 // is being displayed, so record it when the interstitial is going away.
181 ssl_error_classification_->RecordCaptivePortalUMAStatistics(overridable_);
182 #endif
183 if (!callback_.is_null()) {
184 // The page is closed without the user having chosen what to do, default to
185 // deny.
186 metrics_helper()->RecordUserDecision(
187 security_interstitials::MetricsHelper::DONT_PROCEED);
188 RecordSSLExpirationPageEventState(
189 expired_but_previously_allowed_, false, overridable_);
190 NotifyDenyCertificate();
194 void SSLBlockingPage::PopulateInterstitialStrings(
195 base::DictionaryValue* load_time_data) {
196 CHECK(load_time_data);
197 base::string16 url(GetFormattedHostName());
198 // Shared values for both the overridable and non-overridable versions.
199 load_time_data->SetString("type", "SSL");
200 load_time_data->SetBoolean("bad_clock", false);
202 // Shared UI configuration for all SSL interstitials.
203 load_time_data->SetString("errorCode", net::ErrorToString(cert_error_));
204 load_time_data->SetString(
205 "openDetails",
206 l10n_util::GetStringUTF16(IDS_SSL_OPEN_DETAILS_BUTTON));
207 load_time_data->SetString(
208 "closeDetails",
209 l10n_util::GetStringUTF16(IDS_SSL_CLOSE_DETAILS_BUTTON));
211 load_time_data->SetString("tabTitle",
212 l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE));
213 load_time_data->SetString("heading",
214 l10n_util::GetStringUTF16(IDS_SSL_V2_HEADING));
215 load_time_data->SetString(
216 "primaryParagraph",
217 l10n_util::GetStringFUTF16(IDS_SSL_V2_PRIMARY_PARAGRAPH, url));
219 if (overridable_) {
220 load_time_data->SetBoolean("overridable", true);
222 SSLErrorInfo error_info = SSLErrorInfo::CreateError(
223 SSLErrorInfo::NetErrorToErrorType(cert_error_), ssl_info_.cert.get(),
224 request_url());
225 load_time_data->SetString("explanationParagraph", error_info.details());
226 load_time_data->SetString(
227 "primaryButtonText",
228 l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_SAFETY_BUTTON));
229 load_time_data->SetString(
230 "finalParagraph",
231 l10n_util::GetStringFUTF16(IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH, url));
232 } else {
233 load_time_data->SetBoolean("overridable", false);
235 SSLErrorInfo::ErrorType type =
236 SSLErrorInfo::NetErrorToErrorType(cert_error_);
237 if (type == SSLErrorInfo::CERT_INVALID &&
238 SSLErrorClassification::MaybeWindowsLacksSHA256Support()) {
239 load_time_data->SetString(
240 "explanationParagraph",
241 l10n_util::GetStringFUTF16(IDS_SSL_NONOVERRIDABLE_MORE_INVALID_SP3,
242 url));
243 } else {
244 load_time_data->SetString(
245 "explanationParagraph",
246 l10n_util::GetStringFUTF16(IDS_SSL_NONOVERRIDABLE_MORE, url));
248 load_time_data->SetString("primaryButtonText",
249 l10n_util::GetStringUTF16(IDS_SSL_RELOAD));
250 // Customize the help link depending on the specific error type.
251 // Only mark as HSTS if none of the more specific error types apply,
252 // and use INVALID as a fallback if no other string is appropriate.
253 load_time_data->SetInteger("errorType", type);
254 int help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
255 switch (type) {
256 case SSLErrorInfo::CERT_REVOKED:
257 help_string = IDS_SSL_NONOVERRIDABLE_REVOKED;
258 break;
259 case SSLErrorInfo::CERT_PINNED_KEY_MISSING:
260 help_string = IDS_SSL_NONOVERRIDABLE_PINNED;
261 break;
262 case SSLErrorInfo::CERT_INVALID:
263 help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
264 break;
265 default:
266 if (strict_enforcement_)
267 help_string = IDS_SSL_NONOVERRIDABLE_HSTS;
269 load_time_data->SetString("finalParagraph",
270 l10n_util::GetStringFUTF16(help_string, url));
273 // Set debugging information at the bottom of the warning.
274 load_time_data->SetString(
275 "subject", ssl_info_.cert->subject().GetDisplayName());
276 load_time_data->SetString(
277 "issuer", ssl_info_.cert->issuer().GetDisplayName());
278 load_time_data->SetString(
279 "expirationDate",
280 base::TimeFormatShortDate(ssl_info_.cert->valid_expiry()));
281 load_time_data->SetString(
282 "currentDate", base::TimeFormatShortDate(time_triggered_));
283 std::vector<std::string> encoded_chain;
284 ssl_info_.cert->GetPEMEncodedChain(
285 &encoded_chain);
286 load_time_data->SetString(
287 "pem", base::JoinString(encoded_chain, base::StringPiece()));
289 cert_report_helper_->PopulateExtendedReportingOption(load_time_data);
292 void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
293 int cert_id = content::CertStore::GetInstance()->StoreCert(
294 ssl_info_.cert.get(), web_contents()->GetRenderProcessHost()->GetID());
295 DCHECK(cert_id);
297 entry->GetSSL().security_style =
298 content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
299 entry->GetSSL().cert_id = cert_id;
300 entry->GetSSL().cert_status = ssl_info_.cert_status;
301 entry->GetSSL().security_bits = ssl_info_.security_bits;
304 void SSLBlockingPage::SetSSLCertReporterForTesting(
305 scoped_ptr<SSLCertReporter> ssl_cert_reporter) {
306 cert_report_helper_->SetSSLCertReporterForTesting(ssl_cert_reporter.Pass());
309 // This handles the commands sent from the interstitial JavaScript.
310 // DO NOT reorder or change this logic without also changing the JavaScript!
311 void SSLBlockingPage::CommandReceived(const std::string& command) {
312 if (command == "\"pageLoadComplete\"") {
313 // content::WaitForRenderFrameReady sends this message when the page
314 // load completes. Ignore it.
315 return;
318 int cmd = 0;
319 bool retval = base::StringToInt(command, &cmd);
320 DCHECK(retval);
321 switch (cmd) {
322 case CMD_DONT_PROCEED: {
323 interstitial_page()->DontProceed();
324 break;
326 case CMD_PROCEED: {
327 if (danger_overridable_) {
328 interstitial_page()->Proceed();
330 break;
332 case CMD_DO_REPORT: {
333 SetReportingPreference(true);
334 break;
336 case CMD_DONT_REPORT: {
337 SetReportingPreference(false);
338 break;
340 case CMD_SHOW_MORE_SECTION: {
341 metrics_helper()->RecordUserInteraction(
342 security_interstitials::MetricsHelper::SHOW_ADVANCED);
343 break;
345 case CMD_OPEN_HELP_CENTER: {
346 metrics_helper()->RecordUserInteraction(
347 security_interstitials::MetricsHelper::SHOW_LEARN_MORE);
348 content::NavigationController::LoadURLParams help_page_params(
349 google_util::AppendGoogleLocaleParam(
350 GURL(kHelpURL), g_browser_process->GetApplicationLocale()));
351 web_contents()->GetController().LoadURLWithParams(help_page_params);
352 break;
354 case CMD_RELOAD: {
355 metrics_helper()->RecordUserInteraction(
356 security_interstitials::MetricsHelper::RELOAD);
357 // The interstitial can't refresh itself.
358 web_contents()->GetController().Reload(true);
359 break;
361 case CMD_OPEN_REPORTING_PRIVACY:
362 OpenExtendedReportingPrivacyPolicy();
363 break;
364 case CMD_OPEN_DATE_SETTINGS:
365 case CMD_OPEN_DIAGNOSTIC:
366 // Commands not supported by the SSL interstitial.
367 NOTREACHED() << "Unexpected command: " << command;
371 void SSLBlockingPage::OverrideRendererPrefs(
372 content::RendererPreferences* prefs) {
373 Profile* profile = Profile::FromBrowserContext(
374 web_contents()->GetBrowserContext());
375 renderer_preferences_util::UpdateFromSystemSettings(
376 prefs, profile, web_contents());
379 void SSLBlockingPage::OnProceed() {
380 metrics_helper()->RecordUserDecision(
381 security_interstitials::MetricsHelper::PROCEED);
383 // Finish collecting information about invalid certificates, if the
384 // user opted in to.
385 cert_report_helper_->FinishCertCollection(
386 certificate_reporting::ErrorReport::USER_PROCEEDED);
388 RecordSSLExpirationPageEventState(
389 expired_but_previously_allowed_, true, overridable_);
390 // Accepting the certificate resumes the loading of the page.
391 NotifyAllowCertificate();
394 void SSLBlockingPage::OnDontProceed() {
395 metrics_helper()->RecordUserDecision(
396 security_interstitials::MetricsHelper::DONT_PROCEED);
398 // Finish collecting information about invalid certificates, if the
399 // user opted in to.
400 cert_report_helper_->FinishCertCollection(
401 certificate_reporting::ErrorReport::USER_DID_NOT_PROCEED);
403 RecordSSLExpirationPageEventState(
404 expired_but_previously_allowed_, false, overridable_);
405 NotifyDenyCertificate();
408 void SSLBlockingPage::NotifyDenyCertificate() {
409 // It's possible that callback_ may not exist if the user clicks "Proceed"
410 // followed by pressing the back button before the interstitial is hidden.
411 // In that case the certificate will still be treated as allowed.
412 if (callback_.is_null())
413 return;
415 callback_.Run(false);
416 callback_.Reset();
419 void SSLBlockingPage::NotifyAllowCertificate() {
420 DCHECK(!callback_.is_null());
422 callback_.Run(true);
423 callback_.Reset();
426 std::string SSLBlockingPage::GetUmaHistogramPrefix() const {
427 return overridable_ ? "ssl_overridable" : "ssl_nonoverridable";
430 std::string SSLBlockingPage::GetSamplingEventName() const {
431 std::string event_name(kEventNameBase);
432 if (overridable_)
433 event_name.append(kEventOverridable);
434 else
435 event_name.append(kEventNotOverridable);
436 event_name.append(net::ErrorToString(cert_error_));
437 return event_name;
440 // static
441 bool SSLBlockingPage::IsOverridable(int options_mask,
442 const Profile* const profile) {
443 const bool is_overridable =
444 (options_mask & SSLBlockingPage::OVERRIDABLE) &&
445 !(options_mask & SSLBlockingPage::STRICT_ENFORCEMENT) &&
446 profile->GetPrefs()->GetBoolean(prefs::kSSLErrorOverrideAllowed);
447 return is_overridable;
450 // static
451 bool SSLBlockingPage::DoesPolicyAllowDangerOverride(
452 const Profile* const profile) {
453 return profile->GetPrefs()->GetBoolean(prefs::kSSLErrorOverrideAllowed);