Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/prefs/pref_service.h"
     14 #include "base/stl_util.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/time/time.h"
     17 #include "chrome/browser/browser_process.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/common/pref_names.h"
     21 #include "chrome/common/safe_browsing/client_model.pb.h"
     22 #include "chrome/common/safe_browsing/csd.pb.h"
     23 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
     24 #include "content/public/browser/browser_thread.h"
     25 #include "content/public/browser/notification_service.h"
     26 #include "content/public/browser/notification_types.h"
     27 #include "content/public/browser/render_process_host.h"
     28 #include "crypto/sha2.h"
     29 #include "google_apis/google_api_keys.h"
     30 #include "net/base/escape.h"
     31 #include "net/base/load_flags.h"
     32 #include "net/http/http_response_headers.h"
     33 #include "net/http/http_status_code.h"
     34 #include "net/url_request/url_fetcher.h"
     35 #include "net/url_request/url_request_context_getter.h"
     36 #include "net/url_request/url_request_status.h"
     37 #include "url/gurl.h"
     38 
     39 using content::BrowserThread;
     40 
     41 namespace safe_browsing {
     42 
     43 namespace {
     44 
     45   // malware report type for UMA histogram counting.
     46   enum MalwareReportTypes {
     47     REPORT_SENT,
     48     REPORT_HIT_LIMIT,
     49     REPORT_FAILED_SERIALIZATION,
     50 
     51     // Always at the end
     52     REPORT_RESULT_MAX
     53   };
     54 
     55   void UpdateEnumUMAHistogram(MalwareReportTypes report_type) {
     56     DCHECK(report_type >= 0 && report_type < REPORT_RESULT_MAX);
     57     UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
     58                               report_type, REPORT_RESULT_MAX);
     59   }
     60 
     61 }  // namespace
     62 
     63 const size_t ClientSideDetectionService::kMaxModelSizeBytes = 90 * 1024;
     64 const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
     65 // TODO(noelutz): once we know this mechanism works as intended we should fetch
     66 // the model much more frequently.  E.g., every 5 minutes or so.
     67 const int ClientSideDetectionService::kClientModelFetchIntervalMs = 3600 * 1000;
     68 const int ClientSideDetectionService::kInitialClientModelFetchDelayMs = 10000;
     69 
     70 const int ClientSideDetectionService::kReportsIntervalDays = 1;
     71 const int ClientSideDetectionService::kNegativeCacheIntervalDays = 1;
     72 const int ClientSideDetectionService::kPositiveCacheIntervalMinutes = 30;
     73 
     74 const char ClientSideDetectionService::kClientReportPhishingUrl[] =
     75     "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
     76 const char ClientSideDetectionService::kClientReportMalwareUrl[] =
     77     "https://sb-ssl.google.com/safebrowsing/clientreport/malware-check";
     78 const char ClientSideDetectionService::kClientModelUrl[] =
     79     "https://ssl.gstatic.com/safebrowsing/csd/client_model_v4.pb";
     80 
     81 struct ClientSideDetectionService::ClientReportInfo {
     82   ClientReportPhishingRequestCallback callback;
     83   GURL phishing_url;
     84 };
     85 
     86 ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
     87     : is_phishing(phish),
     88       timestamp(time) {}
     89 
     90 ClientSideDetectionService::ClientSideDetectionService(
     91     net::URLRequestContextGetter* request_context_getter)
     92     : enabled_(false),
     93       weak_factory_(this),
     94       request_context_getter_(request_context_getter) {
     95   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
     96                  content::NotificationService::AllBrowserContextsAndSources());
     97 }
     98 
     99 ClientSideDetectionService::~ClientSideDetectionService() {
    100   weak_factory_.InvalidateWeakPtrs();
    101   STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
    102                                  client_phishing_reports_.end());
    103   client_phishing_reports_.clear();
    104   STLDeleteContainerPairPointers(client_malware_reports_.begin(),
    105                                  client_malware_reports_.end());
    106   client_malware_reports_.clear();
    107 }
    108 
    109 // static
    110 ClientSideDetectionService* ClientSideDetectionService::Create(
    111     net::URLRequestContextGetter* request_context_getter) {
    112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    113   scoped_ptr<ClientSideDetectionService> service(
    114       new ClientSideDetectionService(request_context_getter));
    115   if (!service->InitializePrivateNetworks()) {
    116     UMA_HISTOGRAM_COUNTS("SBClientPhishing.InitPrivateNetworksFailed", 1);
    117     return NULL;
    118   }
    119   return service.release();
    120 }
    121 
    122 void ClientSideDetectionService::SetEnabledAndRefreshState(bool enabled) {
    123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    124   SendModelToRenderers();  // always refresh the renderer state
    125   if (enabled == enabled_)
    126     return;
    127   enabled_ = enabled;
    128   if (enabled_) {
    129     // Refresh the model when the service is enabled.  This can happen when the
    130     // preference is toggled, or early during startup if the preference is
    131     // already enabled. In a lot of cases the model will be in the cache so it
    132     // won't actually be fetched from the network.
    133     // We delay the first model fetch to avoid slowing down browser startup.
    134     ScheduleFetchModel(kInitialClientModelFetchDelayMs);
    135   } else {
    136     // Cancel pending requests.
    137     model_fetcher_.reset();
    138     // Invoke pending callbacks with a false verdict.
    139     for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
    140              client_phishing_reports_.begin();
    141          it != client_phishing_reports_.end(); ++it) {
    142       ClientReportInfo* info = it->second;
    143       if (!info->callback.is_null())
    144         info->callback.Run(info->phishing_url, false);
    145     }
    146     STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
    147                                    client_phishing_reports_.end());
    148     client_phishing_reports_.clear();
    149     for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
    150              client_malware_reports_.begin();
    151          it != client_malware_reports_.end(); ++it) {
    152       ClientReportInfo* info = it->second;
    153       if (!info->callback.is_null())
    154         info->callback.Run(info->phishing_url, false);
    155     }
    156     STLDeleteContainerPairPointers(client_malware_reports_.begin(),
    157                                    client_malware_reports_.end());
    158     client_malware_reports_.clear();
    159     cache_.clear();
    160   }
    161 }
    162 
    163 void ClientSideDetectionService::SendClientReportPhishingRequest(
    164     ClientPhishingRequest* verdict,
    165     const ClientReportPhishingRequestCallback& callback) {
    166   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    167   base::MessageLoop::current()->PostTask(
    168       FROM_HERE,
    169       base::Bind(&ClientSideDetectionService::StartClientReportPhishingRequest,
    170                  weak_factory_.GetWeakPtr(), verdict, callback));
    171 }
    172 
    173 void ClientSideDetectionService::SendClientReportMalwareRequest(
    174     ClientMalwareRequest* verdict,
    175     const ClientReportMalwareRequestCallback& callback) {
    176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    177   base::MessageLoop::current()->PostTask(
    178       FROM_HERE,
    179       base::Bind(&ClientSideDetectionService::StartClientReportMalwareRequest,
    180                  weak_factory_.GetWeakPtr(), verdict, callback));
    181 }
    182 
    183 bool ClientSideDetectionService::IsPrivateIPAddress(
    184     const std::string& ip_address) const {
    185   net::IPAddressNumber ip_number;
    186   if (!net::ParseIPLiteralToNumber(ip_address, &ip_number)) {
    187     VLOG(2) << "Unable to parse IP address: '" << ip_address << "'";
    188     // Err on the side of safety and assume this might be private.
    189     return true;
    190   }
    191 
    192   for (std::vector<AddressRange>::const_iterator it =
    193            private_networks_.begin();
    194        it != private_networks_.end(); ++it) {
    195     if (net::IPNumberMatchesPrefix(ip_number, it->first, it->second)) {
    196       return true;
    197     }
    198   }
    199   return false;
    200 }
    201 
    202 bool ClientSideDetectionService::IsBadIpAddress(
    203     const std::string& ip_address) const {
    204   net::IPAddressNumber ip_number;
    205   if (!net::ParseIPLiteralToNumber(ip_address, &ip_number)) {
    206     VLOG(2) << "Unable to parse IP address: '" << ip_address << "'";
    207     return false;
    208   }
    209   if (ip_number.size() == net::kIPv4AddressSize) {
    210     ip_number = net::ConvertIPv4NumberToIPv6Number(ip_number);
    211   }
    212   if (ip_number.size() != net::kIPv6AddressSize) {
    213     VLOG(2) << "Unable to convert IPv4 address to IPv6: '" << ip_address << "'";
    214     return false;  // better safe than sorry.
    215   }
    216   for (BadSubnetMap::const_iterator it = bad_subnets_.begin();
    217        it != bad_subnets_.end(); ++it) {
    218     const std::string& mask = it->first;
    219     DCHECK_EQ(mask.size(), ip_number.size());
    220     std::string subnet(net::kIPv6AddressSize, '.');
    221     for (size_t i = 0; i < net::kIPv6AddressSize; ++i) {
    222       subnet[i] = ip_number[i] & mask[i];
    223     }
    224     if (it->second.count(crypto::SHA256HashString(subnet)) > 0) {
    225       return true;
    226     }
    227   }
    228   return false;
    229 }
    230 
    231 void ClientSideDetectionService::OnURLFetchComplete(
    232     const net::URLFetcher* source) {
    233   std::string data;
    234   source->GetResponseAsString(&data);
    235   if (source == model_fetcher_.get()) {
    236     HandleModelResponse(
    237         source, source->GetURL(), source->GetStatus(),
    238         source->GetResponseCode(), source->GetCookies(), data);
    239   } else if (client_phishing_reports_.find(source) !=
    240              client_phishing_reports_.end()) {
    241     HandlePhishingVerdict(
    242         source, source->GetURL(), source->GetStatus(),
    243         source->GetResponseCode(), source->GetCookies(), data);
    244   } else if (client_malware_reports_.find(source) !=
    245              client_malware_reports_.end()) {
    246     HandleMalwareVerdict(
    247         source, source->GetURL(), source->GetStatus(),
    248         source->GetResponseCode(), source->GetCookies(), data);
    249   } else {
    250     NOTREACHED();
    251   }
    252 }
    253 
    254 void ClientSideDetectionService::Observe(
    255     int type,
    256     const content::NotificationSource& source,
    257     const content::NotificationDetails& details) {
    258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    259   DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
    260   if (!model_.get()) {
    261     // Model might not be ready or maybe there was an error.
    262     return;
    263   }
    264   SendModelToProcess(
    265       content::Source<content::RenderProcessHost>(source).ptr());
    266 }
    267 
    268 void ClientSideDetectionService::SendModelToProcess(
    269     content::RenderProcessHost* process) {
    270   // The ClientSideDetectionService is enabled if _any_ active profile has
    271   // SafeBrowsing turned on.  Here we check the profile for each renderer
    272   // process and only send the model to those that have SafeBrowsing enabled.
    273   Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
    274   std::string model;
    275   if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
    276     VLOG(2) << "Sending phishing model to RenderProcessHost @" << process;
    277     model = model_str_;
    278   } else {
    279     VLOG(2) << "Disabling client-side phishing detection for "
    280             << "RenderProcessHost @" << process;
    281   }
    282   process->Send(new SafeBrowsingMsg_SetPhishingModel(model));
    283 }
    284 
    285 void ClientSideDetectionService::SendModelToRenderers() {
    286   for (content::RenderProcessHost::iterator i(
    287           content::RenderProcessHost::AllHostsIterator());
    288        !i.IsAtEnd(); i.Advance()) {
    289     SendModelToProcess(i.GetCurrentValue());
    290   }
    291 }
    292 
    293 void ClientSideDetectionService::ScheduleFetchModel(int64 delay_ms) {
    294   if (CommandLine::ForCurrentProcess()->HasSwitch(
    295       switches::kSbDisableAutoUpdate))
    296     return;
    297   base::MessageLoop::current()->PostDelayedTask(
    298       FROM_HERE,
    299       base::Bind(&ClientSideDetectionService::StartFetchModel,
    300                  weak_factory_.GetWeakPtr()),
    301       base::TimeDelta::FromMilliseconds(delay_ms));
    302 }
    303 
    304 void ClientSideDetectionService::StartFetchModel() {
    305   if (enabled_) {
    306     // Start fetching the model either from the cache or possibly from the
    307     // network if the model isn't in the cache.
    308     model_fetcher_.reset(net::URLFetcher::Create(
    309         0 /* ID used for testing */, GURL(kClientModelUrl),
    310         net::URLFetcher::GET, this));
    311     model_fetcher_->SetRequestContext(request_context_getter_.get());
    312     model_fetcher_->Start();
    313   }
    314 }
    315 
    316 void ClientSideDetectionService::EndFetchModel(ClientModelStatus status) {
    317   UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.ClientModelStatus",
    318                             status,
    319                             MODEL_STATUS_MAX);
    320   if (status == MODEL_SUCCESS) {
    321     SetBadSubnets(*model_, &bad_subnets_);
    322     SendModelToRenderers();
    323   }
    324   int delay_ms = kClientModelFetchIntervalMs;
    325   // If the most recently fetched model had a valid max-age and the model was
    326   // valid we're scheduling the next model update for after the max-age expired.
    327   if (model_max_age_.get() &&
    328       (status == MODEL_SUCCESS || status == MODEL_NOT_CHANGED)) {
    329     // We're adding 60s of additional delay to make sure we're past
    330     // the model's age.
    331     *model_max_age_ += base::TimeDelta::FromMinutes(1);
    332     delay_ms = model_max_age_->InMilliseconds();
    333   }
    334   model_max_age_.reset();
    335 
    336   // Schedule the next model reload.
    337   ScheduleFetchModel(delay_ms);
    338 }
    339 
    340 void ClientSideDetectionService::StartClientReportPhishingRequest(
    341     ClientPhishingRequest* verdict,
    342     const ClientReportPhishingRequestCallback& callback) {
    343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    344   scoped_ptr<ClientPhishingRequest> request(verdict);
    345 
    346   if (!enabled_) {
    347     if (!callback.is_null())
    348       callback.Run(GURL(request->url()), false);
    349     return;
    350   }
    351 
    352   std::string request_data;
    353   if (!request->SerializeToString(&request_data)) {
    354     UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSerialized", 1);
    355     VLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
    356     if (!callback.is_null())
    357       callback.Run(GURL(request->url()), false);
    358     return;
    359   }
    360 
    361   net::URLFetcher* fetcher = net::URLFetcher::Create(
    362       0 /* ID used for testing */,
    363       GURL(GetClientReportUrl(kClientReportPhishingUrl)),
    364       net::URLFetcher::POST, this);
    365 
    366   // Remember which callback and URL correspond to the current fetcher object.
    367   ClientReportInfo* info = new ClientReportInfo;
    368   info->callback = callback;
    369   info->phishing_url = GURL(request->url());
    370   client_phishing_reports_[fetcher] = info;
    371 
    372   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    373   fetcher->SetRequestContext(request_context_getter_.get());
    374   fetcher->SetUploadData("application/octet-stream", request_data);
    375   fetcher->Start();
    376 
    377   // Record that we made a request
    378   phishing_report_times_.push(base::Time::Now());
    379 }
    380 
    381 void ClientSideDetectionService::StartClientReportMalwareRequest(
    382     ClientMalwareRequest* verdict,
    383     const ClientReportMalwareRequestCallback& callback) {
    384   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    385   scoped_ptr<ClientMalwareRequest> request(verdict);
    386 
    387   if (!enabled_) {
    388     if (!callback.is_null())
    389       callback.Run(GURL(request->url()), false);
    390     return;
    391   }
    392 
    393   if (OverMalwareReportLimit()) {
    394     UpdateEnumUMAHistogram(REPORT_HIT_LIMIT);
    395     DVLOG(1) << "Too many malware report requests sent recently."
    396              << "Skip sending malware report for " << GURL(request->url());
    397     if (!callback.is_null())
    398       callback.Run(GURL(request->url()), false);
    399     return;
    400   }
    401 
    402   std::string request_data;
    403   if (!request->SerializeToString(&request_data)) {
    404     UpdateEnumUMAHistogram(REPORT_FAILED_SERIALIZATION);
    405     DVLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
    406     if (!callback.is_null())
    407       callback.Run(GURL(request->url()), false);
    408     return;
    409   }
    410 
    411   net::URLFetcher* fetcher = net::URLFetcher::Create(
    412       0 /* ID used for testing */,
    413       GURL(GetClientReportUrl(kClientReportMalwareUrl)),
    414       net::URLFetcher::POST, this);
    415 
    416   // Remember which callback and URL correspond to the current fetcher object.
    417   // TODO: need to modify to malware specific code
    418   ClientReportInfo* info = new ClientReportInfo;
    419   info->callback = callback;
    420   info->phishing_url = GURL(request->url());
    421   client_malware_reports_[fetcher] = info;
    422 
    423   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    424   fetcher->SetRequestContext(request_context_getter_.get());
    425   fetcher->SetUploadData("application/octet-stream", request_data);
    426   fetcher->Start();
    427 
    428   UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
    429                             REPORT_SENT, REPORT_RESULT_MAX);
    430 
    431   // Record that we made a malware request
    432   malware_report_times_.push(base::Time::Now());
    433 }
    434 
    435 void ClientSideDetectionService::HandleModelResponse(
    436     const net::URLFetcher* source,
    437     const GURL& url,
    438     const net::URLRequestStatus& status,
    439     int response_code,
    440     const net::ResponseCookies& cookies,
    441     const std::string& data) {
    442   base::TimeDelta max_age;
    443   if (status.is_success() && net::HTTP_OK == response_code &&
    444       source->GetResponseHeaders() &&
    445       source->GetResponseHeaders()->GetMaxAgeValue(&max_age)) {
    446     model_max_age_.reset(new base::TimeDelta(max_age));
    447   }
    448   scoped_ptr<ClientSideModel> model(new ClientSideModel());
    449   ClientModelStatus model_status;
    450   if (!status.is_success() || net::HTTP_OK != response_code) {
    451     model_status = MODEL_FETCH_FAILED;
    452   } else if (data.empty()) {
    453     model_status = MODEL_EMPTY;
    454   } else if (data.size() > kMaxModelSizeBytes) {
    455     model_status = MODEL_TOO_LARGE;
    456   } else if (!model->ParseFromString(data)) {
    457     model_status = MODEL_PARSE_ERROR;
    458   } else if (!model->IsInitialized() || !model->has_version()) {
    459     model_status = MODEL_MISSING_FIELDS;
    460   } else if (!ModelHasValidHashIds(*model)) {
    461     model_status = MODEL_BAD_HASH_IDS;
    462   } else if (model->version() < 0 ||
    463              (model_.get() && model->version() < model_->version())) {
    464     model_status = MODEL_INVALID_VERSION_NUMBER;
    465   } else if (model_.get() && model->version() == model_->version()) {
    466     model_status = MODEL_NOT_CHANGED;
    467   } else {
    468     // The model is valid => replace the existing model with the new one.
    469     model_str_.assign(data);
    470     model_.swap(model);
    471     model_status = MODEL_SUCCESS;
    472   }
    473   EndFetchModel(model_status);
    474 }
    475 
    476 void ClientSideDetectionService::HandlePhishingVerdict(
    477     const net::URLFetcher* source,
    478     const GURL& url,
    479     const net::URLRequestStatus& status,
    480     int response_code,
    481     const net::ResponseCookies& cookies,
    482     const std::string& data) {
    483   ClientPhishingResponse response;
    484   scoped_ptr<ClientReportInfo> info(client_phishing_reports_[source]);
    485   bool is_phishing = false;
    486   if (status.is_success() && net::HTTP_OK == response_code &&
    487       response.ParseFromString(data)) {
    488     // Cache response, possibly flushing an old one.
    489     cache_[info->phishing_url] =
    490         make_linked_ptr(new CacheState(response.phishy(), base::Time::Now()));
    491     is_phishing = response.phishy();
    492   } else {
    493     DLOG(ERROR) << "Unable to get the server verdict for URL: "
    494                 << info->phishing_url << " status: " << status.status() << " "
    495                 << "response_code:" << response_code;
    496   }
    497   if (!info->callback.is_null())
    498     info->callback.Run(info->phishing_url, is_phishing);
    499   client_phishing_reports_.erase(source);
    500   delete source;
    501 }
    502 
    503 void ClientSideDetectionService::HandleMalwareVerdict(
    504     const net::URLFetcher* source,
    505     const GURL& url,
    506     const net::URLRequestStatus& status,
    507     int response_code,
    508     const net::ResponseCookies& cookies,
    509     const std::string& data) {
    510   ClientMalwareResponse response;
    511   scoped_ptr<ClientReportInfo> info(client_malware_reports_[source]);
    512   bool should_blacklist = false;
    513   if (status.is_success() && net::HTTP_OK == response_code &&
    514       response.ParseFromString(data)) {
    515     should_blacklist = response.blacklist();
    516   } else {
    517     DLOG(ERROR) << "Unable to get the server verdict for URL: "
    518                 << info->phishing_url << " status: " << status.status() << " "
    519                 << "response_code:" << response_code;
    520   }
    521   if (!info->callback.is_null())
    522     info->callback.Run(info->phishing_url, should_blacklist);
    523   client_malware_reports_.erase(source);
    524   delete source;
    525 }
    526 
    527 bool ClientSideDetectionService::IsInCache(const GURL& url) {
    528   UpdateCache();
    529 
    530   return cache_.find(url) != cache_.end();
    531 }
    532 
    533 bool ClientSideDetectionService::GetValidCachedResult(const GURL& url,
    534                                                       bool* is_phishing) {
    535   UpdateCache();
    536 
    537   PhishingCache::iterator it = cache_.find(url);
    538   if (it == cache_.end()) {
    539     return false;
    540   }
    541 
    542   // We still need to check if the result is valid.
    543   const CacheState& cache_state = *it->second;
    544   if (cache_state.is_phishing ?
    545       cache_state.timestamp > base::Time::Now() -
    546           base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes) :
    547       cache_state.timestamp > base::Time::Now() -
    548           base::TimeDelta::FromDays(kNegativeCacheIntervalDays)) {
    549     *is_phishing = cache_state.is_phishing;
    550     return true;
    551   }
    552   return false;
    553 }
    554 
    555 void ClientSideDetectionService::UpdateCache() {
    556   // Since we limit the number of requests but allow pass-through for cache
    557   // refreshes, we don't want to remove elements from the cache if they
    558   // could be used for this purpose even if we will not use the entry to
    559   // satisfy the request from the cache.
    560   base::TimeDelta positive_cache_interval =
    561       std::max(base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes),
    562                base::TimeDelta::FromDays(kReportsIntervalDays));
    563   base::TimeDelta negative_cache_interval =
    564       std::max(base::TimeDelta::FromDays(kNegativeCacheIntervalDays),
    565                base::TimeDelta::FromDays(kReportsIntervalDays));
    566 
    567   // Remove elements from the cache that will no longer be used.
    568   for (PhishingCache::iterator it = cache_.begin(); it != cache_.end();) {
    569     const CacheState& cache_state = *it->second;
    570     if (cache_state.is_phishing ?
    571         cache_state.timestamp > base::Time::Now() - positive_cache_interval :
    572         cache_state.timestamp > base::Time::Now() - negative_cache_interval) {
    573       ++it;
    574     } else {
    575       cache_.erase(it++);
    576     }
    577   }
    578 }
    579 
    580 bool ClientSideDetectionService::OverMalwareReportLimit() {
    581   return GetMalwareNumReports() > kMaxReportsPerInterval;
    582 }
    583 
    584 bool ClientSideDetectionService::OverPhishingReportLimit() {
    585   return GetPhishingNumReports() > kMaxReportsPerInterval;
    586 }
    587 
    588 int ClientSideDetectionService::GetMalwareNumReports() {
    589   return GetNumReports(&malware_report_times_);
    590 }
    591 
    592 int ClientSideDetectionService::GetPhishingNumReports() {
    593   return GetNumReports(&phishing_report_times_);
    594 }
    595 
    596 int ClientSideDetectionService::GetNumReports(
    597     std::queue<base::Time>* report_times) {
    598   base::Time cutoff =
    599       base::Time::Now() - base::TimeDelta::FromDays(kReportsIntervalDays);
    600 
    601   // Erase items older than cutoff because we will never care about them again.
    602   while (!report_times->empty() &&
    603          report_times->front() < cutoff) {
    604     report_times->pop();
    605   }
    606 
    607   // Return the number of elements that are above the cutoff.
    608   return report_times->size();
    609 }
    610 
    611 bool ClientSideDetectionService::InitializePrivateNetworks() {
    612   static const char* const kPrivateNetworks[] = {
    613     "10.0.0.0/8",
    614     "127.0.0.0/8",
    615     "172.16.0.0/12",
    616     "192.168.0.0/16",
    617     // IPv6 address ranges
    618     "fc00::/7",
    619     "fec0::/10",
    620     "::1/128",
    621   };
    622 
    623   for (size_t i = 0; i < arraysize(kPrivateNetworks); ++i) {
    624     net::IPAddressNumber ip_number;
    625     size_t prefix_length;
    626     if (net::ParseCIDRBlock(kPrivateNetworks[i], &ip_number, &prefix_length)) {
    627       private_networks_.push_back(std::make_pair(ip_number, prefix_length));
    628     } else {
    629       DLOG(FATAL) << "Unable to parse IP address range: "
    630                   << kPrivateNetworks[i];
    631       return false;
    632     }
    633   }
    634   return true;
    635 }
    636 
    637 // static
    638 void ClientSideDetectionService::SetBadSubnets(const ClientSideModel& model,
    639                                                BadSubnetMap* bad_subnets) {
    640   bad_subnets->clear();
    641   for (int i = 0; i < model.bad_subnet_size(); ++i) {
    642     int size = model.bad_subnet(i).size();
    643     if (size < 0 || size > static_cast<int>(net::kIPv6AddressSize) * 8) {
    644       DLOG(ERROR) << "Invalid bad subnet size: " << size;
    645       continue;
    646     }
    647     if (model.bad_subnet(i).prefix().size() != crypto::kSHA256Length) {
    648       DLOG(ERROR) << "Invalid bad subnet prefix length: "
    649                   << model.bad_subnet(i).prefix().size();
    650       continue;
    651     }
    652     // We precompute the mask for the given subnet size to speed up lookups.
    653     // Basically we need to create a 16B long string which has the highest
    654     // |size| bits sets to one.
    655     std::string mask(net::kIPv6AddressSize, '\x00');
    656     mask.replace(0, size / 8, size / 8, '\xFF');
    657     if (size % 8) {
    658       mask[size / 8] = 0xFF << (8 - (size % 8));
    659     }
    660     (*bad_subnets)[mask].insert(model.bad_subnet(i).prefix());
    661   }
    662 }
    663 
    664 // static
    665 bool ClientSideDetectionService::ModelHasValidHashIds(
    666     const ClientSideModel& model) {
    667   const int max_index = model.hashes_size() - 1;
    668   for (int i = 0; i < model.rule_size(); ++i) {
    669     for (int j = 0; j < model.rule(i).feature_size(); ++j) {
    670       if (model.rule(i).feature(j) < 0 ||
    671           model.rule(i).feature(j) > max_index) {
    672         return false;
    673       }
    674     }
    675   }
    676   for (int i = 0; i < model.page_term_size(); ++i) {
    677     if (model.page_term(i) < 0 || model.page_term(i) > max_index) {
    678       return false;
    679     }
    680   }
    681   return true;
    682 }
    683 
    684 // static
    685 std::string ClientSideDetectionService::GetClientReportUrl(
    686     const std::string& report_url) {
    687   std::string url = report_url;
    688   std::string api_key = google_apis::GetAPIKey();
    689   if (!api_key.empty()) {
    690     base::StringAppendF(&url, "?key=%s",
    691                         net::EscapeQueryParamValue(api_key, true).c_str());
    692   }
    693   return url;
    694 }
    695 }  // namespace safe_browsing
    696