1 // Copyright (c) 2011 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/protocol_manager.h" 6 7 #ifndef NDEBUG 8 #include "base/base64.h" 9 #endif 10 #include "base/environment.h" 11 #include "base/logging.h" 12 #include "base/metrics/histogram.h" 13 #include "base/rand_util.h" 14 #include "base/stl_util-inl.h" 15 #include "base/string_util.h" 16 #include "base/task.h" 17 #include "base/timer.h" 18 #include "chrome/browser/safe_browsing/protocol_parser.h" 19 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 20 #include "chrome/common/chrome_version_info.h" 21 #include "chrome/common/env_vars.h" 22 #include "content/browser/browser_thread.h" 23 #include "net/base/escape.h" 24 #include "net/base/load_flags.h" 25 #include "net/url_request/url_request_context_getter.h" 26 #include "net/url_request/url_request_status.h" 27 28 using base::Time; 29 using base::TimeDelta; 30 31 // Maximum time, in seconds, from start up before we must issue an update query. 32 static const int kSbTimerStartIntervalSec = 5 * 60; 33 34 // The maximum time, in seconds, to wait for a response to an update request. 35 static const int kSbMaxUpdateWaitSec = 10; 36 37 // Maximum back off multiplier. 38 static const int kSbMaxBackOff = 8; 39 40 // The default SBProtocolManagerFactory. 41 class SBProtocolManagerFactoryImpl : public SBProtocolManagerFactory { 42 public: 43 SBProtocolManagerFactoryImpl() { } 44 virtual ~SBProtocolManagerFactoryImpl() { } 45 virtual SafeBrowsingProtocolManager* CreateProtocolManager( 46 SafeBrowsingService* sb_service, 47 const std::string& client_name, 48 const std::string& client_key, 49 const std::string& wrapped_key, 50 net::URLRequestContextGetter* request_context_getter, 51 const std::string& info_url_prefix, 52 const std::string& mackey_url_prefix, 53 bool disable_auto_update) { 54 return new SafeBrowsingProtocolManager( 55 sb_service, client_name, client_key, wrapped_key, 56 request_context_getter, info_url_prefix, mackey_url_prefix, 57 disable_auto_update); 58 } 59 private: 60 DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactoryImpl); 61 }; 62 63 // SafeBrowsingProtocolManager implementation ---------------------------------- 64 65 // static 66 SBProtocolManagerFactory* SafeBrowsingProtocolManager::factory_ = NULL; 67 68 // static 69 SafeBrowsingProtocolManager* SafeBrowsingProtocolManager::Create( 70 SafeBrowsingService* sb_service, 71 const std::string& client_name, 72 const std::string& client_key, 73 const std::string& wrapped_key, 74 net::URLRequestContextGetter* request_context_getter, 75 const std::string& info_url_prefix, 76 const std::string& mackey_url_prefix, 77 bool disable_auto_update) { 78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 79 if (!factory_) 80 factory_ = new SBProtocolManagerFactoryImpl(); 81 return factory_->CreateProtocolManager(sb_service, client_name, client_key, 82 wrapped_key, request_context_getter, 83 info_url_prefix, mackey_url_prefix, 84 disable_auto_update); 85 } 86 87 SafeBrowsingProtocolManager::SafeBrowsingProtocolManager( 88 SafeBrowsingService* sb_service, 89 const std::string& client_name, 90 const std::string& client_key, 91 const std::string& wrapped_key, 92 net::URLRequestContextGetter* request_context_getter, 93 const std::string& http_url_prefix, 94 const std::string& https_url_prefix, 95 bool disable_auto_update) 96 : sb_service_(sb_service), 97 request_type_(NO_REQUEST), 98 update_error_count_(0), 99 gethash_error_count_(0), 100 update_back_off_mult_(1), 101 gethash_back_off_mult_(1), 102 next_update_sec_(-1), 103 update_state_(FIRST_REQUEST), 104 initial_request_(true), 105 chunk_pending_to_write_(false), 106 client_key_(client_key), 107 wrapped_key_(wrapped_key), 108 update_size_(0), 109 client_name_(client_name), 110 request_context_getter_(request_context_getter), 111 http_url_prefix_(http_url_prefix), 112 https_url_prefix_(https_url_prefix), 113 disable_auto_update_(disable_auto_update) { 114 DCHECK(!http_url_prefix_.empty() && !https_url_prefix_.empty()); 115 116 // Set the backoff multiplier fuzz to a random value between 0 and 1. 117 back_off_fuzz_ = static_cast<float>(base::RandDouble()); 118 // The first update must happen between 1-5 minutes of start up. 119 next_update_sec_ = base::RandInt(60, kSbTimerStartIntervalSec); 120 121 chrome::VersionInfo version_info; 122 if (!version_info.is_valid() || version_info.Version().empty()) 123 version_ = "0.1"; 124 else 125 version_ = version_info.Version(); 126 } 127 128 // static 129 void SafeBrowsingProtocolManager::RecordGetHashResult( 130 bool is_download, ResultType result_type) { 131 if (is_download) { 132 UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResultDownload", result_type, 133 GET_HASH_RESULT_MAX); 134 } else { 135 UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResult", result_type, 136 GET_HASH_RESULT_MAX); 137 } 138 } 139 140 SafeBrowsingProtocolManager::~SafeBrowsingProtocolManager() { 141 // Delete in-progress SafeBrowsing requests. 142 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), 143 hash_requests_.end()); 144 hash_requests_.clear(); 145 146 // Delete in-progress safebrowsing reports (hits and details). 147 STLDeleteContainerPointers(safebrowsing_reports_.begin(), 148 safebrowsing_reports_.end()); 149 safebrowsing_reports_.clear(); 150 } 151 152 // Public API used by the SafeBrowsingService ---------------------------------- 153 154 // We can only have one update or chunk request outstanding, but there may be 155 // multiple GetHash requests pending since we don't want to serialize them and 156 // slow down the user. 157 void SafeBrowsingProtocolManager::GetFullHash( 158 SafeBrowsingService::SafeBrowsingCheck* check, 159 const std::vector<SBPrefix>& prefixes) { 160 // If we are in GetHash backoff, we need to check if we're past the next 161 // allowed time. If we are, we can proceed with the request. If not, we are 162 // required to return empty results (i.e. treat the page as safe). 163 if (gethash_error_count_ && Time::Now() <= next_gethash_time_) { 164 std::vector<SBFullHashResult> full_hashes; 165 sb_service_->HandleGetHashResults(check, full_hashes, false); 166 return; 167 } 168 bool use_mac = !client_key_.empty(); 169 GURL gethash_url = GetHashUrl(use_mac); 170 URLFetcher* fetcher = new URLFetcher(gethash_url, URLFetcher::POST, this); 171 hash_requests_[fetcher] = check; 172 173 std::string get_hash; 174 SafeBrowsingProtocolParser parser; 175 parser.FormatGetHash(prefixes, &get_hash); 176 177 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE); 178 fetcher->set_request_context(request_context_getter_); 179 fetcher->set_upload_data("text/plain", get_hash); 180 fetcher->Start(); 181 } 182 183 void SafeBrowsingProtocolManager::GetNextUpdate() { 184 if (initial_request_) { 185 if (client_key_.empty() || wrapped_key_.empty()) { 186 IssueKeyRequest(); 187 return; 188 } else { 189 initial_request_ = false; 190 } 191 } 192 193 if (!request_.get()) 194 IssueUpdateRequest(); 195 } 196 197 // URLFetcher::Delegate implementation ----------------------------------------- 198 199 // All SafeBrowsing request responses are handled here. 200 // TODO(paulg): Clarify with the SafeBrowsing team whether a failed parse of a 201 // chunk should retry the download and parse of that chunk (and 202 // what back off / how many times to try), and if that effects the 203 // update back off. For now, a failed parse of the chunk means we 204 // drop it. This isn't so bad because the next UPDATE_REQUEST we 205 // do will report all the chunks we have. If that chunk is still 206 // required, the SafeBrowsing servers will tell us to get it again. 207 void SafeBrowsingProtocolManager::OnURLFetchComplete( 208 const URLFetcher* source, 209 const GURL& url, 210 const net::URLRequestStatus& status, 211 int response_code, 212 const ResponseCookies& cookies, 213 const std::string& data) { 214 scoped_ptr<const URLFetcher> fetcher; 215 bool parsed_ok = true; 216 bool must_back_off = false; // Reduce SafeBrowsing service query frequency. 217 218 // See if this is a safebrowsing report fetcher. We don't take any action for 219 // the response to those. 220 std::set<const URLFetcher*>::iterator sit = safebrowsing_reports_.find( 221 source); 222 if (sit != safebrowsing_reports_.end()) { 223 const URLFetcher* report = *sit; 224 safebrowsing_reports_.erase(sit); 225 delete report; 226 return; 227 } 228 229 HashRequests::iterator it = hash_requests_.find(source); 230 if (it != hash_requests_.end()) { 231 // GetHash response. 232 fetcher.reset(it->first); 233 SafeBrowsingService::SafeBrowsingCheck* check = it->second; 234 std::vector<SBFullHashResult> full_hashes; 235 bool can_cache = false; 236 if (response_code == 200 || response_code == 204) { 237 // For tracking our GetHash false positive (204) rate, compared to real 238 // (200) responses. 239 if (response_code == 200) 240 RecordGetHashResult(check->is_download, GET_HASH_STATUS_200); 241 else 242 RecordGetHashResult(check->is_download, GET_HASH_STATUS_204); 243 can_cache = true; 244 gethash_error_count_ = 0; 245 gethash_back_off_mult_ = 1; 246 bool re_key = false; 247 SafeBrowsingProtocolParser parser; 248 parsed_ok = parser.ParseGetHash(data.data(), 249 static_cast<int>(data.length()), 250 client_key_, 251 &re_key, 252 &full_hashes); 253 if (!parsed_ok) { 254 // If we fail to parse it, we must still inform the SafeBrowsingService 255 // so that it doesn't hold up the user's request indefinitely. Not sure 256 // what to do at that point though! 257 full_hashes.clear(); 258 } else { 259 if (re_key) 260 HandleReKey(); 261 } 262 } else { 263 HandleGetHashError(Time::Now()); 264 if (status.status() == net::URLRequestStatus::FAILED) { 265 VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() 266 << " failed with os error: " << status.os_error(); 267 } else { 268 VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() 269 << " failed with error: " << response_code; 270 } 271 } 272 273 // Call back the SafeBrowsingService with full_hashes, even if there was a 274 // parse error or an error response code (in which case full_hashes will be 275 // empty). We can't block the user regardless of the error status. 276 sb_service_->HandleGetHashResults(check, full_hashes, can_cache); 277 278 hash_requests_.erase(it); 279 } else { 280 // Update, chunk or key response. 281 fetcher.reset(request_.release()); 282 283 if (request_type_ == UPDATE_REQUEST) { 284 if (!fetcher.get()) { 285 // We've timed out waiting for an update response, so we've cancelled 286 // the update request and scheduled a new one. Ignore this response. 287 return; 288 } 289 290 // Cancel the update response timeout now that we have the response. 291 update_timer_.Stop(); 292 } 293 294 if (response_code == 200) { 295 // We have data from the SafeBrowsing service. 296 parsed_ok = HandleServiceResponse(source->url(), 297 data.data(), 298 static_cast<int>(data.length())); 299 if (!parsed_ok) { 300 VLOG(1) << "SafeBrowsing request for: " << source->url() 301 << " failed parse."; 302 must_back_off = true; 303 chunk_request_urls_.clear(); 304 UpdateFinished(false); 305 } 306 307 switch (request_type_) { 308 case CHUNK_REQUEST: 309 if (parsed_ok) 310 chunk_request_urls_.pop_front(); 311 break; 312 case GETKEY_REQUEST: 313 if (initial_request_) { 314 // This is the first request we've made this session. Now that we 315 // have the keys, do the regular update request. 316 initial_request_ = false; 317 GetNextUpdate(); 318 return; 319 } 320 break; 321 case UPDATE_REQUEST: 322 if (chunk_request_urls_.empty() && parsed_ok) { 323 // We are up to date since the servers gave us nothing new, so we 324 // are done with this update cycle. 325 UpdateFinished(true); 326 } 327 break; 328 default: 329 NOTREACHED(); 330 break; 331 } 332 } else { 333 // The SafeBrowsing service error, or very bad response code: back off. 334 must_back_off = true; 335 if (request_type_ == CHUNK_REQUEST) 336 chunk_request_urls_.clear(); 337 UpdateFinished(false); 338 if (status.status() == net::URLRequestStatus::FAILED) { 339 VLOG(1) << "SafeBrowsing request for: " << source->url() 340 << " failed with os error: " << status.os_error(); 341 } else { 342 VLOG(1) << "SafeBrowsing request for: " << source->url() 343 << " failed with error: " << response_code; 344 } 345 } 346 } 347 348 // Schedule a new update request if we've finished retrieving all the chunks 349 // from the previous update. We treat the update request and the chunk URLs it 350 // contains as an atomic unit as far as back off is concerned. 351 if (chunk_request_urls_.empty() && 352 (request_type_ == CHUNK_REQUEST || request_type_ == UPDATE_REQUEST)) 353 ScheduleNextUpdate(must_back_off); 354 355 // Get the next chunk if available. 356 IssueChunkRequest(); 357 } 358 359 bool SafeBrowsingProtocolManager::HandleServiceResponse(const GURL& url, 360 const char* data, 361 int length) { 362 SafeBrowsingProtocolParser parser; 363 364 switch (request_type_) { 365 case UPDATE_REQUEST: { 366 int next_update_sec = -1; 367 bool re_key = false; 368 bool reset = false; 369 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes( 370 new std::vector<SBChunkDelete>); 371 std::vector<ChunkUrl> chunk_urls; 372 if (!parser.ParseUpdate(data, length, client_key_, 373 &next_update_sec, &re_key, 374 &reset, chunk_deletes.get(), &chunk_urls)) { 375 return false; 376 } 377 378 last_update_ = Time::Now(); 379 380 if (update_state_ == FIRST_REQUEST) 381 update_state_ = SECOND_REQUEST; 382 else if (update_state_ == SECOND_REQUEST) 383 update_state_ = NORMAL_REQUEST; 384 385 // New time for the next update. 386 if (next_update_sec > 0) { 387 next_update_sec_ = next_update_sec; 388 } else if (update_state_ == SECOND_REQUEST) { 389 next_update_sec_ = base::RandInt(15 * 60, 45 * 60); 390 } 391 392 // We need to request a new set of keys for MAC. 393 if (re_key) 394 HandleReKey(); 395 396 // New chunks to download. 397 if (!chunk_urls.empty()) { 398 UMA_HISTOGRAM_COUNTS("SB2.UpdateUrls", chunk_urls.size()); 399 for (size_t i = 0; i < chunk_urls.size(); ++i) 400 chunk_request_urls_.push_back(chunk_urls[i]); 401 } 402 403 // Handle the case were the SafeBrowsing service tells us to dump our 404 // database. 405 if (reset) { 406 sb_service_->ResetDatabase(); 407 return true; 408 } 409 410 // Chunks to delete from our storage. Pass ownership of 411 // |chunk_deletes|. 412 if (!chunk_deletes->empty()) 413 sb_service_->HandleChunkDelete(chunk_deletes.release()); 414 415 break; 416 } 417 case CHUNK_REQUEST: { 418 UMA_HISTOGRAM_TIMES("SB2.ChunkRequest", 419 base::Time::Now() - chunk_request_start_); 420 421 const ChunkUrl chunk_url = chunk_request_urls_.front(); 422 bool re_key = false; 423 scoped_ptr<SBChunkList> chunks(new SBChunkList); 424 UMA_HISTOGRAM_COUNTS("SB2.ChunkSize", length); 425 update_size_ += length; 426 if (!parser.ParseChunk(chunk_url.list_name, data, length, 427 client_key_, chunk_url.mac, 428 &re_key, chunks.get())) { 429 #ifndef NDEBUG 430 std::string data_str; 431 data_str.assign(data, length); 432 std::string encoded_chunk; 433 base::Base64Encode(data, &encoded_chunk); 434 VLOG(1) << "ParseChunk error for chunk: " << chunk_url.url 435 << ", client_key: " << client_key_ 436 << ", wrapped_key: " << wrapped_key_ 437 << ", mac: " << chunk_url.mac 438 << ", Base64Encode(data): " << encoded_chunk 439 << ", length: " << length; 440 #endif 441 return false; 442 } 443 444 if (re_key) 445 HandleReKey(); 446 447 // Chunks to add to storage. Pass ownership of |chunks|. 448 if (!chunks->empty()) { 449 chunk_pending_to_write_ = true; 450 sb_service_->HandleChunk(chunk_url.list_name, chunks.release()); 451 } 452 453 break; 454 } 455 case GETKEY_REQUEST: { 456 std::string client_key, wrapped_key; 457 if (!parser.ParseNewKey(data, length, &client_key, &wrapped_key)) 458 return false; 459 460 client_key_ = client_key; 461 wrapped_key_ = wrapped_key; 462 BrowserThread::PostTask( 463 BrowserThread::UI, FROM_HERE, 464 NewRunnableMethod( 465 sb_service_, &SafeBrowsingService::OnNewMacKeys, client_key_, 466 wrapped_key_)); 467 break; 468 } 469 470 default: 471 return false; 472 } 473 474 return true; 475 } 476 477 void SafeBrowsingProtocolManager::Initialize() { 478 // Don't want to hit the safe browsing servers on build/chrome bots. 479 scoped_ptr<base::Environment> env(base::Environment::Create()); 480 if (env->HasVar(env_vars::kHeadless)) 481 return; 482 483 ScheduleNextUpdate(false /* no back off */); 484 } 485 486 void SafeBrowsingProtocolManager::ScheduleNextUpdate(bool back_off) { 487 DCHECK_GT(next_update_sec_, 0); 488 489 if (disable_auto_update_) { 490 // Unschedule any current timer. 491 update_timer_.Stop(); 492 return; 493 } 494 // Reschedule with the new update. 495 const int next_update = GetNextUpdateTime(back_off); 496 ForceScheduleNextUpdate(next_update); 497 } 498 499 void SafeBrowsingProtocolManager::ForceScheduleNextUpdate( 500 const int next_update_msec) { 501 DCHECK_GE(next_update_msec, 0); 502 // Unschedule any current timer. 503 update_timer_.Stop(); 504 update_timer_.Start(TimeDelta::FromMilliseconds(next_update_msec), this, 505 &SafeBrowsingProtocolManager::GetNextUpdate); 506 } 507 508 // According to section 5 of the SafeBrowsing protocol specification, we must 509 // back off after a certain number of errors. We only change 'next_update_sec_' 510 // when we receive a response from the SafeBrowsing service. 511 int SafeBrowsingProtocolManager::GetNextUpdateTime(bool back_off) { 512 int next = next_update_sec_; 513 if (back_off) { 514 next = GetNextBackOffTime(&update_error_count_, &update_back_off_mult_); 515 } else { 516 // Successful response means error reset. 517 update_error_count_ = 0; 518 update_back_off_mult_ = 1; 519 } 520 return next * 1000; // milliseconds 521 } 522 523 int SafeBrowsingProtocolManager::GetNextBackOffTime(int* error_count, 524 int* multiplier) { 525 DCHECK(multiplier && error_count); 526 (*error_count)++; 527 if (*error_count > 1 && *error_count < 6) { 528 int next = static_cast<int>(*multiplier * (1 + back_off_fuzz_) * 30 * 60); 529 *multiplier *= 2; 530 if (*multiplier > kSbMaxBackOff) 531 *multiplier = kSbMaxBackOff; 532 return next; 533 } 534 535 if (*error_count >= 6) 536 return 60 * 60 * 8; // 8 hours 537 538 return 60; // 1 minute 539 } 540 541 // This request requires getting a list of all the chunks for each list from the 542 // database asynchronously. The request will be issued when we're called back in 543 // OnGetChunksComplete. 544 // TODO(paulg): We should get this at start up and maintain a ChunkRange cache 545 // to avoid hitting the database with each update request. On the 546 // otherhand, this request will only occur ~20-30 minutes so there 547 // isn't that much overhead. Measure! 548 void SafeBrowsingProtocolManager::IssueUpdateRequest() { 549 request_type_ = UPDATE_REQUEST; 550 sb_service_->UpdateStarted(); 551 } 552 553 void SafeBrowsingProtocolManager::IssueChunkRequest() { 554 // We are only allowed to have one request outstanding at any time. Also, 555 // don't get the next url until the previous one has been written to disk so 556 // that we don't use too much memory. 557 if (request_.get() || chunk_request_urls_.empty() || chunk_pending_to_write_) 558 return; 559 560 ChunkUrl next_chunk = chunk_request_urls_.front(); 561 DCHECK(!next_chunk.url.empty()); 562 GURL chunk_url = NextChunkUrl(next_chunk.url); 563 request_type_ = CHUNK_REQUEST; 564 request_.reset(new URLFetcher(chunk_url, URLFetcher::GET, this)); 565 request_->set_load_flags(net::LOAD_DISABLE_CACHE); 566 request_->set_request_context(request_context_getter_); 567 chunk_request_start_ = base::Time::Now(); 568 request_->Start(); 569 } 570 571 void SafeBrowsingProtocolManager::IssueKeyRequest() { 572 GURL key_url = MacKeyUrl(); 573 request_type_ = GETKEY_REQUEST; 574 request_.reset(new URLFetcher(key_url, URLFetcher::GET, this)); 575 request_->set_load_flags(net::LOAD_DISABLE_CACHE); 576 request_->set_request_context(request_context_getter_); 577 request_->Start(); 578 } 579 580 void SafeBrowsingProtocolManager::OnGetChunksComplete( 581 const std::vector<SBListChunkRanges>& lists, bool database_error) { 582 DCHECK_EQ(request_type_, UPDATE_REQUEST); 583 if (database_error) { 584 UpdateFinished(false); 585 ScheduleNextUpdate(false); 586 return; 587 } 588 589 const bool use_mac = !client_key_.empty(); 590 591 // Format our stored chunks: 592 std::string list_data; 593 bool found_malware = false; 594 bool found_phishing = false; 595 for (size_t i = 0; i < lists.size(); ++i) { 596 list_data.append(FormatList(lists[i], use_mac)); 597 if (lists[i].name == safe_browsing_util::kPhishingList) 598 found_phishing = true; 599 600 if (lists[i].name == safe_browsing_util::kMalwareList) 601 found_malware = true; 602 } 603 604 // If we have an empty database, let the server know we want data for these 605 // lists. 606 if (!found_phishing) 607 list_data.append(FormatList( 608 SBListChunkRanges(safe_browsing_util::kPhishingList), use_mac)); 609 610 if (!found_malware) 611 list_data.append(FormatList( 612 SBListChunkRanges(safe_browsing_util::kMalwareList), use_mac)); 613 614 GURL update_url = UpdateUrl(use_mac); 615 request_.reset(new URLFetcher(update_url, URLFetcher::POST, this)); 616 request_->set_load_flags(net::LOAD_DISABLE_CACHE); 617 request_->set_request_context(request_context_getter_); 618 request_->set_upload_data("text/plain", list_data); 619 request_->Start(); 620 621 // Begin the update request timeout. 622 update_timer_.Start(TimeDelta::FromSeconds(kSbMaxUpdateWaitSec), this, 623 &SafeBrowsingProtocolManager::UpdateResponseTimeout); 624 } 625 626 // If we haven't heard back from the server with an update response, this method 627 // will run. Close the current update session and schedule another update. 628 void SafeBrowsingProtocolManager::UpdateResponseTimeout() { 629 DCHECK_EQ(request_type_, UPDATE_REQUEST); 630 request_.reset(); 631 UpdateFinished(false); 632 ScheduleNextUpdate(false); 633 } 634 635 void SafeBrowsingProtocolManager::OnChunkInserted() { 636 chunk_pending_to_write_ = false; 637 638 if (chunk_request_urls_.empty()) { 639 UMA_HISTOGRAM_LONG_TIMES("SB2.Update", Time::Now() - last_update_); 640 UpdateFinished(true); 641 } else { 642 IssueChunkRequest(); 643 } 644 } 645 646 // Sends a SafeBrowsing "hit" for UMA users. 647 void SafeBrowsingProtocolManager::ReportSafeBrowsingHit( 648 const GURL& malicious_url, 649 const GURL& page_url, 650 const GURL& referrer_url, 651 bool is_subresource, 652 SafeBrowsingService::UrlCheckResult threat_type, 653 const std::string& post_data) { 654 GURL report_url = SafeBrowsingHitUrl(malicious_url, page_url, 655 referrer_url, is_subresource, 656 threat_type); 657 URLFetcher* report = new URLFetcher( 658 report_url, post_data.empty() ? URLFetcher::GET : URLFetcher::POST, this); 659 report->set_load_flags(net::LOAD_DISABLE_CACHE); 660 report->set_request_context(request_context_getter_); 661 if (!post_data.empty()) 662 report->set_upload_data("text/plain", post_data); 663 report->Start(); 664 safebrowsing_reports_.insert(report); 665 } 666 667 // Sends malware details for users who opt-in. 668 void SafeBrowsingProtocolManager::ReportMalwareDetails( 669 const std::string& report) { 670 GURL report_url = MalwareDetailsUrl(); 671 URLFetcher* fetcher = new URLFetcher(report_url, URLFetcher::POST, this); 672 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE); 673 fetcher->set_request_context(request_context_getter_); 674 fetcher->set_upload_data("application/octet-stream", report); 675 // Don't try too hard to send reports on failures. 676 fetcher->set_automatically_retry_on_5xx(false); 677 fetcher->Start(); 678 safebrowsing_reports_.insert(fetcher); 679 } 680 681 682 // static 683 std::string SafeBrowsingProtocolManager::FormatList( 684 const SBListChunkRanges& list, bool use_mac) { 685 std::string formatted_results; 686 formatted_results.append(list.name); 687 formatted_results.append(";"); 688 if (!list.adds.empty()) { 689 formatted_results.append("a:" + list.adds); 690 if (!list.subs.empty() || use_mac) 691 formatted_results.append(":"); 692 } 693 if (!list.subs.empty()) { 694 formatted_results.append("s:" + list.subs); 695 if (use_mac) 696 formatted_results.append(":"); 697 } 698 if (use_mac) 699 formatted_results.append("mac"); 700 formatted_results.append("\n"); 701 702 return formatted_results; 703 } 704 705 void SafeBrowsingProtocolManager::HandleReKey() { 706 client_key_.clear(); 707 wrapped_key_.clear(); 708 IssueKeyRequest(); 709 } 710 711 void SafeBrowsingProtocolManager::HandleGetHashError(const Time& now) { 712 int next = GetNextBackOffTime(&gethash_error_count_, &gethash_back_off_mult_); 713 next_gethash_time_ = now + TimeDelta::FromSeconds(next); 714 } 715 716 void SafeBrowsingProtocolManager::UpdateFinished(bool success) { 717 UMA_HISTOGRAM_COUNTS("SB2.UpdateSize", update_size_); 718 update_size_ = 0; 719 sb_service_->UpdateFinished(success); 720 } 721 722 std::string SafeBrowsingProtocolManager::ComposeUrl( 723 const std::string& prefix, const std::string& method, 724 const std::string& client_name, const std::string& version, 725 const std::string& additional_query) { 726 DCHECK(!prefix.empty() && !method.empty() && 727 !client_name.empty() && !version.empty()); 728 std::string url = StringPrintf("%s/%s?client=%s&appver=%s&pver=2.2", 729 prefix.c_str(), method.c_str(), 730 client_name.c_str(), version.c_str()); 731 if (!additional_query.empty()) { 732 DCHECK(url.find("?") != std::string::npos); 733 url.append("&"); 734 url.append(additional_query); 735 } 736 return url; 737 } 738 739 GURL SafeBrowsingProtocolManager::UpdateUrl(bool use_mac) const { 740 std::string url = ComposeUrl(http_url_prefix_, "downloads", client_name_, 741 version_, additional_query_); 742 if (use_mac) { 743 url.append("&wrkey="); 744 url.append(wrapped_key_); 745 } 746 return GURL(url); 747 } 748 749 GURL SafeBrowsingProtocolManager::GetHashUrl(bool use_mac) const { 750 std::string url= ComposeUrl(http_url_prefix_, "gethash", client_name_, 751 version_, additional_query_); 752 if (use_mac) { 753 url.append("&wrkey="); 754 url.append(wrapped_key_); 755 } 756 return GURL(url); 757 } 758 759 GURL SafeBrowsingProtocolManager::MacKeyUrl() const { 760 return GURL(ComposeUrl(https_url_prefix_, "newkey", client_name_, version_, 761 additional_query_)); 762 } 763 764 GURL SafeBrowsingProtocolManager::SafeBrowsingHitUrl( 765 const GURL& malicious_url, const GURL& page_url, 766 const GURL& referrer_url, bool is_subresource, 767 SafeBrowsingService::UrlCheckResult threat_type) const { 768 DCHECK(threat_type == SafeBrowsingService::URL_MALWARE || 769 threat_type == SafeBrowsingService::URL_PHISHING || 770 threat_type == SafeBrowsingService::BINARY_MALWARE_URL || 771 threat_type == SafeBrowsingService::BINARY_MALWARE_HASH); 772 // The malware and phishing hits go over HTTP. 773 std::string url = ComposeUrl(http_url_prefix_, "report", client_name_, 774 version_, additional_query_); 775 std::string threat_list = "none"; 776 switch (threat_type) { 777 case SafeBrowsingService::URL_MALWARE: 778 threat_list = "malblhit"; 779 break; 780 case SafeBrowsingService::URL_PHISHING: 781 threat_list = "phishblhit"; 782 break; 783 case SafeBrowsingService::BINARY_MALWARE_URL: 784 threat_list = "binurlhit"; 785 break; 786 case SafeBrowsingService::BINARY_MALWARE_HASH: 787 threat_list = "binhashhit"; 788 break; 789 default: 790 NOTREACHED(); 791 } 792 return GURL(StringPrintf("%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d", 793 url.c_str(), threat_list.c_str(), 794 EscapeQueryParamValue(malicious_url.spec(), true).c_str(), 795 EscapeQueryParamValue(page_url.spec(), true).c_str(), 796 EscapeQueryParamValue(referrer_url.spec(), true).c_str(), 797 is_subresource)); 798 } 799 800 GURL SafeBrowsingProtocolManager::MalwareDetailsUrl() const { 801 // The malware details go over HTTPS. 802 std::string url = StringPrintf( 803 "%s/clientreport/malware?client=%s&appver=%s&pver=1.0", 804 https_url_prefix_.c_str(), 805 client_name_.c_str(), 806 version_.c_str()); 807 return GURL(url); 808 } 809 810 GURL SafeBrowsingProtocolManager::NextChunkUrl(const std::string& url) const { 811 std::string next_url; 812 if (!StartsWithASCII(url, "http://", false) && 813 !StartsWithASCII(url, "https://", false)) { 814 next_url.append("http://"); 815 next_url.append(url); 816 } else { 817 next_url = url; 818 } 819 if (!additional_query_.empty()) { 820 if (next_url.find("?") != std::string::npos) { 821 next_url.append("&"); 822 } else { 823 next_url.append("?"); 824 } 825 next_url.append(additional_query_); 826 } 827 return GURL(next_url); 828 } 829