diff options
author | sdefresne <sdefresne@chromium.org> | 2015-12-16 08:25:18 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-16 16:26:15 +0000 |
commit | 5eaf7d21d324c5d7a65cb757cfd517f6161dbc87 (patch) | |
tree | 46f86df126aafc4eb53c5e2d6ab24432b6ea1964 | |
parent | 5e3721ac64f5951d1844d7308ce06c4786788565 (diff) | |
download | chromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.zip chromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.tar.gz chromium_src-5eaf7d21d324c5d7a65cb757cfd517f6161dbc87.tar.bz2 |
Upstream iOS implementation of safe browsing service.
BUG=567727
Review URL: https://codereview.chromium.org/1528313002
Cr-Commit-Position: refs/heads/master@{#365538}
18 files changed, 2520 insertions, 1 deletions
diff --git a/components/components.gyp b/components/components.gyp index 57da5e7..a4b3169 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -74,6 +74,7 @@ 'query_parser.gypi', 'rappor.gypi', 'renderer_context_menu.gypi', + 'safe_browsing_db.gypi', 'search.gypi', 'search_engines.gypi', 'search_provider_logos.gypi', @@ -124,7 +125,6 @@ 'packed_ct_ev_whitelist.gypi', 'page_load_metrics.gypi', 'power.gypi', - 'safe_browsing_db.gypi', 'safe_json.gypi', 'visitedlink.gypi', 'wallpaper.gypi', diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn index 150eb46..4323ac0 100644 --- a/ios/chrome/browser/BUILD.gn +++ b/ios/chrome/browser/BUILD.gn @@ -480,6 +480,7 @@ source_set("browser") { "//components/bookmarks/managed", "//components/browser_sync/browser", "//components/browser_sync/common", + "//components/certificate_reporting", "//components/component_updater", "//components/content_settings/core/browser", "//components/cookie_config", @@ -519,6 +520,7 @@ source_set("browser") { "//components/proxy_config", "//components/rappor", "//components/resources", + "//components/safe_browsing_db", "//components/search", "//components/search_engines", "//components/security_interstitials/core", diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS index d18a618..9ca7243 100644 --- a/ios/chrome/browser/DEPS +++ b/ios/chrome/browser/DEPS @@ -8,6 +8,7 @@ include_rules = [ "+components/bookmarks", "+components/browser_sync/browser", "+components/browser_sync/common", + "+components/certificate_reporting", "+components/component_updater", "+components/content_settings/core", "+components/cookie_config", @@ -46,6 +47,7 @@ include_rules = [ "+components/proxy_config", "+components/rappor", "+components/rlz", + "+components/safe_browsing_db", "+components/search", "+components/search_engines", "+components/security_interstitials", diff --git a/ios/chrome/browser/safe_browsing/BUILD.gn b/ios/chrome/browser/safe_browsing/BUILD.gn new file mode 100644 index 0000000..6ae55a6 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/BUILD.gn @@ -0,0 +1,11 @@ +# 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. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("proto") { + sources = [ + "metadata.proto", + ] +} diff --git a/ios/chrome/browser/safe_browsing/hit_report.cc b/ios/chrome/browser/safe_browsing/hit_report.cc new file mode 100644 index 0000000..a536e97 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/hit_report.cc @@ -0,0 +1,22 @@ +// 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 "ios/chrome/browser/safe_browsing/hit_report.h" + +#include "components/metrics/metrics_service.h" +#include "ios/chrome/browser/application_context.h" + +namespace safe_browsing { + +HitReport::HitReport() {} + +HitReport::~HitReport() {} + +bool IsMetricsReportingActive() { + const metrics::MetricsService* metrics = + GetApplicationContext()->GetMetricsService(); + return metrics && metrics->reporting_active(); +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/hit_report.h b/ios/chrome/browser/safe_browsing/hit_report.h new file mode 100644 index 0000000..34aa362 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/hit_report.h @@ -0,0 +1,50 @@ +// 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. +// +// Datastructures that hold details of a Safe Browsing hit for reporting. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_ + +#include "components/safe_browsing_db/util.h" +#include "url/gurl.h" + +namespace safe_browsing { + +// What service classified this threat as unsafe. +enum class ThreatSource { + UNKNOWN, + DATA_SAVER, // From the Data Reduction service. + LOCAL_PVER3, // From LocalSafeBrowingDatabaseManager, protocol v3 + LOCAL_PVER4, // From LocalSafeBrowingDatabaseManager, protocol v4 + REMOTE, // From RemoteSafeBrowingDatabaseManager +}; + +// Data to report about the contents of a particular threat (malware, phishing, +// unsafe download URL). If post_data is non-empty, the request will be +// sent as a POST instead of a GET. +struct HitReport { + HitReport(); + ~HitReport(); + + GURL malicious_url; + GURL page_url; + GURL referrer_url; + + bool is_subresource; + SBThreatType threat_type; + ThreatSource threat_source; + bool is_extended_reporting; + bool is_metrics_reporting_active; + + std::string post_data; +}; + +// Return true if the user has opted in to UMA metrics reporting. +// Used when filling out a HitReport. +bool IsMetricsReportingActive(); + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_HIT_REPORT_H_ diff --git a/ios/chrome/browser/safe_browsing/metadata.proto b/ios/chrome/browser/safe_browsing/metadata.proto new file mode 100644 index 0000000..d37c15a --- /dev/null +++ b/ios/chrome/browser/safe_browsing/metadata.proto @@ -0,0 +1,23 @@ +// 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package safe_browsing; + +// Everything below this comment was copied from the page +// <https://developers.google.com/safe-browsing/developers_guide_v3>, +// section "Full Hash Metadata". + +// Metadata for the goog-malware-shavar list. +message MalwarePatternType { + enum PATTERN_TYPE { + LANDING = 1; + DISTRIBUTION = 2; + } + + required PATTERN_TYPE pattern_type = 1; +} diff --git a/ios/chrome/browser/safe_browsing/ping_manager.cc b/ios/chrome/browser/safe_browsing/ping_manager.cc new file mode 100644 index 0000000..ebeb344 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/ping_manager.cc @@ -0,0 +1,216 @@ +// 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 "ios/chrome/browser/safe_browsing/ping_manager.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "components/certificate_reporting/error_reporter.h" +#include "google_apis/google_api_keys.h" +#include "ios/web/public/web_thread.h" +#include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/ssl/ssl_info.h" +#include "net/url_request/certificate_report_sender.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_status.h" +#include "url/gurl.h" + +namespace { +// URLs to upload invalid certificate chain reports. The HTTP URL is +// preferred since a client seeing an invalid cert might not be able to +// make an HTTPS connection to report it. +const char kExtendedReportingUploadUrlInsecure[] = + "http://safebrowsing.googleusercontent.com/safebrowsing/clientreport/" + "chrome-certs"; +const char kExtendedReportingUploadUrlSecure[] = + "https://sb-ssl.google.com/safebrowsing/clientreport/chrome-certs"; +} // namespace + +namespace safe_browsing { + +// SafeBrowsingPingManager implementation ---------------------------------- + +// static +SafeBrowsingPingManager* SafeBrowsingPingManager::Create( + net::URLRequestContextGetter* request_context_getter, + const SafeBrowsingProtocolConfig& config) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + return new SafeBrowsingPingManager(request_context_getter, config); +} + +SafeBrowsingPingManager::SafeBrowsingPingManager( + net::URLRequestContextGetter* request_context_getter, + const SafeBrowsingProtocolConfig& config) + : client_name_(config.client_name), + request_context_getter_(request_context_getter), + url_prefix_(config.url_prefix) { + DCHECK(!url_prefix_.empty()); + + if (request_context_getter) { + // Set the upload URL and whether or not to send cookies with + // certificate reports sent to Safe Browsing servers. + bool use_insecure_certificate_upload_url = + certificate_reporting::ErrorReporter::IsHttpUploadUrlSupported(); + + net::CertificateReportSender::CookiesPreference cookies_preference; + GURL certificate_upload_url; + if (use_insecure_certificate_upload_url) { + cookies_preference = net::CertificateReportSender::DO_NOT_SEND_COOKIES; + certificate_upload_url = GURL(kExtendedReportingUploadUrlInsecure); + } else { + cookies_preference = net::CertificateReportSender::SEND_COOKIES; + certificate_upload_url = GURL(kExtendedReportingUploadUrlSecure); + } + + certificate_error_reporter_.reset(new certificate_reporting::ErrorReporter( + request_context_getter->GetURLRequestContext(), certificate_upload_url, + cookies_preference)); + } + + version_ = SafeBrowsingProtocolManagerHelper::Version(); +} + +SafeBrowsingPingManager::~SafeBrowsingPingManager() { + // Delete in-progress safebrowsing reports (hits and details). + STLDeleteContainerPointers(safebrowsing_reports_.begin(), + safebrowsing_reports_.end()); +} + +// net::URLFetcherDelegate implementation ---------------------------------- + +// All SafeBrowsing request responses are handled here. +void SafeBrowsingPingManager::OnURLFetchComplete( + const net::URLFetcher* source) { + Reports::iterator sit = safebrowsing_reports_.find(source); + DCHECK(sit != safebrowsing_reports_.end()); + delete *sit; + safebrowsing_reports_.erase(sit); +} + +// Sends a SafeBrowsing "hit" report. +void SafeBrowsingPingManager::ReportSafeBrowsingHit( + const safe_browsing::HitReport& hit_report) { + GURL report_url = SafeBrowsingHitUrl(hit_report); + net::URLFetcher* report = + net::URLFetcher::Create(report_url, hit_report.post_data.empty() + ? net::URLFetcher::GET + : net::URLFetcher::POST, + this) + .release(); + report->SetLoadFlags(net::LOAD_DISABLE_CACHE); + report->SetRequestContext(request_context_getter_.get()); + if (!hit_report.post_data.empty()) + report->SetUploadData("text/plain", hit_report.post_data); + safebrowsing_reports_.insert(report); + report->Start(); +} + +// Sends threat details for users who opt-in. +void SafeBrowsingPingManager::ReportThreatDetails(const std::string& report) { + GURL report_url = ThreatDetailsUrl(); + net::URLFetcher* fetcher = + net::URLFetcher::Create(report_url, net::URLFetcher::POST, this) + .release(); + fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE); + fetcher->SetRequestContext(request_context_getter_.get()); + fetcher->SetUploadData("application/octet-stream", report); + // Don't try too hard to send reports on failures. + fetcher->SetAutomaticallyRetryOn5xx(false); + fetcher->Start(); + safebrowsing_reports_.insert(fetcher); +} + +void SafeBrowsingPingManager::ReportInvalidCertificateChain( + const std::string& serialized_report) { + DCHECK(certificate_error_reporter_); + certificate_error_reporter_->SendExtendedReportingReport(serialized_report); +} + +void SafeBrowsingPingManager::SetCertificateErrorReporterForTesting( + scoped_ptr<certificate_reporting::ErrorReporter> + certificate_error_reporter) { + certificate_error_reporter_ = certificate_error_reporter.Pass(); +} + +GURL SafeBrowsingPingManager::SafeBrowsingHitUrl( + const safe_browsing::HitReport& hit_report) const { + DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE || + hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING || + hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED || + hit_report.threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL || + hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL || + hit_report.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL); + std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl( + url_prefix_, "report", client_name_, version_, std::string(), + hit_report.is_extended_reporting); + + std::string threat_list = "none"; + switch (hit_report.threat_type) { + case SB_THREAT_TYPE_URL_MALWARE: + threat_list = "malblhit"; + break; + case SB_THREAT_TYPE_URL_PHISHING: + threat_list = "phishblhit"; + break; + case SB_THREAT_TYPE_URL_UNWANTED: + threat_list = "uwsblhit"; + break; + case SB_THREAT_TYPE_BINARY_MALWARE_URL: + threat_list = "binurlhit"; + break; + case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL: + threat_list = "phishcsdhit"; + break; + case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL: + threat_list = "malcsdhit"; + break; + default: + NOTREACHED(); + } + + std::string threat_source = "none"; + switch (hit_report.threat_source) { + case safe_browsing::ThreatSource::DATA_SAVER: + threat_source = "ds"; + break; + case safe_browsing::ThreatSource::REMOTE: + threat_source = "rem"; + break; + case safe_browsing::ThreatSource::LOCAL_PVER3: + threat_source = "l3"; + break; + case safe_browsing::ThreatSource::LOCAL_PVER4: + threat_source = "l4"; + break; + case safe_browsing::ThreatSource::UNKNOWN: + NOTREACHED(); + } + + return GURL(base::StringPrintf( + "%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d", url.c_str(), + threat_list.c_str(), + net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(), + net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(), + net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(), + hit_report.is_subresource, threat_source.c_str(), + hit_report.is_metrics_reporting_active)); +} + +GURL SafeBrowsingPingManager::ThreatDetailsUrl() const { + std::string url = base::StringPrintf( + "%s/clientreport/malware?client=%s&appver=%s&pver=1.0", + url_prefix_.c_str(), client_name_.c_str(), version_.c_str()); + std::string api_key = google_apis::GetAPIKey(); + if (!api_key.empty()) { + base::StringAppendF(&url, "&key=%s", + net::EscapeQueryParamValue(api_key, true).c_str()); + } + return GURL(url); +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/ping_manager.h b/ios/chrome/browser/safe_browsing/ping_manager.h new file mode 100644 index 0000000..be84b4e --- /dev/null +++ b/ios/chrome/browser/safe_browsing/ping_manager.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_ + +// A class that reports safebrowsing statistics to Google's SafeBrowsing +// servers. +#include <set> +#include <string> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/safe_browsing_db/util.h" +#include "ios/chrome/browser/safe_browsing/hit_report.h" +#include "ios/chrome/browser/safe_browsing/protocol_manager_helper.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "url/gurl.h" + +namespace certificate_reporting { +class ErrorReporter; +} + +namespace net { +class SSLInfo; +class URLRequestContextGetter; +} // namespace net + +namespace safe_browsing { + +class SafeBrowsingPingManager : public net::URLFetcherDelegate { + public: + ~SafeBrowsingPingManager() override; + + // Create an instance of the safe browsing ping manager. + static SafeBrowsingPingManager* Create( + net::URLRequestContextGetter* request_context_getter, + const SafeBrowsingProtocolConfig& config); + + // net::URLFetcherDelegate interface. + void OnURLFetchComplete(const net::URLFetcher* source) override; + + // Report to Google when a SafeBrowsing warning is shown to the user. + // |hit_report.threat_type| should be one of the types known by + // SafeBrowsingtHitUrl. + void ReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report); + + // Users can opt-in on the SafeBrowsing interstitial to send detailed + // threat reports. |report| is the serialized report. + void ReportThreatDetails(const std::string& report); + + // Users can opt-in on the SSL interstitial to send reports of invalid + // certificate chains. + void ReportInvalidCertificateChain(const std::string& serialized_report); + + void SetCertificateErrorReporterForTesting( + scoped_ptr<certificate_reporting::ErrorReporter> + certificate_error_reporter); + + private: + FRIEND_TEST_ALL_PREFIXES(SafeBrowsingPingManagerTest, TestSafeBrowsingHitUrl); + FRIEND_TEST_ALL_PREFIXES(SafeBrowsingPingManagerTest, TestThreatDetailsUrl); + + typedef std::set<const net::URLFetcher*> Reports; + + // Constructs a SafeBrowsingPingManager that issues network requests + // using |request_context_getter|. + SafeBrowsingPingManager(net::URLRequestContextGetter* request_context_getter, + const SafeBrowsingProtocolConfig& config); + + // Generates URL for reporting safe browsing hits. + GURL SafeBrowsingHitUrl(const safe_browsing::HitReport& hit_report) const; + + // Generates URL for reporting threat details for users who opt-in. + GURL ThreatDetailsUrl() const; + + // Current product version sent in each request. + std::string version_; + + // The safe browsing client name sent in each request. + std::string client_name_; + + // The context we use to issue network requests. + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; + + // URL prefix where browser reports hits to the safebrowsing list and + // sends detaild threat reports for UMA users. + std::string url_prefix_; + + // Track outstanding SafeBrowsing report fetchers for clean up. + // We add both "hit" and "detail" fetchers in this set. + Reports safebrowsing_reports_; + + // Sends reports of invalid SSL certificate chains. + scoped_ptr<certificate_reporting::ErrorReporter> certificate_error_reporter_; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingPingManager); +}; + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_PING_MANAGER_H_ diff --git a/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc b/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc new file mode 100644 index 0000000..2783727 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/protocol_manager_helper.cc @@ -0,0 +1,74 @@ +// 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 "ios/chrome/browser/safe_browsing/protocol_manager_helper.h" + +#include "base/environment.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "components/version_info/version_info.h" +#include "google_apis/google_api_keys.h" +#include "net/base/escape.h" + +namespace safe_browsing { + +SafeBrowsingProtocolConfig::SafeBrowsingProtocolConfig() + : disable_auto_update(false) {} + +SafeBrowsingProtocolConfig::~SafeBrowsingProtocolConfig() {} + +// static +std::string SafeBrowsingProtocolManagerHelper::Version() { + if (version_info::GetVersionNumber().empty()) + return "0.1"; + else + return version_info::GetVersionNumber(); +} + +// static +std::string SafeBrowsingProtocolManagerHelper::ComposeUrl( + const std::string& prefix, + const std::string& method, + const std::string& client_name, + const std::string& version, + const std::string& additional_query) { + DCHECK(!prefix.empty() && !method.empty() && !client_name.empty() && + !version.empty()); + std::string url = + base::StringPrintf("%s/%s?client=%s&appver=%s&pver=3.0", prefix.c_str(), + method.c_str(), client_name.c_str(), version.c_str()); + std::string api_key = google_apis::GetAPIKey(); + if (!api_key.empty()) { + base::StringAppendF(&url, "&key=%s", + net::EscapeQueryParamValue(api_key, true).c_str()); + } + if (!additional_query.empty()) { + DCHECK(url.find("?") != std::string::npos); + url.append("&"); + url.append(additional_query); + } + return url; +} + +// static +std::string SafeBrowsingProtocolManagerHelper::ComposeUrl( + const std::string& prefix, + const std::string& method, + const std::string& client_name, + const std::string& version, + const std::string& additional_query, + bool is_extended_reporting) { + std::string url = + ComposeUrl(prefix, method, client_name, version, additional_query); + if (is_extended_reporting) { + url.append("&ext=1"); + } else { + url.append("&ext=0"); + } + return url; +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/protocol_manager_helper.h b/ios/chrome/browser/safe_browsing/protocol_manager_helper.h new file mode 100644 index 0000000..cafda86 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/protocol_manager_helper.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_ + +// A class that provides common functionality for safebrowsing protocol managers +// that communicate with Google servers. + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" + +namespace safe_browsing { + +struct SafeBrowsingProtocolConfig { + SafeBrowsingProtocolConfig(); + ~SafeBrowsingProtocolConfig(); + std::string client_name; + std::string url_prefix; + std::string backup_connect_error_url_prefix; + std::string backup_http_error_url_prefix; + std::string backup_network_error_url_prefix; + std::string version; + bool disable_auto_update; +}; + +class SafeBrowsingProtocolManagerHelper { + public: + // returns version + static std::string Version(); + + // Composes a URL using |prefix|, |method| (e.g.: gethash, download, report). + // |client_name| and |version|. When not empty, |additional_query| is + // appended to the URL with an additional "&" in the front. + static std::string ComposeUrl(const std::string& prefix, + const std::string& method, + const std::string& client_name, + const std::string& version, + const std::string& additional_query); + + // Similar to above function, and appends "&ext=1" at the end of URL if + // |is_extended_reporting| is true, otherwise, appends "&ext=0". + static std::string ComposeUrl(const std::string& prefix, + const std::string& method, + const std::string& client_name, + const std::string& version, + const std::string& additional_query, + bool is_extended_reporting); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SafeBrowsingProtocolManagerHelper); +}; + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_PROTOCOL_MANAGER_HELPER_H_ diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc new file mode 100644 index 0000000..ad3ef67 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc @@ -0,0 +1,592 @@ +// 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. +// +// Implementation of the SafeBrowsingBlockingPage class. + +#include "ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h" + +#include <string> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/i18n/rtl.h" +#include "base/lazy_instance.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/values.h" +#include "components/google/core/browser/google_util.h" +#include "components/security_interstitials/core/controller_client.h" +#include "ios/chrome/browser/application_context.h" +#include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/interstitials/ios_chrome_metrics_helper.h" +#include "ios/chrome/browser/pref_names.h" +#include "ios/chrome/browser/safe_browsing/ui_manager.h" +#include "ios/chrome/grit/ios_strings.h" +#include "ios/web/public/interstitials/web_interstitial.h" +#include "ios/web/public/navigation_manager.h" +#include "ios/web/public/web_state/web_state.h" +#include "ios/web/public/web_state/web_state.h" +#include "ios/web/public/web_thread.h" +#include "net/base/escape.h" +#include "ui/base/l10n/l10n_util.h" + +namespace safe_browsing { + +namespace { + +// For malware interstitial pages, we link the problematic URL to Google's +// diagnostic page. +#if defined(GOOGLE_CHROME_BUILD) +const char kSbDiagnosticUrl[] = + "https://www.google.com/safebrowsing/" + "diagnostic?site=%s&client=googlechrome"; +#else +const char kSbDiagnosticUrl[] = + "https://www.google.com/safebrowsing/diagnostic?site=%s&client=chromium"; +#endif + +// URL for malware and phishing, V2. +const char kLearnMoreMalwareUrlV2[] = + "https://www.google.com/transparencyreport/safebrowsing/"; +const char kLearnMorePhishingUrlV2[] = + "https://www.google.com/transparencyreport/safebrowsing/"; + +// Constants for the V4 phishing string upgrades. +const char kSocialEngineeringTrial[] = "SafeBrowsingSocialEngineeringStrings"; +const char kSocialEngineeringEnabled[] = "Enabled"; + +// Constants for the Experience Sampling instrumentation. +const char kEventNameMalware[] = "safebrowsing_interstitial_"; +const char kEventNameHarmful[] = "harmful_interstitial_"; +const char kEventNamePhishing[] = "phishing_interstitial_"; +const char kEventNameOther[] = "safebrowsing_other_interstitial_"; + +// Constants for the V4 phishing string upgrades. +const char kReportPhishingErrorUrl[] = + "https://www.google.com/safebrowsing/report_error/"; +const char kReportPhishingErrorTrial[] = "SafeBrowsingReportPhishingErrorLink"; +const char kReportPhishingErrorEnabled[] = "Enabled"; + +base::LazyInstance<SafeBrowsingBlockingPage::UnsafeResourceMap> + g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = nullptr; + +// The default SafeBrowsingBlockingPageFactory. Global, made a singleton so we +// don't leak it. +class SafeBrowsingBlockingPageFactoryImpl + : public SafeBrowsingBlockingPageFactory { + public: + SafeBrowsingBlockingPage* CreateSafeBrowsingPage( + SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) + override { + return new SafeBrowsingBlockingPage(ui_manager, web_state, + unsafe_resources); + } + + private: + friend struct base::DefaultLazyInstanceTraits< + SafeBrowsingBlockingPageFactoryImpl>; + + SafeBrowsingBlockingPageFactoryImpl() {} + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl); +}; + +static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl> + g_safe_browsing_blocking_page_factory_impl = LAZY_INSTANCE_INITIALIZER; + +SafeBrowsingBlockingPage::SafeBrowsingBlockingPage( + SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const UnsafeResourceList& unsafe_resources) + : IOSSecurityInterstitialPage(web_state, unsafe_resources[0].url), + ui_manager_(ui_manager), + is_main_frame_load_blocked_(IsMainPageLoadBlocked(unsafe_resources)), + unsafe_resources_(unsafe_resources), + proceeded_(false) { + bool malware = false; + bool harmful = false; + bool phishing = false; + for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin(); + iter != unsafe_resources_.end(); ++iter) { + const UnsafeResource& resource = *iter; + SBThreatType threat_type = resource.threat_type; + if (threat_type == SB_THREAT_TYPE_URL_MALWARE || + threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) { + malware = true; + } else if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) { + harmful = true; + } else { + DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING || + threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL); + phishing = true; + } + } + DCHECK(phishing || malware || harmful); + if (malware) + interstitial_reason_ = SB_REASON_MALWARE; + else if (harmful) + interstitial_reason_ = SB_REASON_HARMFUL; + else + interstitial_reason_ = SB_REASON_PHISHING; + + // This must be done after calculating |interstitial_reason_| above. + security_interstitials::MetricsHelper::ReportDetails reporting_info; + reporting_info.metric_prefix = GetMetricPrefix(); + reporting_info.extra_suffix = GetExtraMetricsSuffix(); + reporting_info.rappor_prefix = GetRapporPrefix(); + reporting_info.rappor_report_type = rappor::SAFEBROWSING_RAPPOR_TYPE; + SetMetricsHelper(make_scoped_ptr( + new IOSChromeMetricsHelper(web_state, request_url(), reporting_info))); + GetMetricsHelper()->RecordUserDecision( + security_interstitials::MetricsHelper::SHOW); + GetMetricsHelper()->RecordUserInteraction( + security_interstitials::MetricsHelper::TOTAL_VISITS); + if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) { + GetMetricsHelper()->RecordUserDecision( + security_interstitials::MetricsHelper::PROCEEDING_DISABLED); + } + + if (!is_main_frame_load_blocked_) { + navigation_entry_index_to_remove_ = + web_state->GetNavigationManager()->GetLastCommittedItemIndex(); + } else { + navigation_entry_index_to_remove_ = -1; + } +} + +SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {} + +void SafeBrowsingBlockingPage::CommandReceived(const std::string& page_cmd) { + if (page_cmd == "\"pageLoadComplete\"") { + // content::WaitForRenderFrameReady sends this message when the page + // load completes. Ignore it. + return; + } + + int command = 0; + bool retval = base::StringToInt(page_cmd, &command); + DCHECK(retval) << page_cmd; + + switch (command) { + case security_interstitials::CMD_DO_REPORT: { + // User enabled SB Extended Reporting via the checkbox. + SetReportingPreference(true); + break; + } + case security_interstitials::CMD_DONT_REPORT: { + // User disabled SB Extended Reporting via the checkbox. + SetReportingPreference(false); + break; + } + case security_interstitials::CMD_OPEN_HELP_CENTER: { + // User pressed "Learn more". + GetMetricsHelper()->RecordUserInteraction( + security_interstitials::MetricsHelper::SHOW_LEARN_MORE); + GURL learn_more_url(interstitial_reason_ == SB_REASON_PHISHING + ? kLearnMorePhishingUrlV2 + : kLearnMoreMalwareUrlV2); + learn_more_url = google_util::AppendGoogleLocaleParam( + learn_more_url, GetApplicationContext()->GetApplicationLocale()); + web_state()->OpenURL(web::WebState::OpenURLParams( + learn_more_url, web::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_LINK, false)); + break; + } + case security_interstitials::CMD_OPEN_REPORTING_PRIVACY: { + // User pressed on the SB Extended Reporting "privacy policy" link. + OpenExtendedReportingPrivacyPolicy(); + break; + } + case security_interstitials::CMD_PROCEED: { + // User pressed on the button to proceed. + if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) { + GetMetricsHelper()->RecordUserDecision( + security_interstitials::MetricsHelper::PROCEED); + web_interstitial()->Proceed(); + // |this| has been deleted after Proceed() returns. + break; + } + // If the user can't proceed, fall through to CMD_DONT_PROCEED. + } + case security_interstitials::CMD_DONT_PROCEED: { + // User pressed on the button to return to safety. + // Don't record the user action here because there are other ways of + // triggering DontProceed, like clicking the back button. + if (is_main_frame_load_blocked_) { + // If the load is blocked, we want to close the interstitial and discard + // the pending entry. + web_interstitial()->DontProceed(); + // |this| has been deleted after DontProceed() returns. + break; + } + + // Otherwise the offending entry has committed, and we need to go back or + // to a safe page. We will close the interstitial when that page commits. + if (web_state()->GetNavigationManager()->CanGoBack()) { + web_state()->GetNavigationManager()->GoBack(); + } else { + web_state()->OpenURL(web::WebState::OpenURLParams( + GURL(kChromeUINewTabURL), web::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false)); + } + break; + } + case security_interstitials::CMD_OPEN_DIAGNOSTIC: { + // User wants to see why this page is blocked. + const UnsafeResource& unsafe_resource = unsafe_resources_[0]; + std::string bad_url_spec = unsafe_resource.url.spec(); + GetMetricsHelper()->RecordUserInteraction( + security_interstitials::MetricsHelper::SHOW_DIAGNOSTIC); + std::string diagnostic = base::StringPrintf( + kSbDiagnosticUrl, + net::EscapeQueryParamValue(bad_url_spec, true).c_str()); + GURL diagnostic_url(diagnostic); + diagnostic_url = google_util::AppendGoogleLocaleParam( + diagnostic_url, GetApplicationContext()->GetApplicationLocale()); + DCHECK(unsafe_resource.threat_type == SB_THREAT_TYPE_URL_MALWARE || + unsafe_resource.threat_type == + SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL || + unsafe_resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED); + web_state()->OpenURL(web::WebState::OpenURLParams( + diagnostic_url, web::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_LINK, false)); + break; + } + case security_interstitials::CMD_SHOW_MORE_SECTION: { + // User has opened up the hidden text. + GetMetricsHelper()->RecordUserInteraction( + security_interstitials::MetricsHelper::SHOW_ADVANCED); + break; + } + case security_interstitials::CMD_REPORT_PHISHING_ERROR: { + // User wants to report a phishing error. + GetMetricsHelper()->RecordUserInteraction( + security_interstitials::MetricsHelper::REPORT_PHISHING_ERROR); + GURL phishing_error_url(kReportPhishingErrorUrl); + phishing_error_url = google_util::AppendGoogleLocaleParam( + phishing_error_url, GetApplicationContext()->GetApplicationLocale()); + web_state()->OpenURL(web::WebState::OpenURLParams( + phishing_error_url, web::Referrer(), CURRENT_TAB, + ui::PAGE_TRANSITION_LINK, false)); + break; + } + } +} + +void SafeBrowsingBlockingPage::OnProceed() { + proceeded_ = true; + ui_manager_->OnBlockingPageDone(unsafe_resources_, true); + + // Check to see if some new notifications of unsafe resources have been + // received while we were showing the interstitial. + UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap(); + UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_state()); + SafeBrowsingBlockingPage* blocking_page = nullptr; + if (iter != unsafe_resource_map->end() && !iter->second.empty()) { + // Build an interstitial for all the unsafe resources notifications. + // Don't show it now as showing an interstitial while an interstitial is + // already showing would cause DontProceed() to be invoked. + blocking_page = factory_->CreateSafeBrowsingPage(ui_manager_, web_state(), + iter->second); + unsafe_resource_map->erase(iter); + } + + // Now that this interstitial is gone, we can show the new one. + if (blocking_page) + blocking_page->Show(); +} + +bool SafeBrowsingBlockingPage::ShouldCreateNewNavigation() const { + return is_main_frame_load_blocked_; +} + +void SafeBrowsingBlockingPage::OnDontProceed() { + // We could have already called Proceed(), in which case we must not notify + // the SafeBrowsingUIManager again, as the client has been deleted. + if (proceeded_) + return; + + if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) { + GetMetricsHelper()->RecordUserDecision( + security_interstitials::MetricsHelper::DONT_PROCEED); + } + + ui_manager_->OnBlockingPageDone(unsafe_resources_, false); + + // The user does not want to proceed, clear the queued unsafe resources + // notifications we received while the interstitial was showing. + UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap(); + UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_state()); + if (iter != unsafe_resource_map->end() && !iter->second.empty()) { + ui_manager_->OnBlockingPageDone(iter->second, false); + unsafe_resource_map->erase(iter); + } + + // We don't remove the navigation entry if the tab is being destroyed as this + // would trigger a navigation that would cause trouble as the render view host + // for the tab has by then already been destroyed. We also don't delete the + // current entry if it has been committed again, which is possible on a page + // that had a subresource warning. + int last_committed_index = + web_state()->GetNavigationManager()->GetLastCommittedItemIndex(); + if (navigation_entry_index_to_remove_ != -1 && + navigation_entry_index_to_remove_ != last_committed_index && + !web_state()->IsBeingDestroyed()) { + CHECK(web_state()->GetNavigationManager()->RemoveItemAtIndex( + navigation_entry_index_to_remove_)); + navigation_entry_index_to_remove_ = -1; + } +} + +// static +SafeBrowsingBlockingPage::UnsafeResourceMap* +SafeBrowsingBlockingPage::GetUnsafeResourcesMap() { + return g_unsafe_resource_map.Pointer(); +} + +// static +SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage( + SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const UnsafeResource& unsafe_resource) { + std::vector<UnsafeResource> resources; + resources.push_back(unsafe_resource); + // Set up the factory if this has not been done already (tests do that + // before this method is called). + if (!factory_) + factory_ = g_safe_browsing_blocking_page_factory_impl.Pointer(); + return factory_->CreateSafeBrowsingPage(ui_manager, web_state, resources); +} + +// static +void SafeBrowsingBlockingPage::ShowBlockingPage( + web::WebState* web_state, + SafeBrowsingUIManager* ui_manager, + const UnsafeResource& unsafe_resource) { + DVLOG(1) << __FUNCTION__ << " " << unsafe_resource.url.spec(); + web::WebInterstitial* web_interstitial = + web::WebInterstitial::GetWebInterstitial(web_state); + if (web_interstitial && !unsafe_resource.is_subresource) { + // There is already an interstitial showing and we are about to display a + // new one for the main frame. Just hide the current one, it is now + // irrelevent + web_interstitial->DontProceed(); + web_interstitial = nullptr; + } + + if (!web_interstitial) { + // There are no interstitial currently showing in that tab, go ahead and + // show this interstitial. + SafeBrowsingBlockingPage* blocking_page = + CreateBlockingPage(ui_manager, web_state, unsafe_resource); + blocking_page->Show(); + return; + } + + // This is an interstitial for a page's resource, let's queue it. + UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap(); + (*unsafe_resource_map)[web_state].push_back(unsafe_resource); +} + +// static +bool SafeBrowsingBlockingPage::IsMainPageLoadBlocked( + const UnsafeResourceList& unsafe_resources) { + // If there is more than one unsafe resource, the main page load must not be + // blocked. Otherwise, check if the one resource is. + return unsafe_resources.size() == 1 && + unsafe_resources[0].IsMainPageLoadBlocked(); +} + +std::string SafeBrowsingBlockingPage::GetMetricPrefix() const { + bool primary_subresource = unsafe_resources_[0].is_subresource; + switch (interstitial_reason_) { + case SB_REASON_MALWARE: + return primary_subresource ? "malware_subresource" : "malware"; + case SB_REASON_HARMFUL: + return primary_subresource ? "harmful_subresource" : "harmful"; + case SB_REASON_PHISHING: + return primary_subresource ? "phishing_subresource" : "phishing"; + } + NOTREACHED(); + return std::string(); +} + +// We populate a parallel set of metrics to differentiate some threat sources. +std::string SafeBrowsingBlockingPage::GetExtraMetricsSuffix() const { + switch (unsafe_resources_[0].threat_source) { + case safe_browsing::ThreatSource::DATA_SAVER: + return "from_data_saver"; + case safe_browsing::ThreatSource::REMOTE: + case safe_browsing::ThreatSource::LOCAL_PVER3: + // REMOTE and LOCAL_PVER3 can be distinguished in the logs + // by platform type: Remote is mobile, local_pver3 is desktop. + return "from_device"; + case safe_browsing::ThreatSource::LOCAL_PVER4: + return "from_device_v4"; + case safe_browsing::ThreatSource::UNKNOWN: + break; + } + NOTREACHED(); + return std::string(); +} + +std::string SafeBrowsingBlockingPage::GetRapporPrefix() const { + switch (interstitial_reason_) { + case SB_REASON_MALWARE: + return "malware"; + case SB_REASON_HARMFUL: + return "harmful"; + case SB_REASON_PHISHING: + return "phishing"; + } + NOTREACHED(); + return std::string(); +} + +std::string SafeBrowsingBlockingPage::GetSamplingEventName() const { + switch (interstitial_reason_) { + case SB_REASON_MALWARE: + return kEventNameMalware; + case SB_REASON_HARMFUL: + return kEventNameHarmful; + case SB_REASON_PHISHING: + return kEventNamePhishing; + default: + return kEventNameOther; + } +} + +void SafeBrowsingBlockingPage::PopulateInterstitialStrings( + base::DictionaryValue* load_time_data) const { + CHECK(load_time_data); + CHECK(!unsafe_resources_.empty()); + + load_time_data->SetString("type", "SAFEBROWSING"); + load_time_data->SetString( + "tabTitle", l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_TITLE)); + load_time_data->SetString( + "openDetails", + l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON)); + load_time_data->SetString( + "closeDetails", + l10n_util::GetStringUTF16(IDS_IOS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON)); + load_time_data->SetString( + "primaryButtonText", l10n_util::GetStringUTF16( + IDS_IOS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON)); + // TODO(crbug.com/390675): Undo this forkage. This is a temporary fix to make + // sure the broken proceed-from-unsafe-resource path can't be hit on iOS. + // Always set subresource alerts to be non-overridable. Otherwise obey the + // global pref. + bool overridable = !unsafe_resources_[0].is_subresource && + !IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled); + load_time_data->SetBoolean("overridable", overridable); + + switch (interstitial_reason_) { + case SB_REASON_MALWARE: + PopulateMalwareLoadTimeData(load_time_data); + break; + case SB_REASON_HARMFUL: + PopulateHarmfulLoadTimeData(load_time_data); + break; + case SB_REASON_PHISHING: + PopulatePhishingLoadTimeData(load_time_data); + break; + } +} + +void SafeBrowsingBlockingPage::PopulateMalwareLoadTimeData( + base::DictionaryValue* load_time_data) const { + load_time_data->SetBoolean("phishing", false); + load_time_data->SetString( + "heading", l10n_util::GetStringUTF16(IDS_IOS_MALWARE_V3_HEADING)); + load_time_data->SetString( + "primaryParagraph", + l10n_util::GetStringFUTF16(IDS_IOS_MALWARE_V3_PRIMARY_PARAGRAPH, + GetFormattedHostName())); + load_time_data->SetString( + "explanationParagraph", + is_main_frame_load_blocked_ + ? l10n_util::GetStringFUTF16(IDS_IOS_MALWARE_V3_EXPLANATION_PARAGRAPH, + GetFormattedHostName()) + : l10n_util::GetStringFUTF16( + IDS_IOS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE, + base::UTF8ToUTF16(web_state()->GetVisibleURL().host()), + GetFormattedHostName())); + load_time_data->SetString( + "finalParagraph", + l10n_util::GetStringUTF16(IDS_IOS_MALWARE_V3_PROCEED_PARAGRAPH)); + + load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false); +} + +void SafeBrowsingBlockingPage::PopulateHarmfulLoadTimeData( + base::DictionaryValue* load_time_data) const { + load_time_data->SetBoolean("phishing", false); + load_time_data->SetString( + "heading", l10n_util::GetStringUTF16(IDS_IOS_HARMFUL_V3_HEADING)); + load_time_data->SetString( + "primaryParagraph", + l10n_util::GetStringFUTF16(IDS_IOS_HARMFUL_V3_PRIMARY_PARAGRAPH, + GetFormattedHostName())); + load_time_data->SetString( + "explanationParagraph", + l10n_util::GetStringFUTF16(IDS_IOS_HARMFUL_V3_EXPLANATION_PARAGRAPH, + GetFormattedHostName())); + load_time_data->SetString( + "finalParagraph", + l10n_util::GetStringUTF16(IDS_IOS_HARMFUL_V3_PROCEED_PARAGRAPH)); + + load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false); +} + +void SafeBrowsingBlockingPage::PopulatePhishingLoadTimeData( + base::DictionaryValue* load_time_data) const { + bool use_social_engineering_strings = + base::FieldTrialList::FindFullName(kSocialEngineeringTrial) == + kSocialEngineeringEnabled; + load_time_data->SetBoolean("phishing", true); + load_time_data->SetString( + "heading", l10n_util::GetStringUTF16(use_social_engineering_strings + ? IDS_IOS_PHISHING_V4_HEADING + : IDS_IOS_PHISHING_V3_HEADING)); + load_time_data->SetString( + "primaryParagraph", + l10n_util::GetStringFUTF16(use_social_engineering_strings + ? IDS_IOS_PHISHING_V4_PRIMARY_PARAGRAPH + : IDS_IOS_PHISHING_V3_PRIMARY_PARAGRAPH, + GetFormattedHostName())); + load_time_data->SetString( + "explanationParagraph", + l10n_util::GetStringFUTF16(IDS_IOS_PHISHING_V3_EXPLANATION_PARAGRAPH, + GetFormattedHostName())); + + if (base::FieldTrialList::FindFullName(kReportPhishingErrorTrial) == + kReportPhishingErrorEnabled) { + load_time_data->SetString( + "finalParagraph", + l10n_util::GetStringUTF16( + IDS_IOS_PHISHING_V4_PROCEED_AND_REPORT_PARAGRAPH)); + } else { + load_time_data->SetString( + "finalParagraph", + l10n_util::GetStringUTF16(IDS_IOS_PHISHING_V3_PROCEED_PARAGRAPH)); + } + + load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false); +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h new file mode 100644 index 0000000..cfc2828 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h @@ -0,0 +1,167 @@ +// 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. +// +// Classes for managing the SafeBrowsing interstitial pages. +// +// When a user is about to visit a page the SafeBrowsing system has deemed to +// be malicious, either as malware or a phishing page, we show an interstitial +// page with some options (go back, continue) to give the user a chance to avoid +// the harmful page. +// +// The SafeBrowsingBlockingPage is created by the SafeBrowsingUIManager on the +// UI thread when we've determined that a page is malicious. The operation of +// the blocking page occurs on the UI thread, where it waits for the user to +// make a decision about what to do: either go back or continue on. +// +// The blocking page forwards the result of the user's choice back to the +// SafeBrowsingUIManager so that we can cancel the request for the new page, +// or allow it to continue. +// +// A web page may contain several resources flagged as malware/phishing. This +// results into more than one interstitial being shown. On the first unsafe +// resource received we show an interstitial. Any subsequent unsafe resource +// notifications while the first interstitial is showing is queued. If the user +// decides to proceed in the first interstitial, we display all queued unsafe +// resources in a new interstitial. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "ios/chrome/browser/interstitials/ios_security_interstitial_page.h" +#include "ios/chrome/browser/safe_browsing/ui_manager.h" +#include "url/gurl.h" + +namespace web { +class WebState; +} + +namespace safe_browsing { + +class SafeBrowsingBlockingPageFactory; + +class SafeBrowsingBlockingPage : public IOSSecurityInterstitialPage { + public: + typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource; + typedef std::vector<UnsafeResource> UnsafeResourceList; + typedef std::map<web::WebState*, UnsafeResourceList> UnsafeResourceMap; + + ~SafeBrowsingBlockingPage() override; + + // Creates a blocking page. Use ShowBlockingPage if you don't need to access + // the blocking page directly. + static SafeBrowsingBlockingPage* CreateBlockingPage( + SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const UnsafeResource& unsafe_resource); + + // Shows a blocking page warning the user about phishing/malware for a + // specific resource. + // You can call this method several times, if an interstitial is already + // showing, the new one will be queued and displayed if the user decides + // to proceed on the currently showing interstitial. + static void ShowBlockingPage(web::WebState* web_state, + SafeBrowsingUIManager* ui_manager, + const UnsafeResource& resource); + + // Makes the passed |factory| the factory used to instantiate + // SafeBrowsingBlockingPage objects. Useful for tests. + static void RegisterFactory(SafeBrowsingBlockingPageFactory* factory) { + factory_ = factory; + } + + // InterstitialPageDelegate method: + void OnProceed() override; + void OnDontProceed() override; + void CommandReceived(const std::string& command) override; + + protected: + void UpdateReportingPref(); // Used for the transition from old to new pref. + + // Don't instantiate this class directly, use ShowBlockingPage instead. + SafeBrowsingBlockingPage(SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const UnsafeResourceList& unsafe_resources); + + // IOSSecurityInterstitialPage methods: + bool ShouldCreateNewNavigation() const override; + void PopulateInterstitialStrings( + base::DictionaryValue* load_time_data) const override; + void AfterShow() override {} + + // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user + // should be warned about. They are queued when displaying more than one + // interstitial at a time. + static UnsafeResourceMap* GetUnsafeResourcesMap(); + + // Returns true if the passed |unsafe_resources| is blocking the load of + // the main page. + static bool IsMainPageLoadBlocked(const UnsafeResourceList& unsafe_resources); + + friend class SafeBrowsingBlockingPageFactoryImpl; + + // For reporting back user actions. + SafeBrowsingUIManager* ui_manager_; + + // True if the interstitial is blocking the main page because it is on one + // of our lists. False if a subresource is being blocked, or in the case of + // client-side detection where the interstitial is shown after page load + // finishes. + bool is_main_frame_load_blocked_; + + // The index of a navigation entry that should be removed when DontProceed() + // is invoked, -1 if not entry should be removed. + int navigation_entry_index_to_remove_; + + // The list of unsafe resources this page is warning about. + UnsafeResourceList unsafe_resources_; + + bool proceeded_; + + // Which type of Safe Browsing interstitial this is. + enum SBInterstitialReason { + SB_REASON_MALWARE, + SB_REASON_HARMFUL, + SB_REASON_PHISHING, + } interstitial_reason_; + + // The factory used to instantiate SafeBrowsingBlockingPage objects. + // Useful for tests, so they can provide their own implementation of + // SafeBrowsingBlockingPage. + static SafeBrowsingBlockingPageFactory* factory_; + + private: + // Fills the passed dictionary with the values to be passed to the template + // when creating the HTML. + void PopulateMalwareLoadTimeData(base::DictionaryValue* load_time_data) const; + void PopulateHarmfulLoadTimeData(base::DictionaryValue* load_time_data) const; + void PopulatePhishingLoadTimeData( + base::DictionaryValue* load_time_data) const; + + std::string GetMetricPrefix() const; + std::string GetExtraMetricsSuffix() const; + std::string GetRapporPrefix() const; + std::string GetSamplingEventName() const; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPage); +}; + +// Factory for creating SafeBrowsingBlockingPage. Useful for tests. +class SafeBrowsingBlockingPageFactory { + public: + virtual ~SafeBrowsingBlockingPageFactory() {} + + virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage( + SafeBrowsingUIManager* ui_manager, + web::WebState* web_state, + const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) = 0; +}; + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_BLOCKING_PAGE_H_ diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_service.cc b/ios/chrome/browser/safe_browsing/safe_browsing_service.cc new file mode 100644 index 0000000..2427cf7 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -0,0 +1,434 @@ +// 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 "ios/chrome/browser/safe_browsing/safe_browsing_service.h" + +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/lazy_instance.h" +#include "base/metrics/histogram_macros.h" +#include "base/path_service.h" +#include "base/prefs/pref_change_registrar.h" +#include "base/prefs/pref_service.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "ios/chrome/browser/application_context.h" +#include "ios/chrome/browser/chrome_paths.h" +#include "ios/chrome/browser/chrome_switches.h" +#include "ios/chrome/browser/pref_names.h" +#include "ios/chrome/browser/safe_browsing/ping_manager.h" +#include "ios/chrome/browser/safe_browsing/ui_manager.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state_manager.h" +#include "ios/web/public/web_thread.h" +#include "net/cookies/cookie_monster.h" +#include "net/extras/sqlite/cookie_crypto_delegate.h" +#include "net/extras/sqlite/sqlite_persistent_cookie_store.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" + +namespace safe_browsing { + +namespace { + +const base::FilePath::CharType kIOSChromeSafeBrowsingBaseFilename[] = + FILE_PATH_LITERAL("Safe Browsing"); + +// Filename suffix for the cookie database. +const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies"); + +// The default URL prefix where browser fetches chunk updates, hashes, +// and reports safe browsing hits and malware details. +const char kSbDefaultURLPrefix[] = + "https://safebrowsing.google.com/safebrowsing"; + +// The backup URL prefix used when there are issues establishing a connection +// with the server at the primary URL. +const char kSbBackupConnectErrorURLPrefix[] = + "https://alt1-safebrowsing.google.com/safebrowsing"; + +// The backup URL prefix used when there are HTTP-specific issues with the +// server at the primary URL. +const char kSbBackupHttpErrorURLPrefix[] = + "https://alt2-safebrowsing.google.com/safebrowsing"; + +// The backup URL prefix used when there are local network specific issues. +const char kSbBackupNetworkErrorURLPrefix[] = + "https://alt3-safebrowsing.google.com/safebrowsing"; + +} // namespace + +class SafeBrowsingURLRequestContextGetter + : public net::URLRequestContextGetter { + public: + explicit SafeBrowsingURLRequestContextGetter( + SafeBrowsingService* sb_service_); + + // Implementation for net::UrlRequestContextGetter. + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() + const override; + + // Shuts down any pending requests using the getter, and nulls out + // |sb_service_|. + void SafeBrowsingServiceShuttingDown(); + + protected: + ~SafeBrowsingURLRequestContextGetter() override; + + private: + SafeBrowsingService* sb_service_; // Owned by BrowserProcess. + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; +}; + +SafeBrowsingURLRequestContextGetter::SafeBrowsingURLRequestContextGetter( + SafeBrowsingService* sb_service) + : sb_service_(sb_service), + network_task_runner_( + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)) {} + +net::URLRequestContext* +SafeBrowsingURLRequestContextGetter::GetURLRequestContext() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + // Check if the service has been shut down. + if (!sb_service_) + return nullptr; + + DCHECK(sb_service_->url_request_context_.get()); + return sb_service_->url_request_context_.get(); +} + +scoped_refptr<base::SingleThreadTaskRunner> +SafeBrowsingURLRequestContextGetter::GetNetworkTaskRunner() const { + return network_task_runner_; +} + +void SafeBrowsingURLRequestContextGetter::SafeBrowsingServiceShuttingDown() { + sb_service_ = nullptr; + URLRequestContextGetter::NotifyContextShuttingDown(); +} + +SafeBrowsingURLRequestContextGetter::~SafeBrowsingURLRequestContextGetter() {} + +// static +SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = nullptr; + +// The default SafeBrowsingServiceFactory. Global, made a singleton so we +// don't leak it. +class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory { + public: + SafeBrowsingService* CreateSafeBrowsingService() override { + return new SafeBrowsingService(); + } + + private: + friend struct base::DefaultLazyInstanceTraits<SafeBrowsingServiceFactoryImpl>; + + SafeBrowsingServiceFactoryImpl() {} + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl); +}; + +static base::LazyInstance<SafeBrowsingServiceFactoryImpl>::Leaky + g_safe_browsing_service_factory_impl = LAZY_INSTANCE_INITIALIZER; + +// static +base::FilePath SafeBrowsingService::GetBaseFilename() { + base::FilePath path; + bool result = PathService::Get(ios::DIR_USER_DATA, &path); + DCHECK(result); + return path.Append(kIOSChromeSafeBrowsingBaseFilename); +} + +// static +SafeBrowsingService* SafeBrowsingService::CreateSafeBrowsingService() { + if (!factory_) + factory_ = g_safe_browsing_service_factory_impl.Pointer(); + return factory_->CreateSafeBrowsingService(); +} + +SafeBrowsingService::SafeBrowsingService() + : ping_manager_(nullptr), enabled_(false), enabled_by_prefs_(false) {} + +SafeBrowsingService::~SafeBrowsingService() { + // We should have already been shut down. If we're still enabled, then the + // database isn't going to be closed properly, which could lead to corruption. + DCHECK(!enabled_); +} + +void SafeBrowsingService::Initialize() { + url_request_context_getter_ = new SafeBrowsingURLRequestContextGetter(this); + + ui_manager_ = CreateUIManager(); + + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingService::InitURLRequestContextOnIOThread, this, + make_scoped_refptr( + GetApplicationContext()->GetSystemURLRequestContext()))); + + // Track the safe browsing preference of existing browser states. + // The SafeBrowsingService will be started if any existing browser state has + // the preference enabled. It will also listen for updates to the preferences. + ios::ChromeBrowserStateManager* browser_state_manager = + GetApplicationContext()->GetChromeBrowserStateManager(); + DCHECK(browser_state_manager); + + std::vector<ios::ChromeBrowserState*> browser_states = + browser_state_manager->GetLoadedBrowserStates(); + DCHECK_GT(browser_states.size(), 0u); + for (ios::ChromeBrowserState* browser_state : browser_states) { + if (browser_state->IsOffTheRecord()) + continue; + AddPrefService(browser_state->GetPrefs()); + } +} + +void SafeBrowsingService::ShutDown() { + // Deletes the PrefChangeRegistrars, whose dtors also unregister |this| as an + // observer of the preferences. + STLDeleteValues(&prefs_map_); + + Stop(true); + + web::WebThread::PostNonNestableTask( + web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingService::DestroyURLRequestContextOnIOThread, this, + url_request_context_getter_)); + + // Release the URLRequestContextGetter after passing it to the IOThread. It + // has to be released now rather than in the destructor because it can only + // be deleted on the IOThread, and the SafeBrowsingService outlives the IO + // thread. + url_request_context_getter_ = nullptr; +} + +// Binhash verification is only enabled for UMA users for now. +bool SafeBrowsingService::DownloadBinHashNeeded() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + + return false; +} + +net::URLRequestContextGetter* SafeBrowsingService::url_request_context() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + return url_request_context_getter_.get(); +} + +const scoped_refptr<SafeBrowsingUIManager>& SafeBrowsingService::ui_manager() + const { + return ui_manager_; +} + +SafeBrowsingPingManager* SafeBrowsingService::ping_manager() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + return ping_manager_; +} + +SBThreatType SafeBrowsingService::CheckResponseFromProxyRequestHeaders( + scoped_refptr<net::HttpResponseHeaders> headers) { + SBThreatType result = SB_THREAT_TYPE_SAFE; + + if (!headers.get()) + return result; + + // If safebrowsing service is disabled, don't do any check. + if (!enabled()) + return result; + + if (headers->HasHeader("X-Phishing-Url")) + result = SB_THREAT_TYPE_URL_PHISHING; + else if (headers->HasHeader("X-Malware-Url")) + result = SB_THREAT_TYPE_URL_MALWARE; + + return result; +} + +SafeBrowsingUIManager* SafeBrowsingService::CreateUIManager() { + return new SafeBrowsingUIManager(this); +} + +void SafeBrowsingService::InitURLRequestContextOnIOThread( + net::URLRequestContextGetter* system_url_request_context_getter) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + DCHECK(!url_request_context_.get()); + + scoped_refptr<net::SQLitePersistentCookieStore> sqlite_store( + new net::SQLitePersistentCookieStore( + base::FilePath(GetBaseFilename().value() + kCookiesFile), + web::WebThread::GetTaskRunnerForThread(web::WebThread::IO), + web::WebThread::GetBlockingPool()->GetSequencedTaskRunner( + web::WebThread::GetBlockingPool()->GetSequenceToken()), + false, nullptr)); + scoped_refptr<net::CookieStore> cookie_store = + new net::CookieMonster(sqlite_store.get(), nullptr); + + url_request_context_.reset(new net::URLRequestContext); + // |system_url_request_context_getter| may be null during tests. + if (system_url_request_context_getter) { + url_request_context_->CopyFrom( + system_url_request_context_getter->GetURLRequestContext()); + } + url_request_context_->set_cookie_store(cookie_store.get()); +} + +void SafeBrowsingService::DestroyURLRequestContextOnIOThread( + scoped_refptr<SafeBrowsingURLRequestContextGetter> context_getter) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + context_getter->SafeBrowsingServiceShuttingDown(); + url_request_context_.reset(); +} + +SafeBrowsingProtocolConfig SafeBrowsingService::GetProtocolConfig() const { + SafeBrowsingProtocolConfig config; +#if defined(GOOGLE_CHROME_BUILD) + config.client_name = "googlechrome"; +#else + config.client_name = "chromium"; +#endif + + // Mark client string to allow server to differentiate mobile. + config.client_name.append("-i"); + + base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); + config.disable_auto_update = + cmdline->HasSwitch(switches::kSbDisableAutoUpdate) || + cmdline->HasSwitch(switches::kDisableBackgroundNetworking); + config.url_prefix = kSbDefaultURLPrefix; + config.backup_connect_error_url_prefix = kSbBackupConnectErrorURLPrefix; + config.backup_http_error_url_prefix = kSbBackupHttpErrorURLPrefix; + config.backup_network_error_url_prefix = kSbBackupNetworkErrorURLPrefix; + + return config; +} + +void SafeBrowsingService::StartOnIOThread( + net::URLRequestContextGetter* url_request_context_getter) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + if (enabled_) + return; + enabled_ = true; + + SafeBrowsingProtocolConfig config = GetProtocolConfig(); + + DCHECK(!ping_manager_); + ping_manager_ = + SafeBrowsingPingManager::Create(url_request_context_getter, config); +} + +void SafeBrowsingService::StopOnIOThread(bool shutdown) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + ui_manager_->StopOnIOThread(shutdown); + + if (enabled_) { + enabled_ = false; + + delete ping_manager_; + ping_manager_ = nullptr; + } +} + +void SafeBrowsingService::Start() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + + web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingService::StartOnIOThread, + this, url_request_context_getter_)); +} + +void SafeBrowsingService::Stop(bool shutdown) { + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingService::StopOnIOThread, this, shutdown)); +} + +void SafeBrowsingService::AddPrefService(PrefService* pref_service) { + DCHECK(prefs_map_.find(pref_service) == prefs_map_.end()); + PrefChangeRegistrar* registrar = new PrefChangeRegistrar(); + registrar->Init(pref_service); + registrar->Add( + prefs::kSafeBrowsingEnabled, + base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this))); + // ClientSideDetectionService will need to be refresh the models + // renderers have if extended-reporting changes. + registrar->Add( + prefs::kSafeBrowsingExtendedReportingEnabled, + base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this))); + prefs_map_[pref_service] = registrar; + RefreshState(); + + // Record the current pref state. + UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.General", + pref_service->GetBoolean(prefs::kSafeBrowsingEnabled)); + UMA_HISTOGRAM_BOOLEAN( + "SafeBrowsing.Pref.Extended", + pref_service->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)); +} + +void SafeBrowsingService::RemovePrefService(PrefService* pref_service) { + if (prefs_map_.find(pref_service) != prefs_map_.end()) { + delete prefs_map_[pref_service]; + prefs_map_.erase(pref_service); + RefreshState(); + } else { + NOTREACHED(); + } +} + +scoped_ptr<SafeBrowsingService::StateSubscription> +SafeBrowsingService::RegisterStateCallback( + const base::Callback<void(void)>& callback) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + return state_callback_list_.Add(callback); +} + +void SafeBrowsingService::RefreshState() { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + // Check if any browser state requires the service to be active. + bool enable = false; + std::map<PrefService*, PrefChangeRegistrar*>::iterator iter; + for (iter = prefs_map_.begin(); iter != prefs_map_.end(); ++iter) { + if (iter->first->GetBoolean(prefs::kSafeBrowsingEnabled)) { + enable = true; + break; + } + } + + enabled_by_prefs_ = enable; + + if (enable) + Start(); + else + Stop(false); + + state_callback_list_.Notify(); +} + +void SafeBrowsingService::SendDownloadRecoveryReport( + const std::string& report) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingService::OnSendDownloadRecoveryReport, this, + report)); +} + +void SafeBrowsingService::OnSendDownloadRecoveryReport( + const std::string& report) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + if (ping_manager()) + ping_manager()->ReportThreatDetails(report); +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_service.h b/ios/chrome/browser/safe_browsing/safe_browsing_service.h new file mode 100644 index 0000000..78e62c1 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/safe_browsing_service.h @@ -0,0 +1,227 @@ +// 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. +// +// The Safe Browsing service is responsible for downloading anti-phishing and +// anti-malware tables and checking urls against them. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_ + +#include <map> +#include <string> + +#include "base/callback.h" +#include "base/callback_list.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "base/sequenced_task_runner_helpers.h" +#include "components/safe_browsing_db/util.h" +#include "ios/web/public/web_thread.h" + +class PrefChangeRegistrar; +class PrefService; +class TrackedPreferenceValidationDelegate; + +namespace base { +class Thread; +} + +namespace net { +class HttpResponseHeaders; +class URLRequest; +class URLRequestContext; +class URLRequestContextGetter; +} + +namespace safe_browsing { +class ClientSideDetectionService; +class DownloadProtectionService; +struct SafeBrowsingProtocolConfig; +class SafeBrowsingPingManager; +class SafeBrowsingServiceFactory; +class SafeBrowsingUIManager; +class SafeBrowsingURLRequestContextGetter; + +// Construction needs to happen on the main thread. +// The SafeBrowsingService owns both the UI and Database managers which do +// the heavylifting of safebrowsing service. Both of these managers stay +// alive until SafeBrowsingService is destroyed, however, they are disabled +// permanently when Shutdown method is called. +class SafeBrowsingService + : public base::RefCountedThreadSafe<SafeBrowsingService, + web::WebThread::DeleteOnUIThread> { + public: + // Makes the passed |factory| the factory used to instanciate + // a SafeBrowsingService. Useful for tests. + static void RegisterFactory(SafeBrowsingServiceFactory* factory) { + factory_ = factory; + } + + static base::FilePath GetBaseFilename(); + + // Create an instance of the safe browsing service. + static SafeBrowsingService* CreateSafeBrowsingService(); + + // Called on the UI thread to initialize the service. + void Initialize(); + + // Called on the main thread to let us know that the io_thread is going away. + void ShutDown(); + + // Called on UI thread to decide if the download file's sha256 hash + // should be calculated for safebrowsing. + bool DownloadBinHashNeeded() const; + + // Create a protocol config struct. + SafeBrowsingProtocolConfig GetProtocolConfig() const; + + // Get current enabled status. Must be called on IO thread. + bool enabled() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + return enabled_; + } + + // Whether the service is enabled by the current set of profiles. + bool enabled_by_prefs() const { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + return enabled_by_prefs_; + } + + net::URLRequestContextGetter* url_request_context(); + + const scoped_refptr<SafeBrowsingUIManager>& ui_manager() const; + + SafeBrowsingPingManager* ping_manager() const; + + // Checks |headers| from a request that has passed through a proxy which + // adds safe browsing headers, and returns the threat type indicated by those + // headers (or SB_THREAT_TYPE_SAFE if no threat is indicated). + SBThreatType CheckResponseFromProxyRequestHeaders( + scoped_refptr<net::HttpResponseHeaders> headers); + + // Type for subscriptions to SafeBrowsing service state. + typedef base::CallbackList<void(void)>::Subscription StateSubscription; + + // Adds a listener for when SafeBrowsing preferences might have changed. + // To get the current state, the callback should call enabled_by_prefs(). + // Should only be called on the UI thread. + scoped_ptr<StateSubscription> RegisterStateCallback( + const base::Callback<void(void)>& callback); + + // Sends serialized download recovery report to backend. + void SendDownloadRecoveryReport(const std::string& report); + + protected: + // Creates the safe browsing service. Need to initialize before using. + SafeBrowsingService(); + + virtual ~SafeBrowsingService(); + + SafeBrowsingUIManager* CreateUIManager(); + + private: + friend class SafeBrowsingServiceFactoryImpl; + friend struct web::WebThread::DeleteOnThread<web::WebThread::UI>; + friend class base::DeleteHelper<SafeBrowsingService>; + friend class SafeBrowsingURLRequestContextGetter; + + void InitURLRequestContextOnIOThread( + net::URLRequestContextGetter* system_url_request_context_getter); + + // Destroys the URLRequest and shuts down the provided getter on the + // IO thread. + void DestroyURLRequestContextOnIOThread( + scoped_refptr<SafeBrowsingURLRequestContextGetter> context_getter); + + // Called to initialize objects that are used on the io_thread. This may be + // called multiple times during the life of the SafeBrowsingService. + void StartOnIOThread( + net::URLRequestContextGetter* url_request_context_getter); + + // Called to stop or shutdown operations on the io_thread. This may be called + // multiple times to stop during the life of the SafeBrowsingService. If + // shutdown is true, then the operations on the io thread are shutdown + // permanently and cannot be restarted. + void StopOnIOThread(bool shutdown); + + // Start up SafeBrowsing objects. This can be called at browser start, or when + // the user checks the "Enable SafeBrowsing" option in the Advanced options + // UI. + void Start(); + + // Stops the SafeBrowsingService. This can be called when the safe browsing + // preference is disabled. When shutdown is true, operation is permanently + // shutdown and cannot be restarted. + void Stop(bool shutdown); + + // Starts following the safe browsing preference on |pref_service|. + void AddPrefService(PrefService* pref_service); + + // Stop following the safe browsing preference on |pref_service|. + void RemovePrefService(PrefService* pref_service); + + // Checks if any profile is currently using the safe browsing service, and + // starts or stops the service accordingly. + void RefreshState(); + + void OnSendDownloadRecoveryReport(const std::string& report); + + // The factory used to instanciate a SafeBrowsingService object. + // Useful for tests, so they can provide their own implementation of + // SafeBrowsingService. + static SafeBrowsingServiceFactory* factory_; + + // The SafeBrowsingURLRequestContextGetter used to access + // |url_request_context_|. Accessed on UI thread. + scoped_refptr<SafeBrowsingURLRequestContextGetter> + url_request_context_getter_; + + // The SafeBrowsingURLRequestContext. Accessed on IO thread. + scoped_ptr<net::URLRequestContext> url_request_context_; + + // Provides phishing and malware statistics. Accessed on IO thread. + SafeBrowsingPingManager* ping_manager_; + + // Whether the service is running. 'enabled_' is used by SafeBrowsingService + // on the IO thread during normal operations. + bool enabled_; + + // Whether SafeBrowsing is enabled by the current set of profiles. + // Accessed on UI thread. + bool enabled_by_prefs_; + + // Tracks existing PrefServices, and the safe browsing preference on each. + // This is used to determine if any profile is currently using the safe + // browsing service, and to start it up or shut it down accordingly. + // Accessed on UI thread. + std::map<PrefService*, PrefChangeRegistrar*> prefs_map_; + + // Callbacks when SafeBrowsing state might have changed. + // Should only be accessed on the UI thread. + base::CallbackList<void(void)> state_callback_list_; + + // The UI manager handles showing interstitials. Accessed on both UI and IO + // thread. + scoped_refptr<SafeBrowsingUIManager> ui_manager_; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingService); +}; + +// Factory for creating SafeBrowsingService. Useful for tests. +class SafeBrowsingServiceFactory { + public: + SafeBrowsingServiceFactory() {} + virtual ~SafeBrowsingServiceFactory() {} + virtual SafeBrowsingService* CreateSafeBrowsingService() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactory); +}; + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_ diff --git a/ios/chrome/browser/safe_browsing/ui_manager.cc b/ios/chrome/browser/safe_browsing/ui_manager.cc new file mode 100644 index 0000000..d36e8c0 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/ui_manager.cc @@ -0,0 +1,335 @@ +// 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 "ios/chrome/browser/safe_browsing/ui_manager.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/debug/leak_tracker.h" +#include "base/metrics/histogram_macros.h" +#include "base/prefs/pref_service.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "ios/chrome/browser/pref_names.h" +#include "ios/chrome/browser/safe_browsing/metadata.pb.h" +#include "ios/chrome/browser/safe_browsing/ping_manager.h" +#include "ios/chrome/browser/safe_browsing/safe_browsing_blocking_page.h" +#include "ios/chrome/browser/safe_browsing/safe_browsing_service.h" +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" +#include "ios/web/public/navigation_item.h" +#include "ios/web/public/navigation_manager.h" +#include "ios/web/public/web_state/web_state.h" +#include "ios/web/public/web_thread.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/ssl/ssl_info.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" + +namespace { + +const void* const kWhitelistKey = &kWhitelistKey; + +class WhitelistUrlSet : public base::SupportsUserData::Data { + public: + WhitelistUrlSet() {} + + bool Contains(const GURL url) { + auto iter = set_.find(url.GetWithEmptyPath()); + return iter != set_.end(); + } + + void Insert(const GURL url) { set_.insert(url.GetWithEmptyPath()); } + + private: + std::set<GURL> set_; + + DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet); +}; + +web::NavigationItem* GetActiveItemForNavigationManager( + web::NavigationManager* navigation_manager) { + web::NavigationItem* active_item = navigation_manager->GetTransientItem(); + if (!active_item) + active_item = navigation_manager->GetPendingItem(); + if (!active_item) + active_item = navigation_manager->GetLastCommittedItem(); + return active_item; +} + +} // namespace + +namespace safe_browsing { + +// SafeBrowsingUIManager::UnsafeResource --------------------------------------- + +SafeBrowsingUIManager::UnsafeResource::UnsafeResource() + : is_subresource(false), + threat_type(SB_THREAT_TYPE_SAFE), + threat_source(safe_browsing::ThreatSource::UNKNOWN) {} + +SafeBrowsingUIManager::UnsafeResource::~UnsafeResource() {} + +bool SafeBrowsingUIManager::UnsafeResource::IsMainPageLoadBlocked() const { + // Subresource hits cannot happen until after main page load is committed. + if (is_subresource) + return false; + + // Client-side phishing detection interstitials never block the main frame + // load, since they happen after the page is finished loading. + if (threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL || + threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) { + return false; + } + + return true; +} + +// SafeBrowsingUIManager ------------------------------------------------------- + +SafeBrowsingUIManager::SafeBrowsingUIManager( + const scoped_refptr<SafeBrowsingService>& service) + : sb_service_(service) {} + +SafeBrowsingUIManager::~SafeBrowsingUIManager() {} + +void SafeBrowsingUIManager::StopOnIOThread(bool shutdown) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + if (shutdown) + sb_service_ = nullptr; +} + +void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) { + UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time); +} + +void SafeBrowsingUIManager::OnBlockingPageDone( + const std::vector<UnsafeResource>& resources, + bool proceed) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + for (const auto& resource : resources) { + if (!resource.callback.is_null()) { + DCHECK(resource.callback_thread); + resource.callback_thread->PostTask( + FROM_HERE, base::Bind(resource.callback, proceed)); + } + + if (proceed) + AddToWhitelist(resource); + } +} + +void SafeBrowsingUIManager::DisplayBlockingPage( + const UnsafeResource& resource) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + if (resource.is_subresource && !resource.is_subframe) { + // Sites tagged as serving Unwanted Software should only show a warning for + // main-frame or sub-frame resource. Similar warning restrictions should be + // applied to malware sites tagged as "landing sites" (see "Types of + // Malware sites" under + // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings). + MalwarePatternType proto; + if (resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED || + (resource.threat_type == SB_THREAT_TYPE_URL_MALWARE && + !resource.threat_metadata.empty() && + proto.ParseFromString(resource.threat_metadata) && + proto.pattern_type() == MalwarePatternType::LANDING)) { + if (!resource.callback.is_null()) { + DCHECK(resource.callback_thread); + resource.callback_thread->PostTask(FROM_HERE, + base::Bind(resource.callback, true)); + } + + return; + } + } + + // The tab might have been closed. If it was closed, just act as if "Don't + // Proceed" had been chosen. + web::WebState* web_state = resource.weak_web_state.get(); + if (!web_state) { + std::vector<UnsafeResource> resources; + resources.push_back(resource); + OnBlockingPageDone(resources, false); + return; + } + + // Check if the user has already ignored a SB warning for the same WebState + // and top-level domain. + if (IsWhitelisted(resource)) { + if (!resource.callback.is_null()) { + DCHECK(resource.callback_thread); + resource.callback_thread->PostTask(FROM_HERE, + base::Bind(resource.callback, true)); + } + return; + } + + if (resource.threat_type != SB_THREAT_TYPE_SAFE) { + HitReport hit_report; + hit_report.malicious_url = resource.url; + hit_report.page_url = web_state->GetVisibleURL(); + hit_report.is_subresource = resource.is_subresource; + hit_report.threat_type = resource.threat_type; + hit_report.threat_source = resource.threat_source; + + web::NavigationItem* item = + GetActiveItemForNavigationManager(web_state->GetNavigationManager()); + if (item) { + hit_report.referrer_url = item->GetReferrer().url; + } + + // When the malicious url is on the main frame, and resource.original_url + // is not the same as the resource.url, that means we have a redirect from + // resource.original_url to resource.url. + // Also, at this point, page_url points to the _previous_ page that we + // were on. We replace page_url with resource.original_url and referrer + // with page_url. + if (!resource.is_subresource && !resource.original_url.is_empty() && + resource.original_url != resource.url) { + hit_report.referrer_url = hit_report.page_url; + hit_report.page_url = resource.original_url; + } + + ios::ChromeBrowserState* browser_state = + ios::ChromeBrowserState::FromBrowserState(web_state->GetBrowserState()); + hit_report.is_extended_reporting = + browser_state && + browser_state->GetPrefs()->GetBoolean( + prefs::kSafeBrowsingExtendedReportingEnabled); + hit_report.is_metrics_reporting_active = + safe_browsing::IsMetricsReportingActive(); + + MaybeReportSafeBrowsingHit(hit_report); + } + + if (resource.threat_type != SB_THREAT_TYPE_SAFE) { + FOR_EACH_OBSERVER(Observer, observer_list_, OnSafeBrowsingHit(resource)); + } + SafeBrowsingBlockingPage::ShowBlockingPage(web_state, this, resource); +} + +// A safebrowsing hit is sent after a blocking page for malware/phishing +// or after the warning dialog for download urls, only for +// UMA || extended_reporting users. +void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit( + const HitReport& hit_report) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + + // Decide if we should send this report. + if (hit_report.is_metrics_reporting_active || + hit_report.is_extended_reporting) { + web::WebThread::PostTask( + web::WebThread::IO, FROM_HERE, + base::Bind(&SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread, + this, hit_report)); + } +} + +void SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread( + const HitReport& hit_report) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + // The service may delete the ping manager (i.e. when user disabling service, + // etc). This happens on the IO thread. + if (!sb_service_ || !sb_service_->ping_manager()) + return; + + DVLOG(1) << "ReportSafeBrowsingHit: " << hit_report.malicious_url << " " + << hit_report.page_url << " " << hit_report.referrer_url << " " + << hit_report.is_subresource << " " << hit_report.threat_type; + sb_service_->ping_manager()->ReportSafeBrowsingHit(hit_report); +} + +void SafeBrowsingUIManager::ReportInvalidCertificateChain( + const std::string& serialized_report, + const base::Closure& callback) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + web::WebThread::PostTaskAndReply( + web::WebThread::IO, FROM_HERE, + base::Bind( + &SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread, this, + serialized_report), + callback); +} + +void SafeBrowsingUIManager::AddObserver(Observer* observer) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + observer_list_.AddObserver(observer); +} + +void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + observer_list_.RemoveObserver(observer); +} + +void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( + const std::string& serialized_report) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + // The service may delete the ping manager (i.e. when user disabling service, + // etc). This happens on the IO thread. + if (!sb_service_ || !sb_service_->ping_manager()) + return; + + sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); +} + +// If the user had opted-in to send ThreatDetails, this gets called +// when the report is ready. +void SafeBrowsingUIManager::SendSerializedThreatDetails( + const std::string& serialized) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO); + + // The service may delete the ping manager (i.e. when user disabling service, + // etc). This happens on the IO thread. + if (sb_service_.get() == nullptr || sb_service_->ping_manager() == nullptr) + return; + + if (!serialized.empty()) { + DVLOG(1) << "Sending serialized threat details."; + sb_service_->ping_manager()->ReportThreatDetails(serialized); + } +} + +// Whitelist this domain in the current WebState. Either add the +// domain to an existing WhitelistUrlSet, or create a new WhitelistUrlSet. +void SafeBrowsingUIManager::AddToWhitelist(const UnsafeResource& resource) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + DCHECK(resource.weak_web_state.get()); + + web::WebState* web_state = resource.weak_web_state.get(); + WhitelistUrlSet* site_list = + static_cast<WhitelistUrlSet*>(web_state->GetUserData(kWhitelistKey)); + if (!site_list) { + site_list = new WhitelistUrlSet; + web_state->SetUserData(kWhitelistKey, site_list); + } + + GURL whitelisted_url(resource.is_subresource ? web_state->GetVisibleURL() + : resource.url); + site_list->Insert(whitelisted_url); +} + +// Check if the user has already ignored a SB warning for this WebState and +// top-level domain. +bool SafeBrowsingUIManager::IsWhitelisted(const UnsafeResource& resource) { + DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); + DCHECK(resource.weak_web_state.get()); + + web::WebState* web_state = resource.weak_web_state.get(); + GURL maybe_whitelisted_url( + resource.is_subresource ? web_state->GetVisibleURL() : resource.url); + WhitelistUrlSet* site_list = + static_cast<WhitelistUrlSet*>(web_state->GetUserData(kWhitelistKey)); + if (!site_list) + return false; + return site_list->Contains(maybe_whitelisted_url); +} + +} // namespace safe_browsing diff --git a/ios/chrome/browser/safe_browsing/ui_manager.h b/ios/chrome/browser/safe_browsing/ui_manager.h new file mode 100644 index 0000000..8d1eb51 --- /dev/null +++ b/ios/chrome/browser/safe_browsing/ui_manager.h @@ -0,0 +1,164 @@ +// 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. +// +// The Safe Browsing service is responsible for downloading anti-phishing and +// anti-malware tables and checking urls against them. + +#ifndef IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_ +#define IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/time/time.h" +#include "components/safe_browsing_db/util.h" +#include "ios/chrome/browser/safe_browsing/hit_report.h" +#include "url/gurl.h" + +namespace base { +class Thread; +} // namespace base + +namespace net { +class SSLInfo; +} // namespace net + +namespace web { +class WebState; +} // namespace web + +namespace safe_browsing { + +class SafeBrowsingService; + +// Construction needs to happen on the main thread. +class SafeBrowsingUIManager + : public base::RefCountedThreadSafe<SafeBrowsingUIManager> { + public: + // Passed a boolean indicating whether or not it is OK to proceed with + // loading an URL. + typedef base::Callback<void(bool /*proceed*/)> UrlCheckCallback; + + // Structure used to pass parameters between the IO and UI thread when + // interacting with the blocking page. + struct UnsafeResource { + UnsafeResource(); + ~UnsafeResource(); + + bool IsMainPageLoadBlocked() const; + + GURL url; + GURL original_url; + std::vector<GURL> redirect_urls; + bool is_subresource; + bool is_subframe; + SBThreatType threat_type; + std::string threat_metadata; + UrlCheckCallback callback; // This is called back on |callback_thread|. + scoped_refptr<base::SingleThreadTaskRunner> callback_thread; + base::WeakPtr<web::WebState> weak_web_state; + safe_browsing::ThreatSource threat_source; + }; + + // Observer class can be used to get notified when a SafeBrowsing hit + // was found. + class Observer { + public: + // The |resource| was classified as unsafe by SafeBrowsing, and is + // not whitelisted. + // The |resource| must not be accessed after OnSafeBrowsingHit returns. + // This method will be called on the UI thread. + virtual void OnSafeBrowsingHit(const UnsafeResource& resource) = 0; + + protected: + Observer() {} + virtual ~Observer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Observer); + }; + + explicit SafeBrowsingUIManager( + const scoped_refptr<SafeBrowsingService>& service); + + // Called to stop or shutdown operations on the io_thread. This may be called + // multiple times during the life of the UIManager. Should be called + // on IO thread. If shutdown is true, the manager is disabled permanently. + void StopOnIOThread(bool shutdown); + + // Called on the UI thread to display an interstitial page. + // |url| is the url of the resource that matches a safe browsing list. + // If the request contained a chain of redirects, |url| is the last url + // in the chain, and |original_url| is the first one (the root of the + // chain). Otherwise, |original_url| = |url|. + virtual void DisplayBlockingPage(const UnsafeResource& resource); + + // Returns true if we already displayed an interstitial for that top-level + // site in a given WebContents. Called on the UI thread. + bool IsWhitelisted(const UnsafeResource& resource); + + // The blocking page on the UI thread has completed. + void OnBlockingPageDone(const std::vector<UnsafeResource>& resources, + bool proceed); + + // Log the user perceived delay caused by SafeBrowsing. This delay is the time + // delta starting from when we would have started reading data from the + // network, and ending when the SafeBrowsing check completes indicating that + // the current page is 'safe'. + void LogPauseDelay(base::TimeDelta time); + + // Called on the IO thread by the ThreatDetails with the serialized + // protocol buffer, so the service can send it over. + virtual void SendSerializedThreatDetails(const std::string& serialized); + + // Report hits to the unsafe contents (malware, phishing, unsafe download URL) + // to the server. Can only be called on UI thread. If |post_data| is + // non-empty, the request will be sent as a POST instead of a GET. + // Will report only for UMA || is_extended_reporting. + virtual void MaybeReportSafeBrowsingHit( + const safe_browsing::HitReport& hit_report); + + // Report an invalid TLS/SSL certificate chain to the server. Can only + // be called on UI thread. + void ReportInvalidCertificateChain(const std::string& serialized_report, + const base::Closure& callback); + + // Add and remove observers. These methods must be invoked on the UI thread. + void AddObserver(Observer* observer); + void RemoveObserver(Observer* remove); + + protected: + virtual ~SafeBrowsingUIManager(); + + private: + friend class base::RefCountedThreadSafe<SafeBrowsingUIManager>; + + // Call protocol manager on IO thread to report hits of unsafe contents. + void ReportSafeBrowsingHitOnIOThread( + const safe_browsing::HitReport& hit_report); + + // Sends an invalid certificate chain report over the network. + void ReportInvalidCertificateChainOnIOThread( + const std::string& serialized_report); + + // Updates the whitelist state. Called on the UI thread. + void AddToWhitelist(const UnsafeResource& resource); + + // Safebrowsing service. + scoped_refptr<SafeBrowsingService> sb_service_; + + base::ObserverList<Observer> observer_list_; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIManager); +}; + +} // namespace safe_browsing + +#endif // IOS_CHROME_BROWSER_SAFE_BROWSING_UI_MANAGER_H_ diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp index 187bbb1..7f84cf3 100644 --- a/ios/chrome/ios_chrome.gyp +++ b/ios/chrome/ios_chrome.gyp @@ -54,6 +54,7 @@ '../../components/components.gyp:bookmarks_managed', '../../components/components.gyp:browser_sync_browser', '../../components/components.gyp:browser_sync_common', + '../../components/components.gyp:certificate_reporting', '../../components/components.gyp:component_updater', '../../components/components.gyp:content_settings_core_browser', '../../components/components.gyp:cookie_config', @@ -87,6 +88,7 @@ '../../components/components.gyp:pref_registry', '../../components/components.gyp:proxy_config', '../../components/components.gyp:rappor', + '../../components/components.gyp:safe_browsing_db', '../../components/components.gyp:search', '../../components/components.gyp:search_engines', '../../components/components.gyp:security_interstitials_core', @@ -619,6 +621,25 @@ 'ios_chrome_browser_rlz', ], }], + ['safe_browsing!=0', { + 'sources': [ + 'browser/safe_browsing/hit_report.cc', + 'browser/safe_browsing/hit_report.h', + 'browser/safe_browsing/ping_manager.cc', + 'browser/safe_browsing/ping_manager.h', + 'browser/safe_browsing/protocol_manager_helper.cc', + 'browser/safe_browsing/protocol_manager_helper.h', + 'browser/safe_browsing/safe_browsing_blocking_page.cc', + 'browser/safe_browsing/safe_browsing_blocking_page.h', + 'browser/safe_browsing/safe_browsing_service.cc', + 'browser/safe_browsing/safe_browsing_service.h', + 'browser/safe_browsing/ui_manager.cc', + 'browser/safe_browsing/ui_manager.h', + ], + 'dependencies': [ + 'ios_chrome_safe_browsing_proto', + ], + }] ], }, { @@ -785,5 +806,20 @@ }, ], }], + ['safe_browsing!=0', { + 'targets': [ + { + # GN version: //ios/chrome/browser/safe_browsing:proto + 'target_name': 'ios_chrome_safe_browsing_proto', + 'type': 'static_library', + 'sources': [ 'browser/safe_browsing/metadata.proto' ], + 'variables': { + 'proto_in_dir': 'browser/safe_browsing', + 'proto_out_dir': 'ios/chrome/browser/safe_browsing', + }, + 'includes': [ '../../build/protoc.gypi' ], + }, + ], + }], ], } |