| Index: chrome/browser/safe_browsing/v4_protocol_manager.cc
|
| diff --git a/chrome/browser/safe_browsing/v4_protocol_manager.cc b/chrome/browser/safe_browsing/v4_protocol_manager.cc
|
| deleted file mode 100644
|
| index 4cb32e325e8e2d1d8f0456a638902d4c346d78b1..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/safe_browsing/v4_protocol_manager.cc
|
| +++ /dev/null
|
| @@ -1,412 +0,0 @@
|
| -// Copyright 2016 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/safe_browsing/v4_protocol_manager.h"
|
| -
|
| -#include <utility>
|
| -
|
| -#include "base/base64.h"
|
| -#include "base/logging.h"
|
| -#include "base/macros.h"
|
| -#include "base/metrics/histogram_macros.h"
|
| -#include "base/metrics/sparse_histogram.h"
|
| -#include "base/rand_util.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/timer/timer.h"
|
| -#include "net/base/escape.h"
|
| -#include "net/base/load_flags.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/http/http_status_code.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"
|
| -
|
| -using base::Time;
|
| -using base::TimeDelta;
|
| -
|
| -namespace {
|
| -
|
| -// Enumerate parsing failures for histogramming purposes. DO NOT CHANGE
|
| -// THE ORDERING OF THESE VALUES.
|
| -enum ParseResultType {
|
| - // Error parsing the protocol buffer from a string.
|
| - PARSE_FROM_STRING_ERROR = 0,
|
| -
|
| - // A match in the response had an unexpected THREAT_ENTRY_TYPE.
|
| - UNEXPECTED_THREAT_ENTRY_TYPE_ERROR = 1,
|
| -
|
| - // A match in the response had an unexpected THREAT_TYPE.
|
| - UNEXPECTED_THREAT_TYPE_ERROR = 2,
|
| -
|
| - // A match in the response had an unexpected PLATFORM_TYPE.
|
| - UNEXPECTED_PLATFORM_TYPE_ERROR = 3,
|
| -
|
| - // A match in the response contained no metadata where metadata was
|
| - // expected.
|
| - NO_METADATA_ERROR = 4,
|
| -
|
| - // A match in the response contained a ThreatType that was inconsistent
|
| - // with the other matches.
|
| - INCONSISTENT_THREAT_TYPE_ERROR = 5,
|
| -
|
| - // Memory space for histograms is determined by the max. ALWAYS
|
| - // ADD NEW VALUES BEFORE THIS ONE.
|
| - PARSE_GET_HASH_RESULT_MAX = 6
|
| -};
|
| -
|
| -// Record parsing errors of a GetHash result.
|
| -void RecordParseGetHashResult(ParseResultType result_type) {
|
| - UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4HashResult", result_type,
|
| - PARSE_GET_HASH_RESULT_MAX);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace safe_browsing {
|
| -
|
| -const char kUmaV4ResponseMetricName[] =
|
| - "SafeBrowsing.GetV4HashHttpResponseOrErrorCode";
|
| -
|
| -// The URL prefix where browser fetches hashes from the server.
|
| -const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
|
| -
|
| -// The default SBProtocolManagerFactory.
|
| -class V4ProtocolManagerFactoryImpl : public V4ProtocolManagerFactory {
|
| - public:
|
| - V4ProtocolManagerFactoryImpl() {}
|
| - ~V4ProtocolManagerFactoryImpl() override {}
|
| - V4ProtocolManager* CreateProtocolManager(
|
| - net::URLRequestContextGetter* request_context_getter,
|
| - const V4ProtocolConfig& config) override {
|
| - return new V4ProtocolManager(request_context_getter, config);
|
| - }
|
| -
|
| - private:
|
| - DISALLOW_COPY_AND_ASSIGN(V4ProtocolManagerFactoryImpl);
|
| -};
|
| -
|
| -// V4ProtocolManager implementation --------------------------------
|
| -
|
| -// static
|
| -V4ProtocolManagerFactory* V4ProtocolManager::factory_ = NULL;
|
| -
|
| -// static
|
| -V4ProtocolManager* V4ProtocolManager::Create(
|
| - net::URLRequestContextGetter* request_context_getter,
|
| - const V4ProtocolConfig& config) {
|
| - if (!factory_)
|
| - factory_ = new V4ProtocolManagerFactoryImpl();
|
| - return factory_->CreateProtocolManager(request_context_getter, config);
|
| -}
|
| -
|
| -// static
|
| -// Backoff interval is MIN(((2^(n-1))*15 minutes) * (RAND + 1), 24 hours) where
|
| -// n is the number of consecutive errors.
|
| -base::TimeDelta V4ProtocolManager::GetNextBackOffInterval(size_t* error_count,
|
| - size_t* multiplier) {
|
| - DCHECK(multiplier && error_count);
|
| - (*error_count)++;
|
| - if (*error_count > 1 && *error_count < 9) {
|
| - // With error count 9 and above we will hit the 24 hour max interval.
|
| - // Cap the multiplier here to prevent integer overflow errors.
|
| - *multiplier *= 2;
|
| - }
|
| - base::TimeDelta next =
|
| - base::TimeDelta::FromMinutes(*multiplier * (1 + base::RandDouble()) * 15);
|
| -
|
| - base::TimeDelta day = base::TimeDelta::FromHours(24);
|
| -
|
| - if (next < day)
|
| - return next;
|
| - else
|
| - return day;
|
| -}
|
| -
|
| -void V4ProtocolManager::ResetGetHashErrors() {
|
| - gethash_error_count_ = 0;
|
| - gethash_back_off_mult_ = 1;
|
| -}
|
| -
|
| -V4ProtocolManager::V4ProtocolManager(
|
| - net::URLRequestContextGetter* request_context_getter,
|
| - const V4ProtocolConfig& config)
|
| - : gethash_error_count_(0),
|
| - gethash_back_off_mult_(1),
|
| - next_gethash_time_(Time::FromDoubleT(0)),
|
| - version_(config.version),
|
| - client_name_(config.client_name),
|
| - key_param_(config.key_param),
|
| - request_context_getter_(request_context_getter),
|
| - url_fetcher_id_(0) {
|
| - DCHECK(!version_.empty());
|
| -}
|
| -
|
| -// static
|
| -void V4ProtocolManager::RecordGetHashResult(ResultType result_type) {
|
| - UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.GetV4HashResult", result_type,
|
| - GET_HASH_RESULT_MAX);
|
| -}
|
| -
|
| -void V4ProtocolManager::RecordHttpResponseOrErrorCode(
|
| - const char* metric_name,
|
| - const net::URLRequestStatus& status,
|
| - int response_code) {
|
| - UMA_HISTOGRAM_SPARSE_SLOWLY(
|
| - metric_name, status.is_success() ? response_code : status.error());
|
| -}
|
| -
|
| -V4ProtocolManager::~V4ProtocolManager() {
|
| - // Delete in-progress SafeBrowsing requests.
|
| - STLDeleteContainerPairFirstPointers(hash_requests_.begin(),
|
| - hash_requests_.end());
|
| - hash_requests_.clear();
|
| -}
|
| -
|
| -std::string V4ProtocolManager::GetHashRequest(
|
| - const std::vector<SBPrefix>& prefixes,
|
| - const std::vector<PlatformType>& platforms,
|
| - ThreatType threat_type) {
|
| - // Build the request. Client info and client states are not added to the
|
| - // request protocol buffer. Client info is passed as params in the url.
|
| - FindFullHashesRequest req;
|
| - ThreatInfo* info = req.mutable_threat_info();
|
| - info->add_threat_types(threat_type);
|
| - info->add_threat_entry_types(URL_EXPRESSION);
|
| - for (const PlatformType p : platforms) {
|
| - info->add_platform_types(p);
|
| - }
|
| - for (const SBPrefix& prefix : prefixes) {
|
| - std::string hash(reinterpret_cast<const char*>(&prefix), sizeof(SBPrefix));
|
| - info->add_threat_entries()->set_hash(hash);
|
| - }
|
| -
|
| - // Serialize and Base64 encode.
|
| - std::string req_data, req_base64;
|
| - req.SerializeToString(&req_data);
|
| - base::Base64Encode(req_data, &req_base64);
|
| -
|
| - return req_base64;
|
| -}
|
| -
|
| -bool V4ProtocolManager::ParseHashResponse(
|
| - const std::string& data,
|
| - std::vector<SBFullHashResult>* full_hashes,
|
| - base::TimeDelta* negative_cache_duration) {
|
| - FindFullHashesResponse response;
|
| -
|
| - if (!response.ParseFromString(data)) {
|
| - RecordParseGetHashResult(PARSE_FROM_STRING_ERROR);
|
| - return false;
|
| - }
|
| -
|
| - if (response.has_negative_cache_duration()) {
|
| - // Seconds resolution is good enough so we ignore the nanos field.
|
| - *negative_cache_duration = base::TimeDelta::FromSeconds(
|
| - response.negative_cache_duration().seconds());
|
| - }
|
| -
|
| - if (response.has_minimum_wait_duration()) {
|
| - // Seconds resolution is good enough so we ignore the nanos field.
|
| - next_gethash_time_ =
|
| - Time::Now() + base::TimeDelta::FromSeconds(
|
| - response.minimum_wait_duration().seconds());
|
| - }
|
| -
|
| - // We only expect one threat type per request, so we make sure
|
| - // the threat types are consistent between matches.
|
| - ThreatType expected_threat_type = THREAT_TYPE_UNSPECIFIED;
|
| -
|
| - // Loop over the threat matches and fill in full_hashes.
|
| - for (const ThreatMatch& match : response.matches()) {
|
| - // Make sure the platform and threat entry type match.
|
| - if (!(match.has_threat_entry_type() &&
|
| - match.threat_entry_type() == URL_EXPRESSION && match.has_threat())) {
|
| - RecordParseGetHashResult(UNEXPECTED_THREAT_ENTRY_TYPE_ERROR);
|
| - return false;
|
| - }
|
| -
|
| - if (!match.has_threat_type()) {
|
| - RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
|
| - return false;
|
| - }
|
| -
|
| - if (expected_threat_type == THREAT_TYPE_UNSPECIFIED) {
|
| - expected_threat_type = match.threat_type();
|
| - } else if (match.threat_type() != expected_threat_type) {
|
| - RecordParseGetHashResult(INCONSISTENT_THREAT_TYPE_ERROR);
|
| - return false;
|
| - }
|
| -
|
| - // Fill in the full hash.
|
| - SBFullHashResult result;
|
| - result.hash = StringToSBFullHash(match.threat().hash());
|
| -
|
| - if (match.has_cache_duration()) {
|
| - // Seconds resolution is good enough so we ignore the nanos field.
|
| - result.cache_duration =
|
| - base::TimeDelta::FromSeconds(match.cache_duration().seconds());
|
| - }
|
| -
|
| - // Different threat types will handle the metadata differently.
|
| - if (match.threat_type() == API_ABUSE) {
|
| - if (match.has_platform_type() &&
|
| - match.platform_type() == CHROME_PLATFORM) {
|
| - if (match.has_threat_entry_metadata()) {
|
| - // For API Abuse, store a csv of the returned permissions.
|
| - for (const ThreatEntryMetadata::MetadataEntry& m :
|
| - match.threat_entry_metadata().entries()) {
|
| - if (m.key() == "permission") {
|
| - result.metadata += m.value() + ",";
|
| - }
|
| - }
|
| - } else {
|
| - RecordParseGetHashResult(NO_METADATA_ERROR);
|
| - return false;
|
| - }
|
| - } else {
|
| - RecordParseGetHashResult(UNEXPECTED_PLATFORM_TYPE_ERROR);
|
| - return false;
|
| - }
|
| - } else {
|
| - RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
|
| - return false;
|
| - }
|
| -
|
| - full_hashes->push_back(result);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void V4ProtocolManager::GetFullHashes(
|
| - const std::vector<SBPrefix>& prefixes,
|
| - const std::vector<PlatformType>& platforms,
|
| - ThreatType threat_type,
|
| - FullHashCallback callback) {
|
| - DCHECK(CalledOnValidThread());
|
| - // We need to wait the minimum waiting duration, and if we are in backoff,
|
| - // we need to check if we're past the next allowed time. If we are, we can
|
| - // proceed with the request. If not, we are required to return empty results
|
| - // (i.e. treat the page as safe).
|
| - if (Time::Now() <= next_gethash_time_) {
|
| - if (gethash_error_count_) {
|
| - RecordGetHashResult(GET_HASH_BACKOFF_ERROR);
|
| - } else {
|
| - RecordGetHashResult(GET_HASH_MIN_WAIT_DURATION_ERROR);
|
| - }
|
| - std::vector<SBFullHashResult> full_hashes;
|
| - callback.Run(full_hashes, base::TimeDelta());
|
| - return;
|
| - }
|
| -
|
| - std::string req_base64 = GetHashRequest(prefixes, platforms, threat_type);
|
| - GURL gethash_url = GetHashUrl(req_base64);
|
| -
|
| - net::URLFetcher* fetcher =
|
| - net::URLFetcher::Create(url_fetcher_id_++, gethash_url,
|
| - net::URLFetcher::GET, this)
|
| - .release();
|
| - hash_requests_[fetcher] = callback;
|
| -
|
| - fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
|
| - fetcher->SetRequestContext(request_context_getter_.get());
|
| - fetcher->Start();
|
| -}
|
| -
|
| -void V4ProtocolManager::GetFullHashesWithApis(
|
| - const std::vector<SBPrefix>& prefixes,
|
| - FullHashCallback callback) {
|
| - std::vector<PlatformType> platform = {CHROME_PLATFORM};
|
| - GetFullHashes(prefixes, platform, API_ABUSE, callback);
|
| -}
|
| -
|
| -// net::URLFetcherDelegate implementation ----------------------------------
|
| -
|
| -// SafeBrowsing request responses are handled here.
|
| -void V4ProtocolManager::OnURLFetchComplete(const net::URLFetcher* source) {
|
| - DCHECK(CalledOnValidThread());
|
| -
|
| - HashRequests::iterator it = hash_requests_.find(source);
|
| - DCHECK(it != hash_requests_.end()) << "Request not found";
|
| -
|
| - // FindFullHashes response.
|
| - // Reset the scoped pointer so the fetcher gets destroyed properly.
|
| - scoped_ptr<const net::URLFetcher> fetcher(it->first);
|
| -
|
| - int response_code = source->GetResponseCode();
|
| - net::URLRequestStatus status = source->GetStatus();
|
| - RecordHttpResponseOrErrorCode(kUmaV4ResponseMetricName, status,
|
| - response_code);
|
| -
|
| - const FullHashCallback& callback = it->second;
|
| - std::vector<SBFullHashResult> full_hashes;
|
| - base::TimeDelta negative_cache_duration;
|
| - if (status.is_success() && response_code == net::HTTP_OK) {
|
| - RecordGetHashResult(GET_HASH_STATUS_200);
|
| - ResetGetHashErrors();
|
| - std::string data;
|
| - source->GetResponseAsString(&data);
|
| - if (!ParseHashResponse(data, &full_hashes, &negative_cache_duration)) {
|
| - full_hashes.clear();
|
| - RecordGetHashResult(GET_HASH_PARSE_ERROR);
|
| - }
|
| - } else {
|
| - HandleGetHashError(Time::Now());
|
| -
|
| - DVLOG(1) << "SafeBrowsing GetEncodedFullHashes request for: "
|
| - << source->GetURL() << " failed with error: " << status.error()
|
| - << " and response code: " << response_code;
|
| -
|
| - if (status.status() == net::URLRequestStatus::FAILED) {
|
| - RecordGetHashResult(GET_HASH_NETWORK_ERROR);
|
| - } else {
|
| - RecordGetHashResult(GET_HASH_HTTP_ERROR);
|
| - }
|
| - }
|
| -
|
| - // Invoke the callback with full_hashes, even if there was a parse error or
|
| - // an error response code (in which case full_hashes will be empty). The
|
| - // caller can't be blocked indefinitely.
|
| - callback.Run(full_hashes, negative_cache_duration);
|
| -
|
| - hash_requests_.erase(it);
|
| -}
|
| -
|
| -void V4ProtocolManager::HandleGetHashError(const Time& now) {
|
| - DCHECK(CalledOnValidThread());
|
| - base::TimeDelta next = GetNextBackOffInterval(&gethash_error_count_,
|
| - &gethash_back_off_mult_);
|
| - next_gethash_time_ = now + next;
|
| -}
|
| -
|
| -// The API hash call uses the pver4 Safe Browsing server.
|
| -GURL V4ProtocolManager::GetHashUrl(const std::string& request_base64) const {
|
| - std::string url = ComposePver4Url(kSbV4UrlPrefix, "encodedFullHashes",
|
| - request_base64, client_name_, version_, key_param_);
|
| - return GURL(url);
|
| -}
|
| -
|
| -// static
|
| -std::string V4ProtocolManager::ComposePver4Url(const std::string& prefix,
|
| - const std::string& method,
|
| - const std::string& request_base64,
|
| - const std::string& client_id,
|
| - const std::string& version,
|
| - const std::string& key_param) {
|
| - DCHECK(!prefix.empty() && !method.empty() &&
|
| - !client_id.empty() && !version.empty());
|
| - std::string url = base::StringPrintf(
|
| - "%s/%s/%s?alt=proto&client_id=%s&client_version=%s",
|
| - prefix.c_str(), method.c_str(), request_base64.c_str(),
|
| - client_id.c_str(), version.c_str());
|
| - if (!key_param.empty()) {
|
| - base::StringAppendF(&url, "&key=%s",
|
| - net::EscapeQueryParamValue(key_param, true).c_str());
|
| - }
|
| - return url;
|
| -}
|
| -
|
| -} // namespace safe_browsing
|
|
|