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/download_protection_service.h" 6 7 #include "base/bind.h" 8 #include "base/compiler_specific.h" 9 #include "base/format_macros.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/metrics/field_trial.h" 13 #include "base/metrics/histogram.h" 14 #include "base/metrics/sparse_histogram.h" 15 #include "base/sequenced_task_runner_helpers.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/task/cancelable_task_tracker.h" 21 #include "base/threading/sequenced_worker_pool.h" 22 #include "base/time/time.h" 23 #include "chrome/browser/browser_process.h" 24 #include "chrome/browser/history/history_service.h" 25 #include "chrome/browser/history/history_service_factory.h" 26 #include "chrome/browser/safe_browsing/binary_feature_extractor.h" 27 #include "chrome/browser/safe_browsing/download_feedback_service.h" 28 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 29 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h" 30 #include "chrome/browser/ui/browser.h" 31 #include "chrome/browser/ui/browser_list.h" 32 #include "chrome/common/safe_browsing/csd.pb.h" 33 #include "chrome/common/safe_browsing/download_protection_util.h" 34 #include "chrome/common/safe_browsing/zip_analyzer.h" 35 #include "chrome/common/url_constants.h" 36 #include "components/google/core/browser/google_util.h" 37 #include "content/public/browser/browser_thread.h" 38 #include "content/public/browser/download_item.h" 39 #include "content/public/browser/page_navigator.h" 40 #include "google_apis/google_api_keys.h" 41 #include "net/base/escape.h" 42 #include "net/base/load_flags.h" 43 #include "net/cert/x509_cert_types.h" 44 #include "net/cert/x509_certificate.h" 45 #include "net/http/http_status_code.h" 46 #include "net/url_request/url_fetcher.h" 47 #include "net/url_request/url_fetcher_delegate.h" 48 #include "net/url_request/url_request_context_getter.h" 49 #include "net/url_request/url_request_status.h" 50 51 using content::BrowserThread; 52 53 namespace { 54 static const int64 kDownloadRequestTimeoutMs = 7000; 55 } // namespace 56 57 namespace safe_browsing { 58 59 const char DownloadProtectionService::kDownloadRequestUrl[] = 60 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; 61 62 namespace { 63 // List of extensions for which we track some UMA stats. 64 enum MaliciousExtensionType { 65 EXTENSION_EXE, 66 EXTENSION_MSI, 67 EXTENSION_CAB, 68 EXTENSION_SYS, 69 EXTENSION_SCR, 70 EXTENSION_DRV, 71 EXTENSION_BAT, 72 EXTENSION_ZIP, 73 EXTENSION_RAR, 74 EXTENSION_DLL, 75 EXTENSION_PIF, 76 EXTENSION_COM, 77 EXTENSION_JAR, 78 EXTENSION_CLASS, 79 EXTENSION_PDF, 80 EXTENSION_VB, 81 EXTENSION_REG, 82 EXTENSION_GRP, 83 EXTENSION_OTHER, // Groups all other extensions into one bucket. 84 EXTENSION_CRX, 85 EXTENSION_APK, 86 EXTENSION_DMG, 87 EXTENSION_PKG, 88 EXTENSION_TORRENT, 89 EXTENSION_MAX, 90 }; 91 92 MaliciousExtensionType GetExtensionType(const base::FilePath& f) { 93 if (f.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE; 94 if (f.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI; 95 if (f.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB; 96 if (f.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS; 97 if (f.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR; 98 if (f.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV; 99 if (f.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT; 100 if (f.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP; 101 if (f.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR; 102 if (f.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL; 103 if (f.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF; 104 if (f.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM; 105 if (f.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR; 106 if (f.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS; 107 if (f.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF; 108 if (f.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB; 109 if (f.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG; 110 if (f.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP; 111 if (f.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX; 112 if (f.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK; 113 if (f.MatchesExtension(FILE_PATH_LITERAL(".dmg"))) return EXTENSION_DMG; 114 if (f.MatchesExtension(FILE_PATH_LITERAL(".pkg"))) return EXTENSION_PKG; 115 if (f.MatchesExtension(FILE_PATH_LITERAL(".torrent"))) 116 return EXTENSION_TORRENT; 117 return EXTENSION_OTHER; 118 } 119 120 void RecordFileExtensionType(const base::FilePath& file) { 121 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions", 122 GetExtensionType(file), 123 EXTENSION_MAX); 124 } 125 126 // Enumerate for histogramming purposes. 127 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will 128 // be mixed together based on their values). 129 enum SBStatsType { 130 DOWNLOAD_URL_CHECKS_TOTAL, 131 DOWNLOAD_URL_CHECKS_CANCELED, 132 DOWNLOAD_URL_CHECKS_MALWARE, 133 134 DOWNLOAD_HASH_CHECKS_TOTAL, 135 DOWNLOAD_HASH_CHECKS_MALWARE, 136 137 // Memory space for histograms is determined by the max. 138 // ALWAYS ADD NEW VALUES BEFORE THIS ONE. 139 DOWNLOAD_CHECKS_MAX 140 }; 141 } // namespace 142 143 // Parent SafeBrowsing::Client class used to lookup the bad binary 144 // URL and digest list. There are two sub-classes (one for each list). 145 class DownloadSBClient 146 : public SafeBrowsingDatabaseManager::Client, 147 public base::RefCountedThreadSafe<DownloadSBClient> { 148 public: 149 DownloadSBClient( 150 const content::DownloadItem& item, 151 const DownloadProtectionService::CheckDownloadCallback& callback, 152 const scoped_refptr<SafeBrowsingUIManager>& ui_manager, 153 SBStatsType total_type, 154 SBStatsType dangerous_type) 155 : sha256_hash_(item.GetHash()), 156 url_chain_(item.GetUrlChain()), 157 referrer_url_(item.GetReferrerUrl()), 158 callback_(callback), 159 ui_manager_(ui_manager), 160 start_time_(base::TimeTicks::Now()), 161 total_type_(total_type), 162 dangerous_type_(dangerous_type) {} 163 164 virtual void StartCheck() = 0; 165 virtual bool IsDangerous(SBThreatType threat_type) const = 0; 166 167 protected: 168 friend class base::RefCountedThreadSafe<DownloadSBClient>; 169 virtual ~DownloadSBClient() {} 170 171 void CheckDone(SBThreatType threat_type) { 172 DownloadProtectionService::DownloadCheckResult result = 173 IsDangerous(threat_type) ? 174 DownloadProtectionService::DANGEROUS : 175 DownloadProtectionService::SAFE; 176 BrowserThread::PostTask(BrowserThread::UI, 177 FROM_HERE, 178 base::Bind(callback_, result)); 179 UpdateDownloadCheckStats(total_type_); 180 if (threat_type != SB_THREAT_TYPE_SAFE) { 181 UpdateDownloadCheckStats(dangerous_type_); 182 BrowserThread::PostTask( 183 BrowserThread::UI, 184 FROM_HERE, 185 base::Bind(&DownloadSBClient::ReportMalware, 186 this, threat_type)); 187 } 188 } 189 190 void ReportMalware(SBThreatType threat_type) { 191 std::string post_data; 192 if (!sha256_hash_.empty()) 193 post_data += base::HexEncode(sha256_hash_.data(), 194 sha256_hash_.size()) + "\n"; 195 for (size_t i = 0; i < url_chain_.size(); ++i) { 196 post_data += url_chain_[i].spec() + "\n"; 197 } 198 ui_manager_->ReportSafeBrowsingHit( 199 url_chain_.back(), // malicious_url 200 url_chain_.front(), // page_url 201 referrer_url_, 202 true, // is_subresource 203 threat_type, 204 post_data); 205 } 206 207 void UpdateDownloadCheckStats(SBStatsType stat_type) { 208 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks", 209 stat_type, 210 DOWNLOAD_CHECKS_MAX); 211 } 212 213 std::string sha256_hash_; 214 std::vector<GURL> url_chain_; 215 GURL referrer_url_; 216 DownloadProtectionService::CheckDownloadCallback callback_; 217 scoped_refptr<SafeBrowsingUIManager> ui_manager_; 218 base::TimeTicks start_time_; 219 220 private: 221 const SBStatsType total_type_; 222 const SBStatsType dangerous_type_; 223 224 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient); 225 }; 226 227 class DownloadUrlSBClient : public DownloadSBClient { 228 public: 229 DownloadUrlSBClient( 230 const content::DownloadItem& item, 231 const DownloadProtectionService::CheckDownloadCallback& callback, 232 const scoped_refptr<SafeBrowsingUIManager>& ui_manager, 233 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager) 234 : DownloadSBClient(item, callback, ui_manager, 235 DOWNLOAD_URL_CHECKS_TOTAL, 236 DOWNLOAD_URL_CHECKS_MALWARE), 237 database_manager_(database_manager) { } 238 239 virtual void StartCheck() OVERRIDE { 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 241 if (!database_manager_.get() || 242 database_manager_->CheckDownloadUrl(url_chain_, this)) { 243 CheckDone(SB_THREAT_TYPE_SAFE); 244 } else { 245 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr. 246 } 247 } 248 249 virtual bool IsDangerous(SBThreatType threat_type) const OVERRIDE { 250 return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL; 251 } 252 253 virtual void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain, 254 SBThreatType threat_type) OVERRIDE { 255 CheckDone(threat_type); 256 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration", 257 base::TimeTicks::Now() - start_time_); 258 Release(); 259 } 260 261 protected: 262 virtual ~DownloadUrlSBClient() {} 263 264 private: 265 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; 266 267 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient); 268 }; 269 270 class DownloadProtectionService::CheckClientDownloadRequest 271 : public base::RefCountedThreadSafe< 272 DownloadProtectionService::CheckClientDownloadRequest, 273 BrowserThread::DeleteOnUIThread>, 274 public net::URLFetcherDelegate, 275 public content::DownloadItem::Observer { 276 public: 277 CheckClientDownloadRequest( 278 content::DownloadItem* item, 279 const CheckDownloadCallback& callback, 280 DownloadProtectionService* service, 281 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager, 282 BinaryFeatureExtractor* binary_feature_extractor) 283 : item_(item), 284 url_chain_(item->GetUrlChain()), 285 referrer_url_(item->GetReferrerUrl()), 286 tab_url_(item->GetTabUrl()), 287 tab_referrer_url_(item->GetTabReferrerUrl()), 288 zipped_executable_(false), 289 callback_(callback), 290 service_(service), 291 binary_feature_extractor_(binary_feature_extractor), 292 database_manager_(database_manager), 293 pingback_enabled_(service_->enabled()), 294 finished_(false), 295 type_(ClientDownloadRequest::WIN_EXECUTABLE), 296 weakptr_factory_(this), 297 start_time_(base::TimeTicks::Now()) { 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 299 item_->AddObserver(this); 300 } 301 302 void Start() { 303 VLOG(2) << "Starting SafeBrowsing download check for: " 304 << item_->DebugString(true); 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 306 // TODO(noelutz): implement some cache to make sure we don't issue the same 307 // request over and over again if a user downloads the same binary multiple 308 // times. 309 DownloadCheckResultReason reason = REASON_MAX; 310 if (!IsSupportedDownload( 311 *item_, item_->GetTargetFilePath(), &reason, &type_)) { 312 switch (reason) { 313 case REASON_EMPTY_URL_CHAIN: 314 case REASON_INVALID_URL: 315 PostFinishTask(UNKNOWN, reason); 316 return; 317 318 case REASON_NOT_BINARY_FILE: 319 RecordFileExtensionType(item_->GetTargetFilePath()); 320 PostFinishTask(UNKNOWN, reason); 321 return; 322 323 default: 324 // We only expect the reasons explicitly handled above. 325 NOTREACHED(); 326 } 327 } 328 RecordFileExtensionType(item_->GetTargetFilePath()); 329 330 // Compute features from the file contents. Note that we record histograms 331 // based on the result, so this runs regardless of whether the pingbacks 332 // are enabled. 333 if (item_->GetTargetFilePath().MatchesExtension( 334 FILE_PATH_LITERAL(".zip"))) { 335 StartExtractZipFeatures(); 336 } else { 337 DCHECK(!download_protection_util::IsArchiveFile( 338 item_->GetTargetFilePath())); 339 StartExtractFileFeatures(); 340 } 341 } 342 343 // Start a timeout to cancel the request if it takes too long. 344 // This should only be called after we have finished accessing the file. 345 void StartTimeout() { 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 347 if (!service_) { 348 // Request has already been cancelled. 349 return; 350 } 351 timeout_start_time_ = base::TimeTicks::Now(); 352 BrowserThread::PostDelayedTask( 353 BrowserThread::UI, 354 FROM_HERE, 355 base::Bind(&CheckClientDownloadRequest::Cancel, 356 weakptr_factory_.GetWeakPtr()), 357 base::TimeDelta::FromMilliseconds( 358 service_->download_request_timeout_ms())); 359 } 360 361 // Canceling a request will cause us to always report the result as UNKNOWN 362 // unless a pending request is about to call FinishRequest. 363 void Cancel() { 364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 365 if (fetcher_.get()) { 366 // The DownloadProtectionService is going to release its reference, so we 367 // might be destroyed before the URLFetcher completes. Cancel the 368 // fetcher so it does not try to invoke OnURLFetchComplete. 369 fetcher_.reset(); 370 } 371 // Note: If there is no fetcher, then some callback is still holding a 372 // reference to this object. We'll eventually wind up in some method on 373 // the UI thread that will call FinishRequest() again. If FinishRequest() 374 // is called a second time, it will be a no-op. 375 FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED); 376 // Calling FinishRequest might delete this object, we may be deleted by 377 // this point. 378 } 379 380 // content::DownloadItem::Observer implementation. 381 virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE { 382 Cancel(); 383 DCHECK(item_ == NULL); 384 } 385 386 // From the net::URLFetcherDelegate interface. 387 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 389 DCHECK_EQ(source, fetcher_.get()); 390 VLOG(2) << "Received a response for URL: " 391 << item_->GetUrlChain().back() << ": success=" 392 << source->GetStatus().is_success() << " response_code=" 393 << source->GetResponseCode(); 394 if (source->GetStatus().is_success()) { 395 UMA_HISTOGRAM_SPARSE_SLOWLY( 396 "SBClientDownload.DownloadRequestResponseCode", 397 source->GetResponseCode()); 398 } 399 UMA_HISTOGRAM_SPARSE_SLOWLY( 400 "SBClientDownload.DownloadRequestNetError", 401 -source->GetStatus().error()); 402 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED; 403 DownloadCheckResult result = UNKNOWN; 404 if (source->GetStatus().is_success() && 405 net::HTTP_OK == source->GetResponseCode()) { 406 ClientDownloadResponse response; 407 std::string data; 408 bool got_data = source->GetResponseAsString(&data); 409 DCHECK(got_data); 410 if (!response.ParseFromString(data)) { 411 reason = REASON_INVALID_RESPONSE_PROTO; 412 result = UNKNOWN; 413 } else if (response.verdict() == ClientDownloadResponse::SAFE) { 414 reason = REASON_DOWNLOAD_SAFE; 415 result = SAFE; 416 } else if (service_ && !service_->IsSupportedDownload( 417 *item_, item_->GetTargetFilePath())) { 418 // The client of the download protection service assumes that we don't 419 // support this download so we cannot return any other verdict than 420 // UNKNOWN even if the server says it's dangerous to download this file. 421 // Note: if service_ is NULL we already cancelled the request and 422 // returned UNKNOWN. 423 reason = REASON_DOWNLOAD_NOT_SUPPORTED; 424 result = UNKNOWN; 425 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) { 426 reason = REASON_DOWNLOAD_DANGEROUS; 427 result = DANGEROUS; 428 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) { 429 reason = REASON_DOWNLOAD_UNCOMMON; 430 result = UNCOMMON; 431 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) { 432 reason = REASON_DOWNLOAD_DANGEROUS_HOST; 433 result = DANGEROUS_HOST; 434 } else if ( 435 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) { 436 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED; 437 result = POTENTIALLY_UNWANTED; 438 } else { 439 LOG(DFATAL) << "Unknown download response verdict: " 440 << response.verdict(); 441 reason = REASON_INVALID_RESPONSE_VERDICT; 442 result = UNKNOWN; 443 } 444 DownloadFeedbackService::MaybeStorePingsForDownload( 445 result, item_, client_download_request_data_, data); 446 } 447 // We don't need the fetcher anymore. 448 fetcher_.reset(); 449 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration", 450 base::TimeTicks::Now() - start_time_); 451 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration", 452 base::TimeTicks::Now() - request_start_time_); 453 FinishRequest(result, reason); 454 } 455 456 static bool IsSupportedDownload(const content::DownloadItem& item, 457 const base::FilePath& target_path, 458 DownloadCheckResultReason* reason, 459 ClientDownloadRequest::DownloadType* type) { 460 if (item.GetUrlChain().empty()) { 461 *reason = REASON_EMPTY_URL_CHAIN; 462 return false; 463 } 464 const GURL& final_url = item.GetUrlChain().back(); 465 if (!final_url.is_valid() || final_url.is_empty() || 466 !final_url.IsStandard() || final_url.SchemeIsFile()) { 467 *reason = REASON_INVALID_URL; 468 return false; 469 } 470 if (!download_protection_util::IsBinaryFile(target_path)) { 471 *reason = REASON_NOT_BINARY_FILE; 472 return false; 473 } 474 *type = download_protection_util::GetDownloadType(target_path); 475 return true; 476 } 477 478 private: 479 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; 480 friend class base::DeleteHelper<CheckClientDownloadRequest>; 481 482 virtual ~CheckClientDownloadRequest() { 483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 484 DCHECK(item_ == NULL); 485 } 486 487 void OnFileFeatureExtractionDone() { 488 // This can run in any thread, since it just posts more messages. 489 490 // TODO(noelutz): DownloadInfo should also contain the IP address of 491 // every URL in the redirect chain. We also should check whether the 492 // download URL is hosted on the internal network. 493 BrowserThread::PostTask( 494 BrowserThread::IO, 495 FROM_HERE, 496 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this)); 497 498 // We wait until after the file checks finish to start the timeout, as 499 // windows can cause permissions errors if the timeout fired while we were 500 // checking the file signature and we tried to complete the download. 501 BrowserThread::PostTask( 502 BrowserThread::UI, 503 FROM_HERE, 504 base::Bind(&CheckClientDownloadRequest::StartTimeout, this)); 505 } 506 507 void StartExtractFileFeatures() { 508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 509 DCHECK(item_); // Called directly from Start(), item should still exist. 510 // Since we do blocking I/O, offload this to a worker thread. 511 // The task does not need to block shutdown. 512 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( 513 FROM_HERE, 514 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures, 515 this, item_->GetFullPath()), 516 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 517 } 518 519 void ExtractFileFeatures(const base::FilePath& file_path) { 520 base::TimeTicks start_time = base::TimeTicks::Now(); 521 binary_feature_extractor_->CheckSignature(file_path, &signature_info_); 522 bool is_signed = (signature_info_.certificate_chain_size() > 0); 523 if (is_signed) { 524 VLOG(2) << "Downloaded a signed binary: " << file_path.value(); 525 } else { 526 VLOG(2) << "Downloaded an unsigned binary: " 527 << file_path.value(); 528 } 529 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); 530 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime", 531 base::TimeTicks::Now() - start_time); 532 533 start_time = base::TimeTicks::Now(); 534 binary_feature_extractor_->ExtractImageHeaders(file_path, &image_headers_); 535 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime", 536 base::TimeTicks::Now() - start_time); 537 538 OnFileFeatureExtractionDone(); 539 } 540 541 void StartExtractZipFeatures() { 542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 543 DCHECK(item_); // Called directly from Start(), item should still exist. 544 zip_analysis_start_time_ = base::TimeTicks::Now(); 545 // We give the zip analyzer a weak pointer to this object. Since the 546 // analyzer is refcounted, it might outlive the request. 547 analyzer_ = new SandboxedZipAnalyzer( 548 item_->GetFullPath(), 549 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished, 550 weakptr_factory_.GetWeakPtr())); 551 analyzer_->Start(); 552 } 553 554 void OnZipAnalysisFinished(const zip_analyzer::Results& results) { 555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 556 if (!service_) 557 return; 558 if (results.success) { 559 zipped_executable_ = results.has_executable; 560 VLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value() 561 << ", has_executable=" << results.has_executable 562 << " has_archive=" << results.has_archive; 563 } else { 564 VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value(); 565 } 566 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable", 567 zipped_executable_); 568 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable", 569 results.has_archive && !zipped_executable_); 570 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime", 571 base::TimeTicks::Now() - zip_analysis_start_time_); 572 573 if (!zipped_executable_) { 574 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES); 575 return; 576 } 577 OnFileFeatureExtractionDone(); 578 } 579 580 static void RecordCountOfSignedOrWhitelistedDownload() { 581 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1); 582 } 583 584 void CheckWhitelists() { 585 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 586 587 if (!database_manager_.get()) { 588 PostFinishTask(UNKNOWN, REASON_SB_DISABLED); 589 return; 590 } 591 592 const GURL& url = url_chain_.back(); 593 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) { 594 VLOG(2) << url << " is on the download whitelist."; 595 RecordCountOfSignedOrWhitelistedDownload(); 596 PostFinishTask(SAFE, REASON_WHITELISTED_URL); 597 return; 598 } 599 600 if (signature_info_.trusted()) { 601 RecordCountOfSignedOrWhitelistedDownload(); 602 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) { 603 if (CertificateChainIsWhitelisted( 604 signature_info_.certificate_chain(i))) { 605 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE); 606 return; 607 } 608 } 609 } 610 611 if (!pingback_enabled_) { 612 PostFinishTask(UNKNOWN, REASON_PING_DISABLED); 613 return; 614 } 615 616 // Currently, the UI only works on Windows so we don't even bother with 617 // pinging the server if we're not on Windows. 618 // TODO(noelutz): change this code once the UI is done for Linux and Mac. 619 #if defined(OS_MACOSX) 620 // TODO(mattm): remove this (see crbug.com/414834). 621 if (base::FieldTrialList::FindFullName("SafeBrowsingOSXClientDownloadPings") 622 != "Enabled") { 623 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED); 624 return; 625 } 626 #endif 627 #if defined(OS_WIN) || defined(OS_MACOSX) 628 // The URLFetcher is owned by the UI thread, so post a message to 629 // start the pingback. 630 BrowserThread::PostTask( 631 BrowserThread::UI, 632 FROM_HERE, 633 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this)); 634 #else 635 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED); 636 #endif 637 } 638 639 void GetTabRedirects() { 640 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 641 if (!tab_url_.is_valid()) { 642 SendRequest(); 643 return; 644 } 645 646 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext()); 647 HistoryService* history = 648 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS); 649 if (!history) { 650 SendRequest(); 651 return; 652 } 653 654 history->QueryRedirectsTo( 655 tab_url_, 656 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects, 657 base::Unretained(this), 658 tab_url_), 659 &request_tracker_); 660 } 661 662 void OnGotTabRedirects(const GURL& url, 663 const history::RedirectList* redirect_list) { 664 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 665 DCHECK_EQ(url, tab_url_); 666 667 if (!redirect_list->empty()) { 668 tab_redirects_.insert( 669 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend()); 670 } 671 672 SendRequest(); 673 } 674 675 void SendRequest() { 676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 677 678 // This is our last chance to check whether the request has been canceled 679 // before sending it. 680 if (!service_) 681 return; 682 683 ClientDownloadRequest request; 684 request.set_url(item_->GetUrlChain().back().spec()); 685 request.mutable_digests()->set_sha256(item_->GetHash()); 686 request.set_length(item_->GetReceivedBytes()); 687 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) { 688 ClientDownloadRequest::Resource* resource = request.add_resources(); 689 resource->set_url(item_->GetUrlChain()[i].spec()); 690 if (i == item_->GetUrlChain().size() - 1) { 691 // The last URL in the chain is the download URL. 692 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL); 693 resource->set_referrer(item_->GetReferrerUrl().spec()); 694 DVLOG(2) << "dl url " << resource->url(); 695 if (!item_->GetRemoteAddress().empty()) { 696 resource->set_remote_ip(item_->GetRemoteAddress()); 697 DVLOG(2) << " dl url remote addr: " << resource->remote_ip(); 698 } 699 DVLOG(2) << "dl referrer " << resource->referrer(); 700 } else { 701 DVLOG(2) << "dl redirect " << i << " " << resource->url(); 702 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT); 703 } 704 // TODO(noelutz): fill out the remote IP addresses. 705 } 706 // TODO(mattm): fill out the remote IP addresses for tab resources. 707 for (size_t i = 0; i < tab_redirects_.size(); ++i) { 708 ClientDownloadRequest::Resource* resource = request.add_resources(); 709 DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec(); 710 resource->set_url(tab_redirects_[i].spec()); 711 resource->set_type(ClientDownloadRequest::TAB_REDIRECT); 712 } 713 if (tab_url_.is_valid()) { 714 ClientDownloadRequest::Resource* resource = request.add_resources(); 715 resource->set_url(tab_url_.spec()); 716 DVLOG(2) << "tab url " << resource->url(); 717 resource->set_type(ClientDownloadRequest::TAB_URL); 718 if (tab_referrer_url_.is_valid()) { 719 resource->set_referrer(tab_referrer_url_.spec()); 720 DVLOG(2) << "tab referrer " << resource->referrer(); 721 } 722 } 723 724 request.set_user_initiated(item_->HasUserGesture()); 725 request.set_file_basename( 726 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe()); 727 request.set_download_type(type_); 728 request.mutable_signature()->CopyFrom(signature_info_); 729 request.mutable_image_headers()->CopyFrom(image_headers_); 730 if (!request.SerializeToString(&client_download_request_data_)) { 731 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO); 732 return; 733 } 734 735 VLOG(2) << "Sending a request for URL: " 736 << item_->GetUrlChain().back(); 737 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */, 738 GetDownloadRequestUrl(), 739 net::URLFetcher::POST, 740 this)); 741 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 742 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error. 743 fetcher_->SetRequestContext(service_->request_context_getter_.get()); 744 fetcher_->SetUploadData("application/octet-stream", 745 client_download_request_data_); 746 request_start_time_ = base::TimeTicks::Now(); 747 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize", 748 client_download_request_data_.size()); 749 fetcher_->Start(); 750 } 751 752 void PostFinishTask(DownloadCheckResult result, 753 DownloadCheckResultReason reason) { 754 BrowserThread::PostTask( 755 BrowserThread::UI, 756 FROM_HERE, 757 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result, 758 reason)); 759 } 760 761 void FinishRequest(DownloadCheckResult result, 762 DownloadCheckResultReason reason) { 763 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 764 if (finished_) { 765 return; 766 } 767 finished_ = true; 768 // Ensure the timeout task is cancelled while we still have a non-zero 769 // refcount. (crbug.com/240449) 770 weakptr_factory_.InvalidateWeakPtrs(); 771 if (!request_start_time_.is_null()) { 772 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats", 773 reason, 774 REASON_MAX); 775 } 776 if (!timeout_start_time_.is_null()) { 777 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats", 778 reason, 779 REASON_MAX); 780 if (reason != REASON_REQUEST_CANCELED) { 781 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration", 782 base::TimeTicks::Now() - timeout_start_time_); 783 } 784 } 785 if (service_) { 786 VLOG(2) << "SafeBrowsing download verdict for: " 787 << item_->DebugString(true) << " verdict:" << reason 788 << " result:" << result; 789 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", 790 reason, 791 REASON_MAX); 792 #if defined(OS_MACOSX) 793 // OSX is currently sending pings only for evaluation purposes, ignore 794 // the result for now. 795 // TODO(mattm): remove this and update the ifdef in 796 // DownloadItemImpl::IsDangerous (see crbug.com/413968). 797 result = UNKNOWN; 798 #endif 799 callback_.Run(result); 800 item_->RemoveObserver(this); 801 item_ = NULL; 802 DownloadProtectionService* service = service_; 803 service_ = NULL; 804 service->RequestFinished(this); 805 // DownloadProtectionService::RequestFinished will decrement our refcount, 806 // so we may be deleted now. 807 } else { 808 callback_.Run(UNKNOWN); 809 } 810 } 811 812 bool CertificateChainIsWhitelisted( 813 const ClientDownloadRequest_CertificateChain& chain) { 814 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 815 if (chain.element_size() < 2) { 816 // We need to have both a signing certificate and its issuer certificate 817 // present to construct a whitelist entry. 818 return false; 819 } 820 scoped_refptr<net::X509Certificate> cert = 821 net::X509Certificate::CreateFromBytes( 822 chain.element(0).certificate().data(), 823 chain.element(0).certificate().size()); 824 if (!cert.get()) { 825 return false; 826 } 827 828 for (int i = 1; i < chain.element_size(); ++i) { 829 scoped_refptr<net::X509Certificate> issuer = 830 net::X509Certificate::CreateFromBytes( 831 chain.element(i).certificate().data(), 832 chain.element(i).certificate().size()); 833 if (!issuer.get()) { 834 return false; 835 } 836 std::vector<std::string> whitelist_strings; 837 DownloadProtectionService::GetCertificateWhitelistStrings( 838 *cert.get(), *issuer.get(), &whitelist_strings); 839 for (size_t j = 0; j < whitelist_strings.size(); ++j) { 840 if (database_manager_->MatchDownloadWhitelistString( 841 whitelist_strings[j])) { 842 VLOG(2) << "Certificate matched whitelist, cert=" 843 << cert->subject().GetDisplayName() 844 << " issuer=" << issuer->subject().GetDisplayName(); 845 return true; 846 } 847 } 848 cert = issuer; 849 } 850 return false; 851 } 852 853 // The DownloadItem we are checking. Will be NULL if the request has been 854 // canceled. Must be accessed only on UI thread. 855 content::DownloadItem* item_; 856 // Copies of data from |item_| for access on other threads. 857 std::vector<GURL> url_chain_; 858 GURL referrer_url_; 859 // URL chain of redirects leading to (but not including) |tab_url|. 860 std::vector<GURL> tab_redirects_; 861 // URL and referrer of the window the download was started from. 862 GURL tab_url_; 863 GURL tab_referrer_url_; 864 865 bool zipped_executable_; 866 ClientDownloadRequest_SignatureInfo signature_info_; 867 ClientDownloadRequest_ImageHeaders image_headers_; 868 CheckDownloadCallback callback_; 869 // Will be NULL if the request has been canceled. 870 DownloadProtectionService* service_; 871 scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_; 872 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; 873 const bool pingback_enabled_; 874 scoped_ptr<net::URLFetcher> fetcher_; 875 scoped_refptr<SandboxedZipAnalyzer> analyzer_; 876 base::TimeTicks zip_analysis_start_time_; 877 bool finished_; 878 ClientDownloadRequest::DownloadType type_; 879 std::string client_download_request_data_; 880 base::CancelableTaskTracker request_tracker_; // For HistoryService lookup. 881 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_; 882 base::TimeTicks start_time_; // Used for stats. 883 base::TimeTicks timeout_start_time_; 884 base::TimeTicks request_start_time_; 885 886 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest); 887 }; 888 889 DownloadProtectionService::DownloadProtectionService( 890 SafeBrowsingService* sb_service, 891 net::URLRequestContextGetter* request_context_getter) 892 : request_context_getter_(request_context_getter), 893 enabled_(false), 894 binary_feature_extractor_(new BinaryFeatureExtractor()), 895 download_request_timeout_ms_(kDownloadRequestTimeoutMs), 896 feedback_service_(new DownloadFeedbackService( 897 request_context_getter, BrowserThread::GetBlockingPool())) { 898 899 if (sb_service) { 900 ui_manager_ = sb_service->ui_manager(); 901 database_manager_ = sb_service->database_manager(); 902 } 903 } 904 905 DownloadProtectionService::~DownloadProtectionService() { 906 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 907 CancelPendingRequests(); 908 } 909 910 void DownloadProtectionService::SetEnabled(bool enabled) { 911 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 912 if (enabled == enabled_) { 913 return; 914 } 915 enabled_ = enabled; 916 if (!enabled_) { 917 CancelPendingRequests(); 918 } 919 } 920 921 void DownloadProtectionService::CheckClientDownload( 922 content::DownloadItem* item, 923 const CheckDownloadCallback& callback) { 924 scoped_refptr<CheckClientDownloadRequest> request( 925 new CheckClientDownloadRequest(item, callback, this, 926 database_manager_, 927 binary_feature_extractor_.get())); 928 download_requests_.insert(request); 929 request->Start(); 930 } 931 932 void DownloadProtectionService::CheckDownloadUrl( 933 const content::DownloadItem& item, 934 const CheckDownloadCallback& callback) { 935 DCHECK(!item.GetUrlChain().empty()); 936 scoped_refptr<DownloadUrlSBClient> client( 937 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_)); 938 // The client will release itself once it is done. 939 BrowserThread::PostTask( 940 BrowserThread::IO, 941 FROM_HERE, 942 base::Bind(&DownloadUrlSBClient::StartCheck, client)); 943 } 944 945 bool DownloadProtectionService::IsSupportedDownload( 946 const content::DownloadItem& item, 947 const base::FilePath& target_path) const { 948 // Currently, the UI is only enabled on Windows. On Mac we send the ping but 949 // ignore the result (see ifdef in FinishRequest). On Linux we still 950 // want to show the dangerous file type warning if the file is possibly 951 // dangerous which means we have to always return false here. 952 #if defined(OS_WIN) 953 DownloadCheckResultReason reason = REASON_MAX; 954 ClientDownloadRequest::DownloadType type = 955 ClientDownloadRequest::WIN_EXECUTABLE; 956 return (CheckClientDownloadRequest::IsSupportedDownload( 957 item, target_path, &reason, &type) && 958 (ClientDownloadRequest::CHROME_EXTENSION != type)); 959 #else 960 return false; 961 #endif 962 } 963 964 void DownloadProtectionService::CancelPendingRequests() { 965 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 966 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 967 download_requests_.begin(); 968 it != download_requests_.end();) { 969 // We need to advance the iterator before we cancel because canceling 970 // the request will invalidate it when RequestFinished is called below. 971 scoped_refptr<CheckClientDownloadRequest> tmp = *it++; 972 tmp->Cancel(); 973 } 974 DCHECK(download_requests_.empty()); 975 } 976 977 void DownloadProtectionService::RequestFinished( 978 CheckClientDownloadRequest* request) { 979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 980 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 981 download_requests_.find(request); 982 DCHECK(it != download_requests_.end()); 983 download_requests_.erase(*it); 984 } 985 986 void DownloadProtectionService::ShowDetailsForDownload( 987 const content::DownloadItem& item, 988 content::PageNavigator* navigator) { 989 GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL); 990 learn_more_url = google_util::AppendGoogleLocaleParam( 991 learn_more_url, g_browser_process->GetApplicationLocale()); 992 navigator->OpenURL( 993 content::OpenURLParams(learn_more_url, 994 content::Referrer(), 995 NEW_FOREGROUND_TAB, 996 ui::PAGE_TRANSITION_LINK, 997 false)); 998 } 999 1000 namespace { 1001 // Escapes a certificate attribute so that it can be used in a whitelist 1002 // entry. Currently, we only escape slashes, since they are used as a 1003 // separator between attributes. 1004 std::string EscapeCertAttribute(const std::string& attribute) { 1005 std::string escaped; 1006 for (size_t i = 0; i < attribute.size(); ++i) { 1007 if (attribute[i] == '%') { 1008 escaped.append("%25"); 1009 } else if (attribute[i] == '/') { 1010 escaped.append("%2F"); 1011 } else { 1012 escaped.push_back(attribute[i]); 1013 } 1014 } 1015 return escaped; 1016 } 1017 } // namespace 1018 1019 // static 1020 void DownloadProtectionService::GetCertificateWhitelistStrings( 1021 const net::X509Certificate& certificate, 1022 const net::X509Certificate& issuer, 1023 std::vector<std::string>* whitelist_strings) { 1024 // The whitelist paths are in the format: 1025 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit] 1026 // 1027 // Any of CN, O, or OU may be omitted from the whitelist entry, in which 1028 // case they match anything. However, the attributes that do appear will 1029 // always be in the order shown above. At least one attribute will always 1030 // be present. 1031 1032 const net::CertPrincipal& subject = certificate.subject(); 1033 std::vector<std::string> ou_tokens; 1034 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) { 1035 ou_tokens.push_back( 1036 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i])); 1037 } 1038 1039 std::vector<std::string> o_tokens; 1040 for (size_t i = 0; i < subject.organization_names.size(); ++i) { 1041 o_tokens.push_back( 1042 "/O=" + EscapeCertAttribute(subject.organization_names[i])); 1043 } 1044 1045 std::string cn_token; 1046 if (!subject.common_name.empty()) { 1047 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name); 1048 } 1049 1050 std::set<std::string> paths_to_check; 1051 if (!cn_token.empty()) { 1052 paths_to_check.insert(cn_token); 1053 } 1054 for (size_t i = 0; i < o_tokens.size(); ++i) { 1055 paths_to_check.insert(cn_token + o_tokens[i]); 1056 paths_to_check.insert(o_tokens[i]); 1057 for (size_t j = 0; j < ou_tokens.size(); ++j) { 1058 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]); 1059 paths_to_check.insert(o_tokens[i] + ou_tokens[j]); 1060 } 1061 } 1062 for (size_t i = 0; i < ou_tokens.size(); ++i) { 1063 paths_to_check.insert(cn_token + ou_tokens[i]); 1064 paths_to_check.insert(ou_tokens[i]); 1065 } 1066 1067 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data, 1068 sizeof(issuer.fingerprint().data)); 1069 for (std::set<std::string>::iterator it = paths_to_check.begin(); 1070 it != paths_to_check.end(); ++it) { 1071 whitelist_strings->push_back("cert/" + issuer_fp + *it); 1072 } 1073 } 1074 1075 // static 1076 GURL DownloadProtectionService::GetDownloadRequestUrl() { 1077 GURL url(kDownloadRequestUrl); 1078 std::string api_key = google_apis::GetAPIKey(); 1079 if (!api_key.empty()) 1080 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true)); 1081 1082 return url; 1083 } 1084 1085 } // namespace safe_browsing 1086