Fire an error if a pref used in the UI is missing once all prefs are fetched.
[chromium-blink-merge.git] / chrome / browser / ssl / ssl_blocking_page.cc
blob64079cf8e9388f462563aae9bccfe57f85717180
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/build_time.h"
8 #include "base/command_line.h"
9 #include "base/i18n/rtl.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/metrics/histogram.h"
13 #include "base/process/launch.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/renderer_preferences_util.h"
25 #include "chrome/browser/ssl/ssl_error_classification.h"
26 #include "chrome/browser/ssl/ssl_error_info.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/grit/chromium_strings.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "components/google/core/browser/google_util.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/cert_store.h"
33 #include "content/public/browser/interstitial_page.h"
34 #include "content/public/browser/interstitial_page_delegate.h"
35 #include "content/public/browser/navigation_controller.h"
36 #include "content/public/browser/navigation_entry.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_types.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/render_view_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/common/renderer_preferences.h"
43 #include "content/public/common/ssl_status.h"
44 #include "grit/browser_resources.h"
45 #include "net/base/hash_value.h"
46 #include "net/base/net_errors.h"
47 #include "net/base/net_util.h"
48 #include "ui/base/l10n/l10n_util.h"
50 #if defined(OS_WIN)
51 #include "base/base_paths_win.h"
52 #include "base/path_service.h"
53 #include "base/strings/string16.h"
54 #include "base/win/windows_version.h"
55 #endif
57 #if defined(OS_ANDROID)
58 #include "chrome/browser/android/intent_helper.h"
59 #endif
61 #if defined(OS_CHROMEOS)
62 #include "chrome/browser/profiles/profile_manager.h"
63 #include "chrome/browser/ui/chrome_pages.h"
64 #include "chrome/common/url_constants.h"
65 #endif
67 using base::ASCIIToUTF16;
68 using base::TimeTicks;
69 using content::InterstitialPage;
70 using content::InterstitialPageDelegate;
71 using content::NavigationController;
72 using content::NavigationEntry;
74 namespace {
76 // URL for help page.
77 const char kHelpURL[] = "https://support.google.com/chrome/answer/4454607";
79 // Constants for the Experience Sampling instrumentation.
80 const char kEventNameBase[] = "ssl_interstitial_";
81 const char kEventNotOverridable[] = "notoverridable_";
82 const char kEventOverridable[] = "overridable_";
84 // Events for UMA. Do not reorder or change!
85 enum SSLExpirationAndDecision {
86 EXPIRED_AND_PROCEED,
87 EXPIRED_AND_DO_NOT_PROCEED,
88 NOT_EXPIRED_AND_PROCEED,
89 NOT_EXPIRED_AND_DO_NOT_PROCEED,
90 END_OF_SSL_EXPIRATION_AND_DECISION,
93 // Rappor prefix
94 const char kSSLRapporPrefix[] = "ssl";
96 void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed,
97 bool proceed,
98 bool overridable) {
99 SSLExpirationAndDecision event;
100 if (expired_but_previously_allowed && proceed)
101 event = EXPIRED_AND_PROCEED;
102 else if (expired_but_previously_allowed && !proceed)
103 event = EXPIRED_AND_DO_NOT_PROCEED;
104 else if (!expired_but_previously_allowed && proceed)
105 event = NOT_EXPIRED_AND_PROCEED;
106 else
107 event = NOT_EXPIRED_AND_DO_NOT_PROCEED;
109 if (overridable) {
110 UMA_HISTOGRAM_ENUMERATION(
111 "interstitial.ssl.expiration_and_decision.overridable",
112 event,
113 END_OF_SSL_EXPIRATION_AND_DECISION);
114 } else {
115 UMA_HISTOGRAM_ENUMERATION(
116 "interstitial.ssl.expiration_and_decision.nonoverridable",
117 event,
118 END_OF_SSL_EXPIRATION_AND_DECISION);
122 void LaunchDateAndTimeSettings() {
123 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
124 // The code for each OS is completely separate, in order to avoid bugs like
125 // https://crbug.com/430877 .
126 #if defined(OS_ANDROID)
127 chrome::android::OpenDateAndTimeSettings();
129 #elif defined(OS_CHROMEOS)
130 std::string sub_page = std::string(chrome::kSearchSubPage) + "#" +
131 l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME);
132 chrome::ShowSettingsSubPageForProfile(
133 ProfileManager::GetActiveUserProfile(), sub_page);
135 #elif defined(OS_IOS)
136 // iOS does not have a way to launch the date and time settings.
137 NOTREACHED();
139 #elif defined(OS_LINUX)
140 struct ClockCommand {
141 const char* pathname;
142 const char* argument;
144 static const ClockCommand kClockCommands[] = {
145 // Unity
146 { "/usr/bin/unity-control-center", "datetime" },
147 // GNOME
149 // NOTE: On old Ubuntu, naming control panels doesn't work, so it
150 // opens the overview. This will have to be good enough.
151 { "/usr/bin/gnome-control-center", "datetime" },
152 { "/usr/local/bin/gnome-control-center", "datetime" },
153 { "/opt/bin/gnome-control-center", "datetime" },
154 // KDE
155 { "/usr/bin/kcmshell4", "clock" },
156 { "/usr/local/bin/kcmshell4", "clock" },
157 { "/opt/bin/kcmshell4", "clock" },
160 base::CommandLine command(base::FilePath(""));
161 for (size_t i = 0; i < arraysize(kClockCommands); ++i) {
162 base::FilePath pathname(kClockCommands[i].pathname);
163 if (base::PathExists(pathname)) {
164 command.SetProgram(pathname);
165 command.AppendArg(kClockCommands[i].argument);
166 break;
169 if (command.GetProgram().empty()) {
170 // Alas, there is nothing we can do.
171 return;
174 base::LaunchOptions options;
175 options.wait = false;
176 options.allow_new_privs = true;
177 base::LaunchProcess(command, options);
179 #elif defined(OS_MACOSX)
180 base::CommandLine command(base::FilePath("/usr/bin/open"));
181 command.AppendArg("/System/Library/PreferencePanes/DateAndTime.prefPane");
183 base::LaunchOptions options;
184 options.wait = false;
185 base::LaunchProcess(command, options);
187 #elif defined(OS_WIN)
188 base::FilePath path;
189 PathService::Get(base::DIR_SYSTEM, &path);
190 static const base::char16 kControlPanelExe[] = L"control.exe";
191 path = path.Append(base::string16(kControlPanelExe));
192 base::CommandLine command(path);
193 command.AppendArg(std::string("/name"));
194 command.AppendArg(std::string("Microsoft.DateAndTime"));
196 base::LaunchOptions options;
197 options.wait = false;
198 base::LaunchProcess(command, options);
200 #else
201 NOTREACHED();
203 #endif
204 // Don't add code here! (See the comment at the beginning of the function.)
207 bool IsErrorDueToBadClock(const base::Time& now, int error) {
208 if (SSLErrorInfo::NetErrorToErrorType(error) !=
209 SSLErrorInfo::CERT_DATE_INVALID) {
210 return false;
212 return SSLErrorClassification::IsUserClockInThePast(now) ||
213 SSLErrorClassification::IsUserClockInTheFuture(now);
216 } // namespace
218 // static
219 InterstitialPageDelegate::TypeID SSLBlockingPage::kTypeForTesting =
220 &SSLBlockingPage::kTypeForTesting;
222 // Note that we always create a navigation entry with SSL errors.
223 // No error happening loading a sub-resource triggers an interstitial so far.
224 SSLBlockingPage::SSLBlockingPage(content::WebContents* web_contents,
225 int cert_error,
226 const net::SSLInfo& ssl_info,
227 const GURL& request_url,
228 int options_mask,
229 const base::Time& time_triggered,
230 const base::Callback<void(bool)>& callback)
231 : SecurityInterstitialPage(web_contents, request_url),
232 callback_(callback),
233 cert_error_(cert_error),
234 ssl_info_(ssl_info),
235 overridable_(IsOptionsOverridable(options_mask)),
236 danger_overridable_(true),
237 strict_enforcement_((options_mask & STRICT_ENFORCEMENT) != 0),
238 expired_but_previously_allowed_(
239 (options_mask & EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0),
240 time_triggered_(time_triggered) {
241 interstitial_reason_ =
242 IsErrorDueToBadClock(time_triggered_, cert_error_) ?
243 SSL_REASON_BAD_CLOCK : SSL_REASON_SSL;
245 // We collapse the Rappor metric name to just "ssl" so we don't leak
246 // the "overridable" bit. We skip Rappor altogether for bad clocks.
247 // This must be done after calculating |interstitial_reason_| above.
248 metrics_helper_.reset(new SecurityInterstitialMetricsHelper(
249 web_contents, request_url, GetUmaHistogramPrefix(), kSSLRapporPrefix,
250 (interstitial_reason_ == SSL_REASON_BAD_CLOCK
251 ? SecurityInterstitialMetricsHelper::SKIP_RAPPOR
252 : SecurityInterstitialMetricsHelper::REPORT_RAPPOR),
253 GetSamplingEventName()));
255 metrics_helper_->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW);
256 metrics_helper_->RecordUserInteraction(
257 SecurityInterstitialMetricsHelper::TOTAL_VISITS);
259 ssl_error_classification_.reset(new SSLErrorClassification(
260 web_contents,
261 time_triggered_,
262 request_url,
263 cert_error_,
264 *ssl_info_.cert.get()));
265 ssl_error_classification_->RecordUMAStatistics(overridable_);
267 // Creating an interstitial without showing (e.g. from chrome://interstitials)
268 // it leaks memory, so don't create it here.
271 bool SSLBlockingPage::ShouldCreateNewNavigation() const {
272 return true;
275 InterstitialPageDelegate::TypeID SSLBlockingPage::GetTypeForTesting() const {
276 return SSLBlockingPage::kTypeForTesting;
279 SSLBlockingPage::~SSLBlockingPage() {
280 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
281 // Captive portal detection results can arrive anytime during the interstitial
282 // is being displayed, so record it when the interstitial is going away.
283 ssl_error_classification_->RecordCaptivePortalUMAStatistics(overridable_);
284 #endif
285 if (!callback_.is_null()) {
286 // The page is closed without the user having chosen what to do, default to
287 // deny.
288 metrics_helper_->RecordUserDecision(
289 SecurityInterstitialMetricsHelper::DONT_PROCEED);
290 RecordSSLExpirationPageEventState(
291 expired_but_previously_allowed_, false, overridable_);
292 NotifyDenyCertificate();
296 void SSLBlockingPage::PopulateInterstitialStrings(
297 base::DictionaryValue* load_time_data) {
298 CHECK(load_time_data);
299 base::string16 url(GetFormattedHostName());
300 // Shared values for both the overridable and non-overridable versions.
301 load_time_data->SetString("type", "SSL");
303 // Shared UI configuration for all SSL interstitials.
304 load_time_data->SetString("errorCode", net::ErrorToString(cert_error_));
305 load_time_data->SetString(
306 "openDetails",
307 l10n_util::GetStringUTF16(IDS_SSL_V2_OPEN_DETAILS_BUTTON));
308 load_time_data->SetString(
309 "closeDetails",
310 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOSE_DETAILS_BUTTON));
312 // Conditional UI configuration.
313 if (interstitial_reason_ == SSL_REASON_BAD_CLOCK) {
314 load_time_data->SetBoolean("bad_clock", true);
315 load_time_data->SetBoolean("overridable", false);
317 #if defined(OS_IOS)
318 load_time_data->SetBoolean("hide_primary_button", true);
319 #else
320 load_time_data->SetBoolean("hide_primary_button", false);
321 #endif
323 // We're showing the SSL clock warning to be helpful, but we haven't warned
324 // them about the risks. (And there might still be an SSL error after they
325 // fix their clock.) Thus, we don't allow the "danger" override in this
326 // case.
327 danger_overridable_ = false;
329 int heading_string =
330 SSLErrorClassification::IsUserClockInTheFuture(time_triggered_) ?
331 IDS_SSL_V2_CLOCK_AHEAD_HEADING :
332 IDS_SSL_V2_CLOCK_BEHIND_HEADING;
334 load_time_data->SetString(
335 "tabTitle",
336 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_TITLE));
337 load_time_data->SetString(
338 "heading",
339 l10n_util::GetStringUTF16(heading_string));
340 load_time_data->SetString(
341 "primaryParagraph",
342 l10n_util::GetStringFUTF16(
343 IDS_SSL_V2_CLOCK_PRIMARY_PARAGRAPH,
344 url,
345 base::TimeFormatFriendlyDateAndTime(time_triggered_)));
347 load_time_data->SetString(
348 "primaryButtonText",
349 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_UPDATE_DATE_AND_TIME));
350 load_time_data->SetString(
351 "explanationParagraph",
352 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_EXPLANATION));
354 // The interstitial template expects this string, but we're not using it. So
355 // we send a blank string for now.
356 load_time_data->SetString("finalParagraph", std::string());
357 } else {
358 load_time_data->SetBoolean("bad_clock", false);
360 load_time_data->SetString(
361 "tabTitle", l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE));
362 load_time_data->SetString(
363 "heading", l10n_util::GetStringUTF16(IDS_SSL_V2_HEADING));
364 load_time_data->SetString(
365 "primaryParagraph",
366 l10n_util::GetStringFUTF16(IDS_SSL_V2_PRIMARY_PARAGRAPH, url));
368 if (overridable_) {
369 load_time_data->SetBoolean("overridable", true);
371 SSLErrorInfo error_info =
372 SSLErrorInfo::CreateError(
373 SSLErrorInfo::NetErrorToErrorType(cert_error_),
374 ssl_info_.cert.get(),
375 request_url());
376 load_time_data->SetString("explanationParagraph", error_info.details());
377 load_time_data->SetString(
378 "primaryButtonText",
379 l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_SAFETY_BUTTON));
380 load_time_data->SetString(
381 "finalParagraph",
382 l10n_util::GetStringFUTF16(IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH,
383 url));
384 } else {
385 load_time_data->SetBoolean("overridable", false);
387 SSLErrorInfo::ErrorType type =
388 SSLErrorInfo::NetErrorToErrorType(cert_error_);
389 if (type == SSLErrorInfo::CERT_INVALID && SSLErrorClassification::
390 MaybeWindowsLacksSHA256Support()) {
391 load_time_data->SetString(
392 "explanationParagraph",
393 l10n_util::GetStringFUTF16(
394 IDS_SSL_NONOVERRIDABLE_MORE_INVALID_SP3, url));
395 } else {
396 load_time_data->SetString("explanationParagraph",
397 l10n_util::GetStringFUTF16(
398 IDS_SSL_NONOVERRIDABLE_MORE, url));
400 load_time_data->SetString(
401 "primaryButtonText",
402 l10n_util::GetStringUTF16(IDS_SSL_RELOAD));
403 // Customize the help link depending on the specific error type.
404 // Only mark as HSTS if none of the more specific error types apply,
405 // and use INVALID as a fallback if no other string is appropriate.
406 load_time_data->SetInteger("errorType", type);
407 int help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
408 switch (type) {
409 case SSLErrorInfo::CERT_REVOKED:
410 help_string = IDS_SSL_NONOVERRIDABLE_REVOKED;
411 break;
412 case SSLErrorInfo::CERT_PINNED_KEY_MISSING:
413 help_string = IDS_SSL_NONOVERRIDABLE_PINNED;
414 break;
415 case SSLErrorInfo::CERT_INVALID:
416 help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
417 break;
418 default:
419 if (strict_enforcement_)
420 help_string = IDS_SSL_NONOVERRIDABLE_HSTS;
422 load_time_data->SetString(
423 "finalParagraph", l10n_util::GetStringFUTF16(help_string, url));
427 // Set debugging information at the bottom of the warning.
428 load_time_data->SetString(
429 "subject", ssl_info_.cert->subject().GetDisplayName());
430 load_time_data->SetString(
431 "issuer", ssl_info_.cert->issuer().GetDisplayName());
432 load_time_data->SetString(
433 "expirationDate",
434 base::TimeFormatShortDate(ssl_info_.cert->valid_expiry()));
435 load_time_data->SetString(
436 "currentDate", base::TimeFormatShortDate(time_triggered_));
437 std::vector<std::string> encoded_chain;
438 ssl_info_.cert->GetPEMEncodedChain(
439 &encoded_chain);
440 load_time_data->SetString(
441 "pem", JoinString(encoded_chain, std::string()));
444 void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
445 int cert_id = content::CertStore::GetInstance()->StoreCert(
446 ssl_info_.cert.get(), web_contents()->GetRenderProcessHost()->GetID());
447 DCHECK(cert_id);
449 entry->GetSSL().security_style =
450 content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
451 entry->GetSSL().cert_id = cert_id;
452 entry->GetSSL().cert_status = ssl_info_.cert_status;
453 entry->GetSSL().security_bits = ssl_info_.security_bits;
456 // This handles the commands sent from the interstitial JavaScript.
457 // DO NOT reorder or change this logic without also changing the JavaScript!
458 void SSLBlockingPage::CommandReceived(const std::string& command) {
459 int cmd = 0;
460 bool retval = base::StringToInt(command, &cmd);
461 DCHECK(retval);
462 switch (cmd) {
463 case CMD_DONT_PROCEED: {
464 interstitial_page()->DontProceed();
465 break;
467 case CMD_PROCEED: {
468 if (danger_overridable_) {
469 interstitial_page()->Proceed();
471 break;
473 case CMD_SHOW_MORE_SECTION: {
474 metrics_helper_->RecordUserInteraction(
475 SecurityInterstitialMetricsHelper::SHOW_ADVANCED);
476 break;
478 case CMD_OPEN_HELP_CENTER: {
479 metrics_helper_->RecordUserInteraction(
480 SecurityInterstitialMetricsHelper::SHOW_LEARN_MORE);
481 content::NavigationController::LoadURLParams help_page_params(
482 google_util::AppendGoogleLocaleParam(
483 GURL(kHelpURL), g_browser_process->GetApplicationLocale()));
484 web_contents()->GetController().LoadURLWithParams(help_page_params);
485 break;
487 case CMD_RELOAD: {
488 metrics_helper_->RecordUserInteraction(
489 SecurityInterstitialMetricsHelper::RELOAD);
490 // The interstitial can't refresh itself.
491 web_contents()->GetController().Reload(true);
492 break;
494 case CMD_OPEN_DATE_SETTINGS: {
495 metrics_helper_->RecordUserInteraction(
496 SecurityInterstitialMetricsHelper::OPEN_TIME_SETTINGS);
497 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
498 base::Bind(&LaunchDateAndTimeSettings));
499 break;
501 case CMD_OPEN_DIAGNOSTIC:
502 // Google doesn't currently have a transparency report for SSL.
503 case CMD_DO_REPORT:
504 case CMD_DONT_REPORT:
505 case CMD_OPEN_REPORTING_PRIVACY:
506 // Chrome doesn't currently do Extended Reporting for SSL.
507 NOTREACHED() << "Unexpected command: " << command;
511 void SSLBlockingPage::OverrideRendererPrefs(
512 content::RendererPreferences* prefs) {
513 Profile* profile = Profile::FromBrowserContext(
514 web_contents()->GetBrowserContext());
515 renderer_preferences_util::UpdateFromSystemSettings(
516 prefs, profile, web_contents());
519 void SSLBlockingPage::OnProceed() {
520 metrics_helper_->RecordUserDecision(
521 SecurityInterstitialMetricsHelper::PROCEED);
522 RecordSSLExpirationPageEventState(
523 expired_but_previously_allowed_, true, overridable_);
524 // Accepting the certificate resumes the loading of the page.
525 NotifyAllowCertificate();
528 void SSLBlockingPage::OnDontProceed() {
529 metrics_helper_->RecordUserDecision(
530 SecurityInterstitialMetricsHelper::DONT_PROCEED);
531 RecordSSLExpirationPageEventState(
532 expired_but_previously_allowed_, false, overridable_);
533 NotifyDenyCertificate();
536 void SSLBlockingPage::NotifyDenyCertificate() {
537 // It's possible that callback_ may not exist if the user clicks "Proceed"
538 // followed by pressing the back button before the interstitial is hidden.
539 // In that case the certificate will still be treated as allowed.
540 if (callback_.is_null())
541 return;
543 callback_.Run(false);
544 callback_.Reset();
547 void SSLBlockingPage::NotifyAllowCertificate() {
548 DCHECK(!callback_.is_null());
550 callback_.Run(true);
551 callback_.Reset();
554 std::string SSLBlockingPage::GetUmaHistogramPrefix() const {
555 switch (interstitial_reason_) {
556 case SSL_REASON_SSL:
557 if (overridable_)
558 return "ssl_overridable";
559 else
560 return "ssl_nonoverridable";
561 case SSL_REASON_BAD_CLOCK:
562 return "bad_clock";
564 NOTREACHED();
565 return std::string();
568 std::string SSLBlockingPage::GetSamplingEventName() const {
569 std::string event_name(kEventNameBase);
570 if (overridable_)
571 event_name.append(kEventOverridable);
572 else
573 event_name.append(kEventNotOverridable);
574 event_name.append(net::ErrorToString(cert_error_));
575 return event_name;
578 // static
579 bool SSLBlockingPage::IsOptionsOverridable(int options_mask) {
580 return (options_mask & SSLBlockingPage::OVERRIDABLE) &&
581 !(options_mask & SSLBlockingPage::STRICT_ENFORCEMENT);