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