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