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