blob: 9d080e17c894ccae5fb1e77cbf25314341dd1fe9 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/interstitials/chrome_settings_page_helper.h"
#include "chrome/browser/net/secure_dns_config.h"
#include "chrome/browser/net/stub_resolver_config_reader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/ssl/insecure_form/insecure_form_controller_client.h"
#include "chrome/browser/ssl/ssl_error_controller_client.h"
#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/common/channel_info.h"
#include "components/security_interstitials/content/content_metrics_helper.h"
#include "components/security_interstitials/content/settings_page_helper.h"
#include "components/security_interstitials/content/ssl_blocking_page.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "content/public/browser/web_contents.h"
#if defined(OS_WIN)
#include "base/enterprise_util.h"
#elif BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/policy/core/browser_policy_connector_chromeos.h"
#include "chrome/browser/browser_process_platform_part.h"
#endif
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "components/security_interstitials/content/captive_portal_helper_android.h"
#include "content/public/common/referrer.h"
#include "net/android/network_library.h"
#include "ui/base/window_open_disposition.h"
#endif
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
#include "chrome/browser/captive_portal/captive_portal_service_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/captive_portal/content/captive_portal_tab_helper.h"
#include "net/base/net_errors.h"
#include "net/dns/public/secure_dns_mode.h"
#endif
namespace {
enum EnterpriseManaged {
ENTERPRISE_MANAGED_STATUS_NOT_SET,
ENTERPRISE_MANAGED_STATUS_TRUE,
ENTERPRISE_MANAGED_STATUS_FALSE
};
EnterpriseManaged g_is_enterprise_managed_for_testing =
ENTERPRISE_MANAGED_STATUS_NOT_SET;
bool IsEnterpriseManaged() {
// Return the value of the testing flag if it's set.
if (g_is_enterprise_managed_for_testing == ENTERPRISE_MANAGED_STATUS_TRUE) {
return true;
}
if (g_is_enterprise_managed_for_testing == ENTERPRISE_MANAGED_STATUS_FALSE) {
return false;
}
#if defined(OS_WIN)
if (base::IsMachineExternallyManaged()) {
return true;
}
#elif BUILDFLAG(IS_CHROMEOS_ASH)
if (g_browser_process->platform_part()->browser_policy_connector_chromeos()) {
return true;
}
#endif // #if defined OS_WIN
return false;
}
// Opens the login page for a captive portal. Passed in to
// CaptivePortalBlockingPage to be invoked when the user has pressed the
// connect button.
void OpenLoginPage(content::WebContents* web_contents) {
#if !BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// OpenLoginTabForWebContents() is not available on Android (the only
// platform on which captive portal detection is not enabled). Simply open
// the platform's portal detection URL in a new tab.
const std::string url = security_interstitials::GetCaptivePortalServerUrl(
base::android::AttachCurrentThread());
content::OpenURLParams params(GURL(url), content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
web_contents->OpenURL(params);
#else
ChromeSecurityBlockingPageFactory::OpenLoginTabForWebContents(web_contents,
true);
#endif // !BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
}
std::unique_ptr<ContentMetricsHelper> CreateMetricsHelperAndStartRecording(
content::WebContents* web_contents,
const GURL& request_url,
const std::string& metric_prefix,
bool overridable) {
security_interstitials::MetricsHelper::ReportDetails reporting_info;
reporting_info.metric_prefix = metric_prefix;
std::unique_ptr<ContentMetricsHelper> metrics_helper =
std::make_unique<ContentMetricsHelper>(
HistoryServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()),
ServiceAccessType::EXPLICIT_ACCESS),
request_url, reporting_info);
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
metrics_helper.get()->StartRecordingCaptivePortalMetrics(
CaptivePortalServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext())),
overridable);
#endif
return metrics_helper;
}
std::unique_ptr<security_interstitials::SettingsPageHelper>
CreateSettingsPageHelper() {
return security_interstitials::ChromeSettingsPageHelper::
CreateChromeSettingsPageHelper();
}
} // namespace
std::unique_ptr<SSLBlockingPage>
ChromeSecurityBlockingPageFactory::CreateSSLPage(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
int options_mask,
const base::Time& time_triggered,
const GURL& support_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter) {
bool overridable = SSLBlockingPage::IsOverridable(options_mask);
std::unique_ptr<ContentMetricsHelper> metrics_helper(
CreateMetricsHelperAndStartRecording(
web_contents, request_url,
overridable ? "ssl_overridable" : "ssl_nonoverridable", overridable));
StatefulSSLHostStateDelegate* state =
StatefulSSLHostStateDelegateFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
state->DidDisplayErrorPage(cert_error);
bool is_recurrent_error = state->HasSeenRecurrentErrors(cert_error);
if (overridable) {
UMA_HISTOGRAM_BOOLEAN("interstitial.ssl_overridable.is_recurrent_error",
is_recurrent_error);
if (cert_error == net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED) {
UMA_HISTOGRAM_BOOLEAN(
"interstitial.ssl_overridable.is_recurrent_error.ct_error",
is_recurrent_error);
}
} else {
UMA_HISTOGRAM_BOOLEAN("interstitial.ssl_nonoverridable.is_recurrent_error",
is_recurrent_error);
if (cert_error == net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED) {
UMA_HISTOGRAM_BOOLEAN(
"interstitial.ssl_nonoverridable.is_recurrent_error.ct_error",
is_recurrent_error);
}
}
auto controller_client = std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
std::move(metrics_helper), CreateSettingsPageHelper());
std::unique_ptr<SSLBlockingPage> page;
page = std::make_unique<SSLBlockingPage>(
web_contents, cert_error, ssl_info, request_url, options_mask,
time_triggered, support_url, std::move(ssl_cert_reporter), overridable,
/*can_show_enhanced_protection_message=*/true,
std::move(controller_client));
DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<CaptivePortalBlockingPage>
ChromeSecurityBlockingPageFactory::CreateCaptivePortalBlockingPage(
content::WebContents* web_contents,
const GURL& request_url,
const GURL& login_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
const net::SSLInfo& ssl_info,
int cert_error) {
auto page = std::make_unique<CaptivePortalBlockingPage>(
web_contents, request_url, login_url, std::move(ssl_cert_reporter),
/*can_show_enhanced_protection_message=*/true, ssl_info,
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"captive_portal", false),
CreateSettingsPageHelper()),
base::BindRepeating(&OpenLoginPage));
DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<BadClockBlockingPage>
ChromeSecurityBlockingPageFactory::CreateBadClockBlockingPage(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
const base::Time& time_triggered,
ssl_errors::ClockState clock_state,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter) {
auto page = std::make_unique<BadClockBlockingPage>(
web_contents, cert_error, ssl_info, request_url, time_triggered,
/*can_show_enhanced_protection_message=*/true, clock_state,
std::move(ssl_cert_reporter),
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"bad_clock", false),
CreateSettingsPageHelper()));
ChromeSecurityBlockingPageFactory::DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<LegacyTLSBlockingPage>
ChromeSecurityBlockingPageFactory::CreateLegacyTLSBlockingPage(
content::WebContents* web_contents,
int cert_error,
const GURL& request_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
const net::SSLInfo& ssl_info) {
auto page = std::make_unique<LegacyTLSBlockingPage>(
web_contents, cert_error, request_url, std::move(ssl_cert_reporter),
/*can_show_enhanced_protection_message=*/true, ssl_info,
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"legacy_tls", false),
CreateSettingsPageHelper()));
DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<MITMSoftwareBlockingPage>
ChromeSecurityBlockingPageFactory::CreateMITMSoftwareBlockingPage(
content::WebContents* web_contents,
int cert_error,
const GURL& request_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
const net::SSLInfo& ssl_info,
const std::string& mitm_software_name) {
auto page = std::make_unique<MITMSoftwareBlockingPage>(
web_contents, cert_error, request_url, std::move(ssl_cert_reporter),
/*can_show_enhanced_protection_message=*/true, ssl_info,
mitm_software_name, IsEnterpriseManaged(),
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"mitm_software", false),
CreateSettingsPageHelper()));
DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<BlockedInterceptionBlockingPage>
ChromeSecurityBlockingPageFactory::CreateBlockedInterceptionBlockingPage(
content::WebContents* web_contents,
int cert_error,
const GURL& request_url,
std::unique_ptr<SSLCertReporter> ssl_cert_reporter,
const net::SSLInfo& ssl_info) {
auto page = std::make_unique<BlockedInterceptionBlockingPage>(
web_contents, cert_error, request_url, std::move(ssl_cert_reporter),
/*can_show_enhanced_protection_message=*/true, ssl_info,
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"blocked_interception", false),
CreateSettingsPageHelper()));
ChromeSecurityBlockingPageFactory::DoChromeSpecificSetup(page.get());
return page;
}
std::unique_ptr<security_interstitials::InsecureFormBlockingPage>
ChromeSecurityBlockingPageFactory::CreateInsecureFormBlockingPage(
content::WebContents* web_contents,
const GURL& request_url) {
std::unique_ptr<InsecureFormControllerClient> client =
std::make_unique<InsecureFormControllerClient>(web_contents, request_url);
auto page =
std::make_unique<security_interstitials::InsecureFormBlockingPage>(
web_contents, request_url, std::move(client));
return page;
}
// static
void ChromeSecurityBlockingPageFactory::DoChromeSpecificSetup(
SSLBlockingPageBase* page) {
page->cert_report_helper()->set_client_details_callback(
base::BindRepeating([](CertificateErrorReport* report) {
report->AddChromeChannel(chrome::GetChannel());
#if defined(OS_WIN)
report->SetIsEnterpriseManaged(base::IsMachineExternallyManaged());
#elif BUILDFLAG(IS_CHROMEOS_ASH)
report->SetIsEnterpriseManaged(g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->IsDeviceEnterpriseManaged());
#endif
// TODO(estade): this one is probably necessary for all clients, and
// should be enforced (e.g. via a pure virtual method) rather than
// optional.
report->AddNetworkTimeInfo(g_browser_process->network_time_tracker());
}));
}
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// static
void ChromeSecurityBlockingPageFactory::OpenLoginTabForWebContents(
content::WebContents* web_contents,
bool focus) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
// If the Profile doesn't have a tabbed browser window open, do nothing.
if (!browser)
return;
SecureDnsConfig secure_dns_config =
SystemNetworkContextManager::GetStubResolverConfigReader()
->GetSecureDnsConfiguration(
false /* force_check_parental_controls */);
// If the DNS mode is SECURE, captive portal login tabs should be opened in
// new popup windows where secure DNS will be disabled.
if (secure_dns_config.mode() == net::SecureDnsMode::kSecure) {
// If there is already a captive portal popup window, do not create another.
for (auto* contents : AllTabContentses()) {
captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
captive_portal::CaptivePortalTabHelper::FromWebContents(contents);
if (captive_portal_tab_helper->IsLoginTab()) {
Browser* browser_with_login_tab =
chrome::FindBrowserWithWebContents(contents);
browser_with_login_tab->window()->Show();
browser_with_login_tab->tab_strip_model()->ActivateTabAt(
browser_with_login_tab->tab_strip_model()->GetIndexOfWebContents(
contents));
return;
}
}
// Otherwise, create a captive portal popup window.
NavigateParams params(
browser,
CaptivePortalServiceFactory::GetForProfile(browser->profile())
->test_url(),
ui::PAGE_TRANSITION_TYPED);
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.is_captive_portal_popup = true;
Navigate(&params);
content::WebContents* new_contents = params.navigated_or_inserted_contents;
captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
captive_portal::CaptivePortalTabHelper::FromWebContents(new_contents);
captive_portal_tab_helper->SetIsLoginTab();
return;
}
// Check if the Profile's topmost browser window already has a login tab.
// If so, do nothing.
// TODO(mmenke): Consider focusing that tab, at least if this is the tab
// helper for the currently active tab for the profile.
for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
content::WebContents* contents =
browser->tab_strip_model()->GetWebContentsAt(i);
captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
captive_portal::CaptivePortalTabHelper::FromWebContents(contents);
if (captive_portal_tab_helper->IsLoginTab()) {
if (focus)
browser->tab_strip_model()->ActivateTabAt(i);
return;
}
}
// Otherwise, open a login tab. Only end up here when a captive portal result
// was received, so it's safe to assume profile has a
// captive_portal::CaptivePortalService.
content::WebContents* new_contents = chrome::AddSelectedTabWithURL(
browser,
CaptivePortalServiceFactory::GetForProfile(browser->profile())
->test_url(),
ui::PAGE_TRANSITION_TYPED);
captive_portal::CaptivePortalTabHelper* captive_portal_tab_helper =
captive_portal::CaptivePortalTabHelper::FromWebContents(new_contents);
captive_portal_tab_helper->SetIsLoginTab();
}
#endif // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
void ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(
bool enterprise_managed) {
if (enterprise_managed) {
g_is_enterprise_managed_for_testing = ENTERPRISE_MANAGED_STATUS_TRUE;
} else {
g_is_enterprise_managed_for_testing = ENTERPRISE_MANAGED_STATUS_FALSE;
}
}