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