// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ssl/model/ios_ssl_blocking_page.h"
#import <utility>
#import "base/memory/ptr_util.h"
#import "base/metrics/histogram_macros.h"
#import "base/strings/string_number_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
#import "components/security_interstitials/core/metrics_helper.h"
#import "components/security_interstitials/core/ssl_error_options_mask.h"
#import "components/security_interstitials/core/ssl_error_ui.h"
#import "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/safe_browsing/model/safe_browsing_metrics_collector_factory.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/components/security_interstitials/ios_blocking_page_controller_client.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/security/ssl_status.h"
#import "ios/web/public/session/session_certificate_policy_cache.h"
#import "ios/web/public/web_state.h"
#import "net/base/net_errors.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"
using security_interstitials::SSLErrorOptionsMask;
using security_interstitials::SSLErrorUI;
// Note that we always create a navigation entry with SSL errors.
// No error happening loading a sub-resource triggers an interstitial so far.
IOSSSLBlockingPage::IOSSSLBlockingPage(
web::WebState* web_state,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
int options_mask,
const base::Time& time_triggered,
std::unique_ptr<security_interstitials::IOSBlockingPageControllerClient>
client)
: IOSSecurityInterstitialPage(web_state, request_url, client.get()),
web_state_(web_state),
ssl_info_(ssl_info),
overridable_(IsOverridable(options_mask)),
controller_(std::move(client)) {
DCHECK(web_state_);
// Override prefs for the SSLErrorUI.
if (overridable_)
options_mask |= SSLErrorOptionsMask::SOFT_OVERRIDE_ENABLED;
else
options_mask &= ~SSLErrorOptionsMask::SOFT_OVERRIDE_ENABLED;
ssl_error_ui_.reset(new SSLErrorUI(request_url, cert_error, ssl_info,
options_mask, time_triggered, GURL(),
controller_.get()));
ChromeBrowserState* browser_state =
ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState());
safe_browsing::SafeBrowsingMetricsCollector* metrics_collector =
SafeBrowsingMetricsCollectorFactory::GetForBrowserState(browser_state);
if (metrics_collector) {
metrics_collector->AddSafeBrowsingEventToPref(
safe_browsing::SafeBrowsingMetricsCollector::EventType::
SECURITY_SENSITIVE_SSL_INTERSTITIAL);
}
// Creating an interstitial without showing (e.g. from chrome://interstitials)
// it leaks memory, so don't create it here.
}
bool IOSSSLBlockingPage::ShouldCreateNewNavigation() const {
return true;
}
IOSSSLBlockingPage::~IOSSSLBlockingPage() {
}
void IOSSSLBlockingPage::PopulateInterstitialStrings(
base::Value::Dict& load_time_data) const {
ssl_error_ui_->PopulateStringsForHTML(load_time_data);
}
// static
bool IOSSSLBlockingPage::IsOverridable(int options_mask) {
return (options_mask & SSLErrorOptionsMask::SOFT_OVERRIDE_ENABLED) &&
!(options_mask & SSLErrorOptionsMask::STRICT_ENFORCEMENT);
}
void IOSSSLBlockingPage::HandleCommand(
security_interstitials::SecurityInterstitialCommand command) {
// If a proceed command is received, allowlist the certificate and reload
// the page to re-initiate the original navigation.
if (command == security_interstitials::CMD_PROCEED) {
web_state_->GetSessionCertificatePolicyCache()->RegisterAllowedCertificate(
ssl_info_.cert, request_url().host(), ssl_info_.cert_status);
web_state_->GetNavigationManager()->Reload(web::ReloadType::NORMAL,
/*check_for_repost=*/true);
return;
}
ssl_error_ui_->HandleCommand(command);
}