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"
8 #include "base/bind_helpers.h"
9 #include "base/build_time.h"
10 #include "base/callback_helpers.h"
11 #include "base/command_line.h"
12 #include "base/i18n/rtl.h"
13 #include "base/i18n/time_formatting.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/process/launch.h"
17 #include "base/rand_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/time/time.h"
24 #include "base/values.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/interstitials/security_interstitial_metrics_helper.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/renderer_preferences_util.h"
30 #include "chrome/browser/ssl/cert_report_helper.h"
31 #include "chrome/browser/ssl/certificate_error_report.h"
32 #include "chrome/browser/ssl/ssl_cert_reporter.h"
33 #include "chrome/browser/ssl/ssl_error_classification.h"
34 #include "chrome/browser/ssl/ssl_error_info.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/grit/chromium_strings.h"
38 #include "chrome/grit/generated_resources.h"
39 #include "components/google/core/browser/google_util.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/browser/cert_store.h"
42 #include "content/public/browser/interstitial_page.h"
43 #include "content/public/browser/interstitial_page_delegate.h"
44 #include "content/public/browser/navigation_controller.h"
45 #include "content/public/browser/navigation_entry.h"
46 #include "content/public/browser/notification_service.h"
47 #include "content/public/browser/notification_types.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/web_contents.h"
51 #include "content/public/common/renderer_preferences.h"
52 #include "content/public/common/ssl_status.h"
53 #include "grit/browser_resources.h"
54 #include "net/base/hash_value.h"
55 #include "net/base/net_errors.h"
56 #include "net/base/net_util.h"
57 #include "ui/base/l10n/l10n_util.h"
60 #include "base/base_paths_win.h"
61 #include "base/path_service.h"
62 #include "base/strings/string16.h"
63 #include "base/win/windows_version.h"
66 #if defined(OS_ANDROID)
67 #include "chrome/browser/android/intent_helper.h"
70 #if defined(OS_CHROMEOS)
71 #include "chrome/browser/profiles/profile_manager.h"
72 #include "chrome/browser/ui/chrome_pages.h"
73 #include "chrome/common/url_constants.h"
76 using base::ASCIIToUTF16
;
77 using base::TimeTicks
;
78 using content::InterstitialPage
;
79 using content::InterstitialPageDelegate
;
80 using content::NavigationController
;
81 using content::NavigationEntry
;
86 const char kHelpURL
[] = "https://support.google.com/chrome/answer/4454607";
88 // Constants for the Experience Sampling instrumentation.
89 const char kEventNameBase
[] = "ssl_interstitial_";
90 const char kEventNotOverridable
[] = "notoverridable_";
91 const char kEventOverridable
[] = "overridable_";
93 // Events for UMA. Do not reorder or change!
94 enum SSLExpirationAndDecision
{
96 EXPIRED_AND_DO_NOT_PROCEED
,
97 NOT_EXPIRED_AND_PROCEED
,
98 NOT_EXPIRED_AND_DO_NOT_PROCEED
,
99 END_OF_SSL_EXPIRATION_AND_DECISION
,
103 const char kSSLRapporPrefix
[] = "ssl2";
105 void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed
,
108 SSLExpirationAndDecision event
;
109 if (expired_but_previously_allowed
&& proceed
)
110 event
= EXPIRED_AND_PROCEED
;
111 else if (expired_but_previously_allowed
&& !proceed
)
112 event
= EXPIRED_AND_DO_NOT_PROCEED
;
113 else if (!expired_but_previously_allowed
&& proceed
)
114 event
= NOT_EXPIRED_AND_PROCEED
;
116 event
= NOT_EXPIRED_AND_DO_NOT_PROCEED
;
119 UMA_HISTOGRAM_ENUMERATION(
120 "interstitial.ssl.expiration_and_decision.overridable",
122 END_OF_SSL_EXPIRATION_AND_DECISION
);
124 UMA_HISTOGRAM_ENUMERATION(
125 "interstitial.ssl.expiration_and_decision.nonoverridable",
127 END_OF_SSL_EXPIRATION_AND_DECISION
);
131 void LaunchDateAndTimeSettings() {
132 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
133 // The code for each OS is completely separate, in order to avoid bugs like
134 // https://crbug.com/430877 .
135 #if defined(OS_ANDROID)
136 chrome::android::OpenDateAndTimeSettings();
138 #elif defined(OS_CHROMEOS)
139 std::string sub_page
= std::string(chrome::kSearchSubPage
) + "#" +
140 l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME
);
141 chrome::ShowSettingsSubPageForProfile(
142 ProfileManager::GetActiveUserProfile(), sub_page
);
144 #elif defined(OS_IOS)
145 // iOS does not have a way to launch the date and time settings.
148 #elif defined(OS_LINUX)
149 struct ClockCommand
{
150 const char* pathname
;
151 const char* argument
;
153 static const ClockCommand kClockCommands
[] = {
155 { "/usr/bin/unity-control-center", "datetime" },
158 // NOTE: On old Ubuntu, naming control panels doesn't work, so it
159 // opens the overview. This will have to be good enough.
160 { "/usr/bin/gnome-control-center", "datetime" },
161 { "/usr/local/bin/gnome-control-center", "datetime" },
162 { "/opt/bin/gnome-control-center", "datetime" },
164 { "/usr/bin/kcmshell4", "clock" },
165 { "/usr/local/bin/kcmshell4", "clock" },
166 { "/opt/bin/kcmshell4", "clock" },
169 base::CommandLine
command(base::FilePath(""));
170 for (size_t i
= 0; i
< arraysize(kClockCommands
); ++i
) {
171 base::FilePath
pathname(kClockCommands
[i
].pathname
);
172 if (base::PathExists(pathname
)) {
173 command
.SetProgram(pathname
);
174 command
.AppendArg(kClockCommands
[i
].argument
);
178 if (command
.GetProgram().empty()) {
179 // Alas, there is nothing we can do.
183 base::LaunchOptions options
;
184 options
.wait
= false;
185 options
.allow_new_privs
= true;
186 base::LaunchProcess(command
, options
);
188 #elif defined(OS_MACOSX)
189 base::CommandLine
command(base::FilePath("/usr/bin/open"));
190 command
.AppendArg("/System/Library/PreferencePanes/DateAndTime.prefPane");
192 base::LaunchOptions options
;
193 options
.wait
= false;
194 base::LaunchProcess(command
, options
);
196 #elif defined(OS_WIN)
198 PathService::Get(base::DIR_SYSTEM
, &path
);
199 static const base::char16 kControlPanelExe
[] = L
"control.exe";
200 path
= path
.Append(base::string16(kControlPanelExe
));
201 base::CommandLine
command(path
);
202 command
.AppendArg(std::string("/name"));
203 command
.AppendArg(std::string("Microsoft.DateAndTime"));
205 base::LaunchOptions options
;
206 options
.wait
= false;
207 base::LaunchProcess(command
, options
);
213 // Don't add code here! (See the comment at the beginning of the function.)
216 bool IsErrorDueToBadClock(const base::Time
& now
, int error
) {
217 if (SSLErrorInfo::NetErrorToErrorType(error
) !=
218 SSLErrorInfo::CERT_DATE_INVALID
) {
221 return SSLErrorClassification::IsUserClockInThePast(now
) ||
222 SSLErrorClassification::IsUserClockInTheFuture(now
);
228 InterstitialPageDelegate::TypeID
SSLBlockingPage::kTypeForTesting
=
229 &SSLBlockingPage::kTypeForTesting
;
231 // Note that we always create a navigation entry with SSL errors.
232 // No error happening loading a sub-resource triggers an interstitial so far.
233 SSLBlockingPage::SSLBlockingPage(content::WebContents
* web_contents
,
235 const net::SSLInfo
& ssl_info
,
236 const GURL
& request_url
,
238 const base::Time
& time_triggered
,
239 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
,
240 const base::Callback
<void(bool)>& callback
)
241 : SecurityInterstitialPage(web_contents
, request_url
),
243 cert_error_(cert_error
),
245 overridable_(IsOverridable(
247 Profile::FromBrowserContext(web_contents
->GetBrowserContext()))),
248 danger_overridable_(DoesPolicyAllowDangerOverride(
249 Profile::FromBrowserContext(web_contents
->GetBrowserContext()))),
250 strict_enforcement_((options_mask
& STRICT_ENFORCEMENT
) != 0),
251 expired_but_previously_allowed_(
252 (options_mask
& EXPIRED_BUT_PREVIOUSLY_ALLOWED
) != 0),
253 time_triggered_(time_triggered
) {
254 interstitial_reason_
=
255 IsErrorDueToBadClock(time_triggered_
, cert_error_
) ?
256 SSL_REASON_BAD_CLOCK
: SSL_REASON_SSL
;
258 // We collapse the Rappor metric name to just "ssl" so we don't leak
259 // the "overridable" bit. We skip Rappor altogether for bad clocks.
260 // This must be done after calculating |interstitial_reason_| above.
261 set_metrics_helper(new SecurityInterstitialMetricsHelper(
262 web_contents
, request_url
, GetUmaHistogramPrefix(), kSSLRapporPrefix
,
263 (interstitial_reason_
== SSL_REASON_BAD_CLOCK
264 ? SecurityInterstitialMetricsHelper::SKIP_RAPPOR
265 : SecurityInterstitialMetricsHelper::REPORT_RAPPOR
),
266 GetSamplingEventName()));
268 metrics_helper()->RecordUserDecision(SecurityInterstitialMetricsHelper::SHOW
);
269 metrics_helper()->RecordUserInteraction(
270 SecurityInterstitialMetricsHelper::TOTAL_VISITS
);
272 cert_report_helper_
.reset(new CertReportHelper(
273 ssl_cert_reporter
.Pass(), web_contents
, request_url
, ssl_info
,
274 GetCertReportInterstitialReason(), overridable_
, metrics_helper()));
276 ssl_error_classification_
.reset(new SSLErrorClassification(
281 *ssl_info_
.cert
.get()));
282 ssl_error_classification_
->RecordUMAStatistics(overridable_
);
284 // Creating an interstitial without showing (e.g. from chrome://interstitials)
285 // it leaks memory, so don't create it here.
288 bool SSLBlockingPage::ShouldCreateNewNavigation() const {
292 InterstitialPageDelegate::TypeID
SSLBlockingPage::GetTypeForTesting() const {
293 return SSLBlockingPage::kTypeForTesting
;
296 SSLBlockingPage::~SSLBlockingPage() {
297 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
298 // Captive portal detection results can arrive anytime during the interstitial
299 // is being displayed, so record it when the interstitial is going away.
300 ssl_error_classification_
->RecordCaptivePortalUMAStatistics(overridable_
);
302 if (!callback_
.is_null()) {
303 // The page is closed without the user having chosen what to do, default to
305 metrics_helper()->RecordUserDecision(
306 SecurityInterstitialMetricsHelper::DONT_PROCEED
);
307 RecordSSLExpirationPageEventState(
308 expired_but_previously_allowed_
, false, overridable_
);
309 NotifyDenyCertificate();
313 void SSLBlockingPage::PopulateInterstitialStrings(
314 base::DictionaryValue
* load_time_data
) {
315 CHECK(load_time_data
);
316 base::string16
url(GetFormattedHostName());
317 // Shared values for both the overridable and non-overridable versions.
318 load_time_data
->SetString("type", "SSL");
320 // Shared UI configuration for all SSL interstitials.
321 load_time_data
->SetString("errorCode", net::ErrorToString(cert_error_
));
322 load_time_data
->SetString(
324 l10n_util::GetStringUTF16(IDS_SSL_V2_OPEN_DETAILS_BUTTON
));
325 load_time_data
->SetString(
327 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOSE_DETAILS_BUTTON
));
329 // Conditional UI configuration.
330 if (interstitial_reason_
== SSL_REASON_BAD_CLOCK
) {
331 load_time_data
->SetBoolean("bad_clock", true);
332 load_time_data
->SetBoolean("overridable", false);
335 load_time_data
->SetBoolean("hide_primary_button", true);
337 load_time_data
->SetBoolean("hide_primary_button", false);
340 // We're showing the SSL clock warning to be helpful, but we haven't warned
341 // them about the risks. (And there might still be an SSL error after they
342 // fix their clock.) Thus, we don't allow the "danger" override in this
344 danger_overridable_
= false;
347 SSLErrorClassification::IsUserClockInTheFuture(time_triggered_
) ?
348 IDS_SSL_V2_CLOCK_AHEAD_HEADING
:
349 IDS_SSL_V2_CLOCK_BEHIND_HEADING
;
351 load_time_data
->SetString(
353 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_TITLE
));
354 load_time_data
->SetString(
356 l10n_util::GetStringUTF16(heading_string
));
357 load_time_data
->SetString(
359 l10n_util::GetStringFUTF16(
360 IDS_SSL_V2_CLOCK_PRIMARY_PARAGRAPH
,
362 base::TimeFormatFriendlyDateAndTime(time_triggered_
)));
364 load_time_data
->SetString(
366 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_UPDATE_DATE_AND_TIME
));
367 load_time_data
->SetString(
368 "explanationParagraph",
369 l10n_util::GetStringUTF16(IDS_SSL_V2_CLOCK_EXPLANATION
));
371 // The interstitial template expects this string, but we're not using it. So
372 // we send a blank string for now.
373 load_time_data
->SetString("finalParagraph", std::string());
375 load_time_data
->SetBoolean("bad_clock", false);
377 load_time_data
->SetString(
378 "tabTitle", l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE
));
379 load_time_data
->SetString(
380 "heading", l10n_util::GetStringUTF16(IDS_SSL_V2_HEADING
));
381 load_time_data
->SetString(
383 l10n_util::GetStringFUTF16(IDS_SSL_V2_PRIMARY_PARAGRAPH
, url
));
386 load_time_data
->SetBoolean("overridable", true);
388 SSLErrorInfo error_info
=
389 SSLErrorInfo::CreateError(
390 SSLErrorInfo::NetErrorToErrorType(cert_error_
),
391 ssl_info_
.cert
.get(),
393 load_time_data
->SetString("explanationParagraph", error_info
.details());
394 load_time_data
->SetString(
396 l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_SAFETY_BUTTON
));
397 load_time_data
->SetString(
399 l10n_util::GetStringFUTF16(IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH
,
402 load_time_data
->SetBoolean("overridable", false);
404 SSLErrorInfo::ErrorType type
=
405 SSLErrorInfo::NetErrorToErrorType(cert_error_
);
406 if (type
== SSLErrorInfo::CERT_INVALID
&& SSLErrorClassification::
407 MaybeWindowsLacksSHA256Support()) {
408 load_time_data
->SetString(
409 "explanationParagraph",
410 l10n_util::GetStringFUTF16(
411 IDS_SSL_NONOVERRIDABLE_MORE_INVALID_SP3
, url
));
413 load_time_data
->SetString("explanationParagraph",
414 l10n_util::GetStringFUTF16(
415 IDS_SSL_NONOVERRIDABLE_MORE
, url
));
417 load_time_data
->SetString(
419 l10n_util::GetStringUTF16(IDS_SSL_RELOAD
));
420 // Customize the help link depending on the specific error type.
421 // Only mark as HSTS if none of the more specific error types apply,
422 // and use INVALID as a fallback if no other string is appropriate.
423 load_time_data
->SetInteger("errorType", type
);
424 int help_string
= IDS_SSL_NONOVERRIDABLE_INVALID
;
426 case SSLErrorInfo::CERT_REVOKED
:
427 help_string
= IDS_SSL_NONOVERRIDABLE_REVOKED
;
429 case SSLErrorInfo::CERT_PINNED_KEY_MISSING
:
430 help_string
= IDS_SSL_NONOVERRIDABLE_PINNED
;
432 case SSLErrorInfo::CERT_INVALID
:
433 help_string
= IDS_SSL_NONOVERRIDABLE_INVALID
;
436 if (strict_enforcement_
)
437 help_string
= IDS_SSL_NONOVERRIDABLE_HSTS
;
439 load_time_data
->SetString(
440 "finalParagraph", l10n_util::GetStringFUTF16(help_string
, url
));
444 // Set debugging information at the bottom of the warning.
445 load_time_data
->SetString(
446 "subject", ssl_info_
.cert
->subject().GetDisplayName());
447 load_time_data
->SetString(
448 "issuer", ssl_info_
.cert
->issuer().GetDisplayName());
449 load_time_data
->SetString(
451 base::TimeFormatShortDate(ssl_info_
.cert
->valid_expiry()));
452 load_time_data
->SetString(
453 "currentDate", base::TimeFormatShortDate(time_triggered_
));
454 std::vector
<std::string
> encoded_chain
;
455 ssl_info_
.cert
->GetPEMEncodedChain(
457 load_time_data
->SetString(
458 "pem", base::JoinString(encoded_chain
, base::StringPiece()));
460 cert_report_helper_
->PopulateExtendedReportingOption(load_time_data
);
463 void SSLBlockingPage::OverrideEntry(NavigationEntry
* entry
) {
464 int cert_id
= content::CertStore::GetInstance()->StoreCert(
465 ssl_info_
.cert
.get(), web_contents()->GetRenderProcessHost()->GetID());
468 entry
->GetSSL().security_style
=
469 content::SECURITY_STYLE_AUTHENTICATION_BROKEN
;
470 entry
->GetSSL().cert_id
= cert_id
;
471 entry
->GetSSL().cert_status
= ssl_info_
.cert_status
;
472 entry
->GetSSL().security_bits
= ssl_info_
.security_bits
;
475 void SSLBlockingPage::SetSSLCertReporterForTesting(
476 scoped_ptr
<SSLCertReporter
> ssl_cert_reporter
) {
477 cert_report_helper_
->SetSSLCertReporterForTesting(ssl_cert_reporter
.Pass());
480 // This handles the commands sent from the interstitial JavaScript.
481 // DO NOT reorder or change this logic without also changing the JavaScript!
482 void SSLBlockingPage::CommandReceived(const std::string
& command
) {
483 if (command
== "\"pageLoadComplete\"") {
484 // content::WaitForRenderFrameReady sends this message when the page
485 // load completes. Ignore it.
490 bool retval
= base::StringToInt(command
, &cmd
);
493 case CMD_DONT_PROCEED
: {
494 interstitial_page()->DontProceed();
498 if (danger_overridable_
) {
499 interstitial_page()->Proceed();
503 case CMD_DO_REPORT
: {
504 SetReportingPreference(true);
507 case CMD_DONT_REPORT
: {
508 SetReportingPreference(false);
511 case CMD_SHOW_MORE_SECTION
: {
512 metrics_helper()->RecordUserInteraction(
513 SecurityInterstitialMetricsHelper::SHOW_ADVANCED
);
516 case CMD_OPEN_HELP_CENTER
: {
517 metrics_helper()->RecordUserInteraction(
518 SecurityInterstitialMetricsHelper::SHOW_LEARN_MORE
);
519 content::NavigationController::LoadURLParams
help_page_params(
520 google_util::AppendGoogleLocaleParam(
521 GURL(kHelpURL
), g_browser_process
->GetApplicationLocale()));
522 web_contents()->GetController().LoadURLWithParams(help_page_params
);
526 metrics_helper()->RecordUserInteraction(
527 SecurityInterstitialMetricsHelper::RELOAD
);
528 // The interstitial can't refresh itself.
529 web_contents()->GetController().Reload(true);
532 case CMD_OPEN_DATE_SETTINGS
: {
533 metrics_helper()->RecordUserInteraction(
534 SecurityInterstitialMetricsHelper::OPEN_TIME_SETTINGS
);
535 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE
,
536 base::Bind(&LaunchDateAndTimeSettings
));
539 case CMD_OPEN_REPORTING_PRIVACY
:
540 OpenExtendedReportingPrivacyPolicy();
542 case CMD_OPEN_DIAGNOSTIC
:
543 // Google doesn't currently have a transparency report for SSL.
544 NOTREACHED() << "Unexpected command: " << command
;
548 void SSLBlockingPage::OverrideRendererPrefs(
549 content::RendererPreferences
* prefs
) {
550 Profile
* profile
= Profile::FromBrowserContext(
551 web_contents()->GetBrowserContext());
552 renderer_preferences_util::UpdateFromSystemSettings(
553 prefs
, profile
, web_contents());
556 void SSLBlockingPage::OnProceed() {
557 metrics_helper()->RecordUserDecision(
558 SecurityInterstitialMetricsHelper::PROCEED
);
560 // Finish collecting information about invalid certificates, if the
562 cert_report_helper_
->FinishCertCollection(
563 CertificateErrorReport::USER_PROCEEDED
);
565 RecordSSLExpirationPageEventState(
566 expired_but_previously_allowed_
, true, overridable_
);
567 // Accepting the certificate resumes the loading of the page.
568 NotifyAllowCertificate();
571 void SSLBlockingPage::OnDontProceed() {
572 metrics_helper()->RecordUserDecision(
573 SecurityInterstitialMetricsHelper::DONT_PROCEED
);
575 // Finish collecting information about invalid certificates, if the
577 cert_report_helper_
->FinishCertCollection(
578 CertificateErrorReport::USER_DID_NOT_PROCEED
);
580 RecordSSLExpirationPageEventState(
581 expired_but_previously_allowed_
, false, overridable_
);
582 NotifyDenyCertificate();
585 void SSLBlockingPage::NotifyDenyCertificate() {
586 // It's possible that callback_ may not exist if the user clicks "Proceed"
587 // followed by pressing the back button before the interstitial is hidden.
588 // In that case the certificate will still be treated as allowed.
589 if (callback_
.is_null())
592 callback_
.Run(false);
596 void SSLBlockingPage::NotifyAllowCertificate() {
597 DCHECK(!callback_
.is_null());
603 CertificateErrorReport::InterstitialReason
604 SSLBlockingPage::GetCertReportInterstitialReason() {
605 switch (interstitial_reason_
) {
607 return CertificateErrorReport::INTERSTITIAL_SSL
;
608 case SSL_REASON_BAD_CLOCK
:
609 return CertificateErrorReport::INTERSTITIAL_CLOCK
;
613 return CertificateErrorReport::INTERSTITIAL_SSL
;
616 std::string
SSLBlockingPage::GetUmaHistogramPrefix() const {
617 switch (interstitial_reason_
) {
620 return "ssl_overridable";
622 return "ssl_nonoverridable";
623 case SSL_REASON_BAD_CLOCK
:
627 return std::string();
630 std::string
SSLBlockingPage::GetSamplingEventName() const {
631 std::string
event_name(kEventNameBase
);
633 event_name
.append(kEventOverridable
);
635 event_name
.append(kEventNotOverridable
);
636 event_name
.append(net::ErrorToString(cert_error_
));
641 bool SSLBlockingPage::IsOverridable(int options_mask
,
642 const Profile
* const profile
) {
643 const bool is_overridable
=
644 (options_mask
& SSLBlockingPage::OVERRIDABLE
) &&
645 !(options_mask
& SSLBlockingPage::STRICT_ENFORCEMENT
) &&
646 profile
->GetPrefs()->GetBoolean(prefs::kSSLErrorOverrideAllowed
);
647 return is_overridable
;
651 bool SSLBlockingPage::DoesPolicyAllowDangerOverride(
652 const Profile
* const profile
) {
653 return profile
->GetPrefs()->GetBoolean(prefs::kSSLErrorOverrideAllowed
);