blob: 6fc4c5afe4de6be06fe3518c49ab3ffdbf655e82 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.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/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#include "chrome/browser/safe_browsing/safe_browsing_metrics_collector_factory.h"
#include "chrome/browser/ssl/https_only_mode_controller_client.h"
#include "chrome/browser/ssl/https_upgrades_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/ui/browser_navigator.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.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/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "content/public/browser/web_contents.h"
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#include "base/enterprise_util.h"
#elif BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/browser_process_platform_part.h"
#endif
#if BUILDFLAG(IS_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;
// 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, /*navigation_handle_callback=*/{});
#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();
}
void LogSafeBrowsingSecuritySensitiveAction(
safe_browsing::SafeBrowsingMetricsCollector* metrics_collector) {
if (metrics_collector) {
metrics_collector->AddSafeBrowsingEventToPref(
safe_browsing::SafeBrowsingMetricsCollector::EventType::
SECURITY_SENSITIVE_SSL_INTERSTITIAL);
}
}
} // 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) {
bool overridable = SSLBlockingPage::IsOverridable(options_mask);
std::unique_ptr<ContentMetricsHelper> metrics_helper(
CreateMetricsHelperAndStartRecording(
web_contents, request_url,
overridable ? "ssl_overridable" : "ssl_nonoverridable", overridable));
LogSafeBrowsingSecuritySensitiveAction(
safe_browsing::SafeBrowsingMetricsCollectorFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext())));
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, overridable,
/*can_show_enhanced_protection_message=*/true,
std::move(controller_client));
return page;
}
std::unique_ptr<CaptivePortalBlockingPage>
ChromeSecurityBlockingPageFactory::CreateCaptivePortalBlockingPage(
content::WebContents* web_contents,
const GURL& request_url,
const GURL& login_url,
const net::SSLInfo& ssl_info,
int cert_error) {
auto page = std::make_unique<CaptivePortalBlockingPage>(
web_contents, request_url, login_url,
/*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));
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) {
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::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"bad_clock", false),
CreateSettingsPageHelper()));
return page;
}
std::unique_ptr<MITMSoftwareBlockingPage>
ChromeSecurityBlockingPageFactory::CreateMITMSoftwareBlockingPage(
content::WebContents* web_contents,
int cert_error,
const GURL& request_url,
const net::SSLInfo& ssl_info,
const std::string& mitm_software_name) {
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
LogSafeBrowsingSecuritySensitiveAction(
safe_browsing::SafeBrowsingMetricsCollectorFactory::GetForProfile(
profile));
auto page = std::make_unique<MITMSoftwareBlockingPage>(
web_contents, cert_error, request_url,
/*can_show_enhanced_protection_message=*/true, ssl_info,
mitm_software_name, IsEnterpriseManaged(profile),
std::make_unique<SSLErrorControllerClient>(
web_contents, ssl_info, cert_error, request_url,
CreateMetricsHelperAndStartRecording(web_contents, request_url,
"mitm_software", false),
CreateSettingsPageHelper()));
return page;
}
std::unique_ptr<BlockedInterceptionBlockingPage>
ChromeSecurityBlockingPageFactory::CreateBlockedInterceptionBlockingPage(
content::WebContents* web_contents,
int cert_error,
const GURL& request_url,
const net::SSLInfo& ssl_info) {
LogSafeBrowsingSecuritySensitiveAction(
safe_browsing::SafeBrowsingMetricsCollectorFactory::GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext())));
auto page = std::make_unique<BlockedInterceptionBlockingPage>(
web_contents, cert_error, request_url,
/*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()));
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;
}
std::unique_ptr<security_interstitials::HttpsOnlyModeBlockingPage>
ChromeSecurityBlockingPageFactory::CreateHttpsOnlyModeBlockingPage(
content::WebContents* web_contents,
const GURL& request_url,
security_interstitials::https_only_mode::HttpInterstitialState
interstitial_state,
std::optional<std::string> url_type_param,
security_interstitials::HttpsOnlyModeBlockingPage::MetricsCallback
metrics_callback) {
std::unique_ptr<HttpsOnlyModeControllerClient> client =
std::make_unique<HttpsOnlyModeControllerClient>(
web_contents, request_url, CreateSettingsPageHelper());
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
if (url_type_param) {
if (*url_type_param == "advanced_protection") {
interstitial_state.enabled_by_advanced_protection = true;
} else if (*url_type_param == "site_engagement") {
interstitial_state.enabled_by_engagement_heuristic = true;
} else if (*url_type_param == "typically_secure") {
interstitial_state.enabled_by_typically_secure_browsing = true;
} else if (*url_type_param == "incognito") {
interstitial_state.enabled_by_incognito = true;
}
} else {
interstitial_state.enabled_by_advanced_protection =
profile &&
safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
profile)
->IsUnderAdvancedProtection();
// HFM interstitial with Site Engagement heuristic is only shown if the
// feature flag is enabled, so update the relevant flag here.
interstitial_state.enabled_by_engagement_heuristic =
interstitial_state.enabled_by_engagement_heuristic &&
base::FeatureList::IsEnabled(
features::kHttpsFirstModeV2ForEngagedSites);
}
auto page =
std::make_unique<security_interstitials::HttpsOnlyModeBlockingPage>(
web_contents, request_url, std::move(client), interstitial_state,
metrics_callback);
return page;
}
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// Open a login tab or popup for the captive portal login page.
void OpenLoginTab(Browser* browser,
captive_portal::CaptivePortalWindowType portal_type) {
// We only end up here when a captive portal result was received, so it's safe
// to assume profile has a captive_portal::CaptivePortalService.
NavigateParams params(
browser,
CaptivePortalServiceFactory::GetForProfile(browser->profile())
->test_url(),
ui::PAGE_TRANSITION_TYPED);
WindowOpenDisposition disposition;
switch (portal_type) {
case captive_portal::CaptivePortalWindowType::kPopup:
disposition = WindowOpenDisposition::NEW_POPUP;
break;
case captive_portal::CaptivePortalWindowType::kTab:
disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
break;
default:
NOTREACHED() << "Invalid captive portal window type";
}
params.captive_portal_window_type = portal_type;
params.disposition = disposition;
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();
}
// static
void ChromeSecurityBlockingPageFactory::OpenLoginTabForWebContents(
content::WebContents* web_contents,
bool focus) {
Browser* browser = chrome::FindBrowserWithTab(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::FindBrowserWithTab(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.
OpenLoginTab(browser, captive_portal::CaptivePortalWindowType::kPopup);
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.
OpenLoginTab(browser, captive_portal::CaptivePortalWindowType::kTab);
}
#endif // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// static
bool ChromeSecurityBlockingPageFactory::IsEnterpriseManaged(Profile* profile) {
// 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 BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
if (base::IsManagedOrEnterpriseDevice()) {
return true;
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_CHROMEOS)
auto* connector =
g_browser_process->platform_part()->browser_policy_connector_ash();
if (connector && connector->IsDeviceEnterpriseManaged()) {
return true;
}
#endif // BUILDFLAG(IS_CHROMEOS)
if (profile && profile->GetProfilePolicyConnector() &&
profile->GetProfilePolicyConnector()->IsManaged()) {
return true;
}
return false;
}
// static
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;
}
}