download_protection_service.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/sequenced_task_runner_helpers.h" 14#include "base/stl_util.h" 15#include "base/strings/string_number_conversions.h" 16#include "base/strings/string_util.h" 17#include "base/strings/stringprintf.h" 18#include "base/threading/sequenced_worker_pool.h" 19#include "base/time/time.h" 20#include "chrome/browser/safe_browsing/download_feedback_service.h" 21#include "chrome/browser/safe_browsing/safe_browsing_service.h" 22#include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h" 23#include "chrome/browser/safe_browsing/signature_util.h" 24#include "chrome/browser/ui/browser.h" 25#include "chrome/browser/ui/browser_list.h" 26#include "chrome/common/safe_browsing/csd.pb.h" 27#include "chrome/common/safe_browsing/download_protection_util.h" 28#include "chrome/common/safe_browsing/zip_analyzer.h" 29#include "chrome/common/url_constants.h" 30#include "content/public/browser/browser_thread.h" 31#include "content/public/browser/download_item.h" 32#include "content/public/browser/page_navigator.h" 33#include "google_apis/google_api_keys.h" 34#include "net/base/escape.h" 35#include "net/base/load_flags.h" 36#include "net/cert/x509_cert_types.h" 37#include "net/cert/x509_certificate.h" 38#include "net/http/http_status_code.h" 39#include "net/url_request/url_fetcher.h" 40#include "net/url_request/url_fetcher_delegate.h" 41#include "net/url_request/url_request_context_getter.h" 42#include "net/url_request/url_request_status.h" 43 44using content::BrowserThread; 45 46namespace { 47static const int64 kDownloadRequestTimeoutMs = 3000; 48} // namespace 49 50namespace safe_browsing { 51 52const char DownloadProtectionService::kDownloadRequestUrl[] = 53 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; 54 55namespace { 56ClientDownloadRequest::DownloadType GetDownloadType( 57 const base::FilePath& file) { 58 DCHECK(download_protection_util::IsBinaryFile(file)); 59 if (file.MatchesExtension(FILE_PATH_LITERAL(".apk"))) 60 return ClientDownloadRequest::ANDROID_APK; 61 else if (file.MatchesExtension(FILE_PATH_LITERAL(".crx"))) 62 return ClientDownloadRequest::CHROME_EXTENSION; 63 // For zip files, we use the ZIPPED_EXECUTABLE type since we will only send 64 // the pingback if we find an executable inside the zip archive. 65 else if (file.MatchesExtension(FILE_PATH_LITERAL(".zip"))) 66 return ClientDownloadRequest::ZIPPED_EXECUTABLE; 67 return ClientDownloadRequest::WIN_EXECUTABLE; 68} 69 70// List of extensions for which we track some UMA stats. 71enum MaliciousExtensionType { 72 EXTENSION_EXE, 73 EXTENSION_MSI, 74 EXTENSION_CAB, 75 EXTENSION_SYS, 76 EXTENSION_SCR, 77 EXTENSION_DRV, 78 EXTENSION_BAT, 79 EXTENSION_ZIP, 80 EXTENSION_RAR, 81 EXTENSION_DLL, 82 EXTENSION_PIF, 83 EXTENSION_COM, 84 EXTENSION_JAR, 85 EXTENSION_CLASS, 86 EXTENSION_PDF, 87 EXTENSION_VB, 88 EXTENSION_REG, 89 EXTENSION_GRP, 90 EXTENSION_OTHER, // Groups all other extensions into one bucket. 91 EXTENSION_CRX, 92 EXTENSION_APK, 93 EXTENSION_MAX, 94}; 95 96MaliciousExtensionType GetExtensionType(const base::FilePath& f) { 97 if (f.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE; 98 if (f.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI; 99 if (f.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB; 100 if (f.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS; 101 if (f.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR; 102 if (f.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV; 103 if (f.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT; 104 if (f.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP; 105 if (f.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR; 106 if (f.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL; 107 if (f.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF; 108 if (f.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM; 109 if (f.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR; 110 if (f.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS; 111 if (f.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF; 112 if (f.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB; 113 if (f.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG; 114 if (f.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP; 115 if (f.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX; 116 if (f.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK; 117 return EXTENSION_OTHER; 118} 119 120void 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). 129enum 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). 145class 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 227class 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 270class 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 SignatureUtil* signature_util) 283 : item_(item), 284 url_chain_(item->GetUrlChain()), 285 referrer_url_(item->GetReferrerUrl()), 286 zipped_executable_(false), 287 callback_(callback), 288 service_(service), 289 signature_util_(signature_util), 290 database_manager_(database_manager), 291 pingback_enabled_(service_->enabled()), 292 finished_(false), 293 type_(ClientDownloadRequest::WIN_EXECUTABLE), 294 weakptr_factory_(this), 295 start_time_(base::TimeTicks::Now()) { 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 297 item_->AddObserver(this); 298 } 299 300 void Start() { 301 VLOG(2) << "Starting SafeBrowsing download check for: " 302 << item_->DebugString(true); 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 304 // TODO(noelutz): implement some cache to make sure we don't issue the same 305 // request over and over again if a user downloads the same binary multiple 306 // times. 307 DownloadCheckResultReason reason = REASON_MAX; 308 if (!IsSupportedDownload( 309 *item_, item_->GetTargetFilePath(), &reason, &type_)) { 310 switch (reason) { 311 case REASON_EMPTY_URL_CHAIN: 312 case REASON_INVALID_URL: 313 PostFinishTask(SAFE, reason); 314 return; 315 316 case REASON_NOT_BINARY_FILE: 317 RecordFileExtensionType(item_->GetTargetFilePath()); 318 PostFinishTask(SAFE, reason); 319 return; 320 321 default: 322 // We only expect the reasons explicitly handled above. 323 NOTREACHED(); 324 } 325 } 326 RecordFileExtensionType(item_->GetTargetFilePath()); 327 328 // Compute features from the file contents. Note that we record histograms 329 // based on the result, so this runs regardless of whether the pingbacks 330 // are enabled. 331 if (item_->GetTargetFilePath().MatchesExtension( 332 FILE_PATH_LITERAL(".zip"))) { 333 StartExtractZipFeatures(); 334 } else { 335 DCHECK(!download_protection_util::IsArchiveFile( 336 item_->GetTargetFilePath())); 337 StartExtractSignatureFeatures(); 338 } 339 } 340 341 // Start a timeout to cancel the request if it takes too long. 342 // This should only be called after we have finished accessing the file. 343 void StartTimeout() { 344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 345 if (!service_) { 346 // Request has already been cancelled. 347 return; 348 } 349 BrowserThread::PostDelayedTask( 350 BrowserThread::UI, 351 FROM_HERE, 352 base::Bind(&CheckClientDownloadRequest::Cancel, 353 weakptr_factory_.GetWeakPtr()), 354 base::TimeDelta::FromMilliseconds( 355 service_->download_request_timeout_ms())); 356 } 357 358 // Canceling a request will cause us to always report the result as SAFE 359 // unless a pending request is about to call FinishRequest. 360 void Cancel() { 361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 362 if (fetcher_.get()) { 363 // The DownloadProtectionService is going to release its reference, so we 364 // might be destroyed before the URLFetcher completes. Cancel the 365 // fetcher so it does not try to invoke OnURLFetchComplete. 366 fetcher_.reset(); 367 } 368 // Note: If there is no fetcher, then some callback is still holding a 369 // reference to this object. We'll eventually wind up in some method on 370 // the UI thread that will call FinishRequest() again. If FinishRequest() 371 // is called a second time, it will be a no-op. 372 FinishRequest(SAFE, REASON_REQUEST_CANCELED); 373 // Calling FinishRequest might delete this object, we may be deleted by 374 // this point. 375 } 376 377 // content::DownloadItem::Observer implementation. 378 virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE { 379 Cancel(); 380 DCHECK(item_ == NULL); 381 } 382 383 // From the net::URLFetcherDelegate interface. 384 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 386 DCHECK_EQ(source, fetcher_.get()); 387 VLOG(2) << "Received a response for URL: " 388 << item_->GetUrlChain().back() << ": success=" 389 << source->GetStatus().is_success() << " response_code=" 390 << source->GetResponseCode(); 391 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED; 392 DownloadCheckResult result = SAFE; 393 if (source->GetStatus().is_success() && 394 net::HTTP_OK == source->GetResponseCode()) { 395 ClientDownloadResponse response; 396 std::string data; 397 bool got_data = source->GetResponseAsString(&data); 398 DCHECK(got_data); 399 if (!response.ParseFromString(data)) { 400 reason = REASON_INVALID_RESPONSE_PROTO; 401 } else if (response.verdict() == ClientDownloadResponse::SAFE) { 402 reason = REASON_DOWNLOAD_SAFE; 403 } else if (service_ && !service_->IsSupportedDownload( 404 *item_, item_->GetTargetFilePath())) { 405 // The client of the download protection service assumes that we don't 406 // support this download so we cannot return any other verdict than 407 // SAFE even if the server says it's dangerous to download this file. 408 // Note: if service_ is NULL we already cancelled the request and 409 // returned SAFE. 410 reason = REASON_DOWNLOAD_NOT_SUPPORTED; 411 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) { 412 reason = REASON_DOWNLOAD_DANGEROUS; 413 result = DANGEROUS; 414 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) { 415 reason = REASON_DOWNLOAD_UNCOMMON; 416 result = UNCOMMON; 417 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) { 418 reason = REASON_DOWNLOAD_DANGEROUS_HOST; 419 result = DANGEROUS_HOST; 420 } else { 421 LOG(DFATAL) << "Unknown download response verdict: " 422 << response.verdict(); 423 reason = REASON_INVALID_RESPONSE_VERDICT; 424 } 425 DownloadFeedbackService::MaybeStorePingsForDownload( 426 result, item_, client_download_request_data_, data); 427 } 428 // We don't need the fetcher anymore. 429 fetcher_.reset(); 430 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration", 431 base::TimeTicks::Now() - start_time_); 432 FinishRequest(result, reason); 433 } 434 435 static bool IsSupportedDownload(const content::DownloadItem& item, 436 const base::FilePath& target_path, 437 DownloadCheckResultReason* reason, 438 ClientDownloadRequest::DownloadType* type) { 439 if (item.GetUrlChain().empty()) { 440 *reason = REASON_EMPTY_URL_CHAIN; 441 return false; 442 } 443 const GURL& final_url = item.GetUrlChain().back(); 444 if (!final_url.is_valid() || final_url.is_empty() || 445 !final_url.IsStandard() || final_url.SchemeIsFile()) { 446 *reason = REASON_INVALID_URL; 447 return false; 448 } 449 if (!download_protection_util::IsBinaryFile(target_path)) { 450 *reason = REASON_NOT_BINARY_FILE; 451 return false; 452 } 453 *type = GetDownloadType(target_path); 454 return true; 455 } 456 457 private: 458 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; 459 friend class base::DeleteHelper<CheckClientDownloadRequest>; 460 461 virtual ~CheckClientDownloadRequest() { 462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 463 DCHECK(item_ == NULL); 464 } 465 466 void OnFileFeatureExtractionDone() { 467 // This can run in any thread, since it just posts more messages. 468 469 // TODO(noelutz): DownloadInfo should also contain the IP address of 470 // every URL in the redirect chain. We also should check whether the 471 // download URL is hosted on the internal network. 472 BrowserThread::PostTask( 473 BrowserThread::IO, 474 FROM_HERE, 475 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this)); 476 477 // We wait until after the file checks finish to start the timeout, as 478 // windows can cause permissions errors if the timeout fired while we were 479 // checking the file signature and we tried to complete the download. 480 BrowserThread::PostTask( 481 BrowserThread::UI, 482 FROM_HERE, 483 base::Bind(&CheckClientDownloadRequest::StartTimeout, this)); 484 } 485 486 void StartExtractSignatureFeatures() { 487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 488 DCHECK(item_); // Called directly from Start(), item should still exist. 489 // Since we do blocking I/O, offload this to a worker thread. 490 // The task does not need to block shutdown. 491 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( 492 FROM_HERE, 493 base::Bind(&CheckClientDownloadRequest::ExtractSignatureFeatures, 494 this, item_->GetFullPath()), 495 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 496 } 497 498 void ExtractSignatureFeatures(const base::FilePath& file_path) { 499 base::TimeTicks start_time = base::TimeTicks::Now(); 500 signature_util_->CheckSignature(file_path, &signature_info_); 501 bool is_signed = (signature_info_.certificate_chain_size() > 0); 502 if (is_signed) { 503 VLOG(2) << "Downloaded a signed binary: " << file_path.value(); 504 } else { 505 VLOG(2) << "Downloaded an unsigned binary: " 506 << file_path.value(); 507 } 508 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); 509 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime", 510 base::TimeTicks::Now() - start_time); 511 512 OnFileFeatureExtractionDone(); 513 } 514 515 void StartExtractZipFeatures() { 516 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 517 DCHECK(item_); // Called directly from Start(), item should still exist. 518 zip_analysis_start_time_ = base::TimeTicks::Now(); 519 // We give the zip analyzer a weak pointer to this object. Since the 520 // analyzer is refcounted, it might outlive the request. 521 analyzer_ = new SandboxedZipAnalyzer( 522 item_->GetFullPath(), 523 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished, 524 weakptr_factory_.GetWeakPtr())); 525 analyzer_->Start(); 526 } 527 528 void OnZipAnalysisFinished(const zip_analyzer::Results& results) { 529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 530 if (!service_) 531 return; 532 if (results.success) { 533 zipped_executable_ = results.has_executable; 534 VLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value() 535 << ", has_executable=" << results.has_executable 536 << " has_archive=" << results.has_archive; 537 } else { 538 VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value(); 539 } 540 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable", 541 zipped_executable_); 542 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable", 543 results.has_archive && !zipped_executable_); 544 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime", 545 base::TimeTicks::Now() - zip_analysis_start_time_); 546 547 if (!zipped_executable_) { 548 PostFinishTask(SAFE, REASON_ARCHIVE_WITHOUT_BINARIES); 549 return; 550 } 551 OnFileFeatureExtractionDone(); 552 } 553 554 void CheckWhitelists() { 555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 556 DownloadCheckResultReason reason = REASON_MAX; 557 if (!database_manager_.get()) { 558 reason = REASON_SB_DISABLED; 559 } else { 560 for (size_t i = 0; i < url_chain_.size(); ++i) { 561 const GURL& url = url_chain_[i]; 562 if (url.is_valid() && 563 database_manager_->MatchDownloadWhitelistUrl(url)) { 564 VLOG(2) << url << " is on the download whitelist."; 565 reason = REASON_WHITELISTED_URL; 566 break; 567 } 568 } 569 if (referrer_url_.is_valid() && reason == REASON_MAX && 570 database_manager_->MatchDownloadWhitelistUrl( 571 referrer_url_)) { 572 VLOG(2) << "Referrer url " << referrer_url_ 573 << " is on the download whitelist."; 574 reason = REASON_WHITELISTED_REFERRER; 575 } 576 if (reason != REASON_MAX || signature_info_.trusted()) { 577 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1); 578 } 579 } 580 if (reason == REASON_MAX && signature_info_.trusted()) { 581 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) { 582 if (CertificateChainIsWhitelisted( 583 signature_info_.certificate_chain(i))) { 584 reason = REASON_TRUSTED_EXECUTABLE; 585 break; 586 } 587 } 588 } 589 if (reason != REASON_MAX) { 590 PostFinishTask(SAFE, reason); 591 } else if (!pingback_enabled_) { 592 PostFinishTask(SAFE, REASON_PING_DISABLED); 593 } else { 594 // Currently, the UI only works on Windows so we don't even bother 595 // with pinging the server if we're not on Windows. TODO(noelutz): 596 // change this code once the UI is done for Linux and Mac. 597#if defined(OS_WIN) 598 // The URLFetcher is owned by the UI thread, so post a message to 599 // start the pingback. 600 BrowserThread::PostTask( 601 BrowserThread::UI, 602 FROM_HERE, 603 base::Bind(&CheckClientDownloadRequest::SendRequest, this)); 604#else 605 PostFinishTask(SAFE, REASON_OS_NOT_SUPPORTED); 606#endif 607 } 608 } 609 610 void SendRequest() { 611 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 612 613 // This is our last chance to check whether the request has been canceled 614 // before sending it. 615 if (!service_) 616 return; 617 618 ClientDownloadRequest request; 619 request.set_url(item_->GetUrlChain().back().spec()); 620 request.mutable_digests()->set_sha256(item_->GetHash()); 621 request.set_length(item_->GetReceivedBytes()); 622 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) { 623 ClientDownloadRequest::Resource* resource = request.add_resources(); 624 resource->set_url(item_->GetUrlChain()[i].spec()); 625 if (i == item_->GetUrlChain().size() - 1) { 626 // The last URL in the chain is the download URL. 627 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL); 628 resource->set_referrer(item_->GetReferrerUrl().spec()); 629 if (!item_->GetRemoteAddress().empty()) { 630 resource->set_remote_ip(item_->GetRemoteAddress()); 631 } 632 } else { 633 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT); 634 } 635 // TODO(noelutz): fill out the remote IP addresses. 636 } 637 request.set_user_initiated(item_->HasUserGesture()); 638 request.set_file_basename( 639 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe()); 640 request.set_download_type(type_); 641 request.mutable_signature()->CopyFrom(signature_info_); 642 if (!request.SerializeToString(&client_download_request_data_)) { 643 FinishRequest(SAFE, REASON_INVALID_REQUEST_PROTO); 644 return; 645 } 646 647 VLOG(2) << "Sending a request for URL: " 648 << item_->GetUrlChain().back(); 649 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */, 650 GURL(GetDownloadRequestUrl()), 651 net::URLFetcher::POST, 652 this)); 653 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 654 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error. 655 fetcher_->SetRequestContext(service_->request_context_getter_.get()); 656 fetcher_->SetUploadData("application/octet-stream", 657 client_download_request_data_); 658 fetcher_->Start(); 659 } 660 661 void PostFinishTask(DownloadCheckResult result, 662 DownloadCheckResultReason reason) { 663 BrowserThread::PostTask( 664 BrowserThread::UI, 665 FROM_HERE, 666 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result, 667 reason)); 668 } 669 670 void FinishRequest(DownloadCheckResult result, 671 DownloadCheckResultReason reason) { 672 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 673 if (finished_) { 674 return; 675 } 676 finished_ = true; 677 // Ensure the timeout task is cancelled while we still have a non-zero 678 // refcount. (crbug.com/240449) 679 weakptr_factory_.InvalidateWeakPtrs(); 680 if (service_) { 681 VLOG(2) << "SafeBrowsing download verdict for: " 682 << item_->DebugString(true) << " verdict:" << reason; 683 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", 684 reason, 685 REASON_MAX); 686 callback_.Run(result); 687 item_->RemoveObserver(this); 688 item_ = NULL; 689 DownloadProtectionService* service = service_; 690 service_ = NULL; 691 service->RequestFinished(this); 692 // DownloadProtectionService::RequestFinished will decrement our refcount, 693 // so we may be deleted now. 694 } else { 695 callback_.Run(SAFE); 696 } 697 } 698 699 bool CertificateChainIsWhitelisted( 700 const ClientDownloadRequest_CertificateChain& chain) { 701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 702 if (chain.element_size() < 2) { 703 // We need to have both a signing certificate and its issuer certificate 704 // present to construct a whitelist entry. 705 return false; 706 } 707 scoped_refptr<net::X509Certificate> cert = 708 net::X509Certificate::CreateFromBytes( 709 chain.element(0).certificate().data(), 710 chain.element(0).certificate().size()); 711 if (!cert.get()) { 712 return false; 713 } 714 715 for (int i = 1; i < chain.element_size(); ++i) { 716 scoped_refptr<net::X509Certificate> issuer = 717 net::X509Certificate::CreateFromBytes( 718 chain.element(i).certificate().data(), 719 chain.element(i).certificate().size()); 720 if (!issuer.get()) { 721 return false; 722 } 723 std::vector<std::string> whitelist_strings; 724 DownloadProtectionService::GetCertificateWhitelistStrings( 725 *cert.get(), *issuer.get(), &whitelist_strings); 726 for (size_t j = 0; j < whitelist_strings.size(); ++j) { 727 if (database_manager_->MatchDownloadWhitelistString( 728 whitelist_strings[j])) { 729 VLOG(2) << "Certificate matched whitelist, cert=" 730 << cert->subject().GetDisplayName() 731 << " issuer=" << issuer->subject().GetDisplayName(); 732 return true; 733 } 734 } 735 cert = issuer; 736 } 737 return false; 738 } 739 740 // The DownloadItem we are checking. Will be NULL if the request has been 741 // canceled. Must be accessed only on UI thread. 742 content::DownloadItem* item_; 743 // Copies of data from |item_| for access on other threads. 744 std::vector<GURL> url_chain_; 745 GURL referrer_url_; 746 747 bool zipped_executable_; 748 ClientDownloadRequest_SignatureInfo signature_info_; 749 CheckDownloadCallback callback_; 750 // Will be NULL if the request has been canceled. 751 DownloadProtectionService* service_; 752 scoped_refptr<SignatureUtil> signature_util_; 753 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; 754 const bool pingback_enabled_; 755 scoped_ptr<net::URLFetcher> fetcher_; 756 scoped_refptr<SandboxedZipAnalyzer> analyzer_; 757 base::TimeTicks zip_analysis_start_time_; 758 bool finished_; 759 ClientDownloadRequest::DownloadType type_; 760 std::string client_download_request_data_; 761 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_; 762 base::TimeTicks start_time_; // Used for stats. 763 764 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest); 765}; 766 767DownloadProtectionService::DownloadProtectionService( 768 SafeBrowsingService* sb_service, 769 net::URLRequestContextGetter* request_context_getter) 770 : request_context_getter_(request_context_getter), 771 enabled_(false), 772 signature_util_(new SignatureUtil()), 773 download_request_timeout_ms_(kDownloadRequestTimeoutMs), 774 feedback_service_(new DownloadFeedbackService( 775 request_context_getter, BrowserThread::GetBlockingPool())) { 776 777 if (sb_service) { 778 ui_manager_ = sb_service->ui_manager(); 779 database_manager_ = sb_service->database_manager(); 780 } 781} 782 783DownloadProtectionService::~DownloadProtectionService() { 784 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 785 CancelPendingRequests(); 786} 787 788void DownloadProtectionService::SetEnabled(bool enabled) { 789 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 790 if (enabled == enabled_) { 791 return; 792 } 793 enabled_ = enabled; 794 if (!enabled_) { 795 CancelPendingRequests(); 796 } 797} 798 799void DownloadProtectionService::CheckClientDownload( 800 content::DownloadItem* item, 801 const CheckDownloadCallback& callback) { 802 scoped_refptr<CheckClientDownloadRequest> request( 803 new CheckClientDownloadRequest(item, callback, this, 804 database_manager_, signature_util_.get())); 805 download_requests_.insert(request); 806 request->Start(); 807} 808 809void DownloadProtectionService::CheckDownloadUrl( 810 const content::DownloadItem& item, 811 const CheckDownloadCallback& callback) { 812 DCHECK(!item.GetUrlChain().empty()); 813 scoped_refptr<DownloadUrlSBClient> client( 814 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_)); 815 // The client will release itself once it is done. 816 BrowserThread::PostTask( 817 BrowserThread::IO, 818 FROM_HERE, 819 base::Bind(&DownloadUrlSBClient::StartCheck, client)); 820} 821 822bool DownloadProtectionService::IsSupportedDownload( 823 const content::DownloadItem& item, 824 const base::FilePath& target_path) const { 825 // Currently, the UI only works on Windows. On Linux and Mac we still 826 // want to show the dangerous file type warning if the file is possibly 827 // dangerous which means we have to always return false here. 828#if defined(OS_WIN) 829 DownloadCheckResultReason reason = REASON_MAX; 830 ClientDownloadRequest::DownloadType type = 831 ClientDownloadRequest::WIN_EXECUTABLE; 832 return (CheckClientDownloadRequest::IsSupportedDownload(item, target_path, 833 &reason, &type) && 834 (ClientDownloadRequest::ANDROID_APK == type || 835 ClientDownloadRequest::WIN_EXECUTABLE == type || 836 ClientDownloadRequest::ZIPPED_EXECUTABLE == type)); 837#else 838 return false; 839#endif 840} 841 842void DownloadProtectionService::CancelPendingRequests() { 843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 844 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 845 download_requests_.begin(); 846 it != download_requests_.end();) { 847 // We need to advance the iterator before we cancel because canceling 848 // the request will invalidate it when RequestFinished is called below. 849 scoped_refptr<CheckClientDownloadRequest> tmp = *it++; 850 tmp->Cancel(); 851 } 852 DCHECK(download_requests_.empty()); 853} 854 855void DownloadProtectionService::RequestFinished( 856 CheckClientDownloadRequest* request) { 857 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 858 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 859 download_requests_.find(request); 860 DCHECK(it != download_requests_.end()); 861 download_requests_.erase(*it); 862} 863 864void DownloadProtectionService::ShowDetailsForDownload( 865 const content::DownloadItem& item, 866 content::PageNavigator* navigator) { 867 navigator->OpenURL( 868 content::OpenURLParams(GURL(chrome::kDownloadScanningLearnMoreURL), 869 content::Referrer(), 870 NEW_FOREGROUND_TAB, 871 content::PAGE_TRANSITION_LINK, 872 false)); 873} 874 875namespace { 876// Escapes a certificate attribute so that it can be used in a whitelist 877// entry. Currently, we only escape slashes, since they are used as a 878// separator between attributes. 879std::string EscapeCertAttribute(const std::string& attribute) { 880 std::string escaped; 881 for (size_t i = 0; i < attribute.size(); ++i) { 882 if (attribute[i] == '%') { 883 escaped.append("%25"); 884 } else if (attribute[i] == '/') { 885 escaped.append("%2F"); 886 } else { 887 escaped.push_back(attribute[i]); 888 } 889 } 890 return escaped; 891} 892} // namespace 893 894// static 895void DownloadProtectionService::GetCertificateWhitelistStrings( 896 const net::X509Certificate& certificate, 897 const net::X509Certificate& issuer, 898 std::vector<std::string>* whitelist_strings) { 899 // The whitelist paths are in the format: 900 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit] 901 // 902 // Any of CN, O, or OU may be omitted from the whitelist entry, in which 903 // case they match anything. However, the attributes that do appear will 904 // always be in the order shown above. At least one attribute will always 905 // be present. 906 907 const net::CertPrincipal& subject = certificate.subject(); 908 std::vector<std::string> ou_tokens; 909 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) { 910 ou_tokens.push_back( 911 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i])); 912 } 913 914 std::vector<std::string> o_tokens; 915 for (size_t i = 0; i < subject.organization_names.size(); ++i) { 916 o_tokens.push_back( 917 "/O=" + EscapeCertAttribute(subject.organization_names[i])); 918 } 919 920 std::string cn_token; 921 if (!subject.common_name.empty()) { 922 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name); 923 } 924 925 std::set<std::string> paths_to_check; 926 if (!cn_token.empty()) { 927 paths_to_check.insert(cn_token); 928 } 929 for (size_t i = 0; i < o_tokens.size(); ++i) { 930 paths_to_check.insert(cn_token + o_tokens[i]); 931 paths_to_check.insert(o_tokens[i]); 932 for (size_t j = 0; j < ou_tokens.size(); ++j) { 933 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]); 934 paths_to_check.insert(o_tokens[i] + ou_tokens[j]); 935 } 936 } 937 for (size_t i = 0; i < ou_tokens.size(); ++i) { 938 paths_to_check.insert(cn_token + ou_tokens[i]); 939 paths_to_check.insert(ou_tokens[i]); 940 } 941 942 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data, 943 sizeof(issuer.fingerprint().data)); 944 for (std::set<std::string>::iterator it = paths_to_check.begin(); 945 it != paths_to_check.end(); ++it) { 946 whitelist_strings->push_back("cert/" + issuer_fp + *it); 947 } 948} 949 950// static 951std::string DownloadProtectionService::GetDownloadRequestUrl() { 952 std::string url = kDownloadRequestUrl; 953 std::string api_key = google_apis::GetAPIKey(); 954 if (!api_key.empty()) { 955 base::StringAppendF(&url, "?key=%s", 956 net::EscapeQueryParamValue(api_key, true).c_str()); 957 } 958 return url; 959} 960 961} // namespace safe_browsing 962