blob: 2e40a356c7949464bf175020b03067c164da5485 [file] [log] [blame]
// Copyright 2015 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/bad_clock_blocking_page.h"
#include <utility>
#include "base/callback_helpers.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/interstitials/chrome_controller_client.h"
#include "chrome/browser/interstitials/chrome_metrics_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/ssl/cert_report_helper.h"
#include "chrome/browser/ssl/ssl_cert_reporter.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/security_interstitials/core/bad_clock_ui.h"
#include "components/security_interstitials/core/controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "content/public/browser/cert_store.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/interstitial_page_delegate.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/signed_certificate_timestamp_store.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/common/ssl_status.h"
#include "net/base/net_errors.h"
using content::InterstitialPageDelegate;
using content::NavigationController;
using content::NavigationEntry;
namespace {
const char kMetricsName[] = "bad_clock";
} // namespace
// static
InterstitialPageDelegate::TypeID BadClockBlockingPage::kTypeForTesting =
&BadClockBlockingPage::kTypeForTesting;
// Note that we always create a navigation entry with SSL errors.
// No error happening loading a sub-resource triggers an interstitial so far.
// Creating an interstitial without showing (e.g. from chrome://interstitials)
// it leaks memory, so don't create it here.
BadClockBlockingPage::BadClockBlockingPage(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
const base::Time& time_triggered,
scoped_ptr<SSLCertReporter> ssl_cert_reporter,
const base::Callback<void(bool)>& callback)
: SecurityInterstitialPage(web_contents, request_url),
callback_(callback),
ssl_info_(ssl_info),
time_triggered_(time_triggered),
controller_(new ChromeControllerClient(web_contents)) {
// Get the language for the BadClockUI.
std::string languages;
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
if (profile)
languages = profile->GetPrefs()->GetString(prefs::kAcceptLanguages);
// Set up the metrics helper for the BadClockUI.
security_interstitials::MetricsHelper::ReportDetails reporting_info;
reporting_info.metric_prefix = kMetricsName;
ChromeMetricsHelper* chrome_metrics_helper = new ChromeMetricsHelper(
web_contents, request_url, reporting_info, kMetricsName);
chrome_metrics_helper->StartRecordingCaptivePortalMetrics(false);
scoped_ptr<security_interstitials::MetricsHelper> metrics_helper(
chrome_metrics_helper);
controller_->set_metrics_helper(std::move(metrics_helper));
cert_report_helper_.reset(new CertReportHelper(
std::move(ssl_cert_reporter), web_contents, request_url, ssl_info,
certificate_reporting::ErrorReport::INTERSTITIAL_CLOCK,
false /* overridable */, controller_->metrics_helper()));
bad_clock_ui_.reset(new security_interstitials::BadClockUI(
request_url, cert_error, ssl_info, time_triggered, languages,
controller_.get()));
}
BadClockBlockingPage::~BadClockBlockingPage() {
if (!callback_.is_null()) {
// Deny when the page is closed.
NotifyDenyCertificate();
}
}
bool BadClockBlockingPage::ShouldCreateNewNavigation() const {
return true;
}
InterstitialPageDelegate::TypeID BadClockBlockingPage::GetTypeForTesting()
const {
return BadClockBlockingPage::kTypeForTesting;
}
void BadClockBlockingPage::AfterShow() {
controller_->set_interstitial_page(interstitial_page());
}
void BadClockBlockingPage::PopulateInterstitialStrings(
base::DictionaryValue* load_time_data) {
bad_clock_ui_->PopulateStringsForHTML(load_time_data);
cert_report_helper_->PopulateExtendedReportingOption(load_time_data);
}
void BadClockBlockingPage::OverrideEntry(NavigationEntry* entry) {
const int process_id = web_contents()->GetRenderProcessHost()->GetID();
const int cert_id = content::CertStore::GetInstance()->StoreCert(
ssl_info_.cert.get(), process_id);
DCHECK(cert_id);
content::SignedCertificateTimestampStore* sct_store(
content::SignedCertificateTimestampStore::GetInstance());
content::SignedCertificateTimestampIDStatusList sct_ids;
for (const auto& sct_and_status : ssl_info_.signed_certificate_timestamps) {
const int sct_id(sct_store->Store(sct_and_status.sct.get(), process_id));
DCHECK(sct_id);
sct_ids.push_back(content::SignedCertificateTimestampIDAndStatus(
sct_id, sct_and_status.status));
}
entry->GetSSL() =
content::SSLStatus(content::SECURITY_STYLE_AUTHENTICATION_BROKEN, cert_id,
sct_ids, ssl_info_);
}
void BadClockBlockingPage::SetSSLCertReporterForTesting(
scoped_ptr<SSLCertReporter> ssl_cert_reporter) {
cert_report_helper_->SetSSLCertReporterForTesting(
std::move(ssl_cert_reporter));
}
// This handles the commands sent from the interstitial JavaScript.
void BadClockBlockingPage::CommandReceived(const std::string& command) {
if (command == "\"pageLoadComplete\"") {
// content::WaitForRenderFrameReady sends this message when the page
// load completes. Ignore it.
return;
}
int cmd = 0;
bool retval = base::StringToInt(command, &cmd);
DCHECK(retval);
bad_clock_ui_->HandleCommand(
static_cast<security_interstitials::SecurityInterstitialCommands>(cmd));
}
void BadClockBlockingPage::OverrideRendererPrefs(
content::RendererPreferences* prefs) {
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
renderer_preferences_util::UpdateFromSystemSettings(prefs, profile,
web_contents());
}
void BadClockBlockingPage::OnDontProceed() {
cert_report_helper_->FinishCertCollection(
certificate_reporting::ErrorReport::USER_DID_NOT_PROCEED);
NotifyDenyCertificate();
}
void BadClockBlockingPage::NotifyDenyCertificate() {
// It's possible that callback_ may not exist if the user clicks "Proceed"
// followed by pressing the back button before the interstitial is hidden.
// In that case the certificate will still be treated as allowed.
if (callback_.is_null())
return;
base::ResetAndReturn(&callback_).Run(false);
}