Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: chrome/browser/safe_browsing/download_protection_service.cc

Issue 8762007: Implement a whitelist for code-signing certificates. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/safe_browsing/download_protection_service.h" 5 #include "chrome/browser/safe_browsing/download_protection_service.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/weak_ptr.h" 11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h" 13 #include "base/stl_util.h"
14 #include "base/string_number_conversions.h" 14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h" 15 #include "base/string_util.h"
16 #include "base/stringprintf.h" 16 #include "base/stringprintf.h"
17 #include "base/time.h" 17 #include "base/time.h"
18 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 18 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
19 #include "chrome/browser/safe_browsing/signature_util.h" 19 #include "chrome/browser/safe_browsing/signature_util.h"
20 #include "chrome/common/net/http_return.h" 20 #include "chrome/common/net/http_return.h"
21 #include "chrome/common/safe_browsing/csd.pb.h" 21 #include "chrome/common/safe_browsing/csd.pb.h"
22 #include "content/browser/download/download_item.h" 22 #include "content/browser/download/download_item.h"
23 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/common/url_fetcher.h" 24 #include "content/public/common/url_fetcher.h"
25 #include "content/public/common/url_fetcher_delegate.h" 25 #include "content/public/common/url_fetcher_delegate.h"
26 #include "net/base/load_flags.h" 26 #include "net/base/load_flags.h"
27 #include "net/base/x509_cert_types.h"
28 #include "net/base/x509_certificate.h"
27 #include "net/url_request/url_request_context_getter.h" 29 #include "net/url_request/url_request_context_getter.h"
28 #include "net/url_request/url_request_status.h" 30 #include "net/url_request/url_request_status.h"
29 31
30 using content::BrowserThread; 32 using content::BrowserThread;
31 33
32 namespace { 34 namespace {
33 static const int64 kDownloadRequestTimeoutMs = 3000; 35 static const int64 kDownloadRequestTimeoutMs = 3000;
34 } // namespace 36 } // namespace
35 37
36 namespace safe_browsing { 38 namespace safe_browsing {
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 498
497 void CheckWhitelists() { 499 void CheckWhitelists() {
498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
499 DownloadCheckResultReason reason = REASON_MAX; 501 DownloadCheckResultReason reason = REASON_MAX;
500 if (!sb_service_.get()) { 502 if (!sb_service_.get()) {
501 reason = REASON_SB_DISABLED; 503 reason = REASON_SB_DISABLED;
502 } else { 504 } else {
503 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) { 505 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) {
504 const GURL& url = info_.download_url_chain[i]; 506 const GURL& url = info_.download_url_chain[i];
505 if (url.is_valid() && sb_service_->MatchDownloadWhitelistUrl(url)) { 507 if (url.is_valid() && sb_service_->MatchDownloadWhitelistUrl(url)) {
508 VLOG(2) << url << " is on the download whitelist.";
506 reason = REASON_WHITELISTED_URL; 509 reason = REASON_WHITELISTED_URL;
507 break; 510 break;
508 } 511 }
509 } 512 }
510 if (info_.referrer_url.is_valid() && reason == REASON_MAX && 513 if (info_.referrer_url.is_valid() && reason == REASON_MAX &&
511 sb_service_->MatchDownloadWhitelistUrl(info_.referrer_url)) { 514 sb_service_->MatchDownloadWhitelistUrl(info_.referrer_url)) {
515 VLOG(2) << "Referrer url " << info_.referrer_url
516 << " is on the download whitelist.";
512 reason = REASON_WHITELISTED_REFERRER; 517 reason = REASON_WHITELISTED_REFERRER;
513 } 518 }
514 if (reason != REASON_MAX || signature_info_.trusted()) { 519 if (reason != REASON_MAX || signature_info_.trusted()) {
515 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1); 520 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
516 } 521 }
517 } 522 }
518 if (reason == REASON_MAX && signature_info_.trusted()) { 523 if (reason == REASON_MAX && signature_info_.trusted()) {
519 // TODO(noelutz): implement a certificate whitelist and only whitelist 524 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
520 // binaries whose certificate match the whitelist. 525 if (CertificateChainIsWhitelisted(
521 reason = REASON_TRUSTED_EXECUTABLE; 526 signature_info_.certificate_chain(i))) {
527 reason = REASON_TRUSTED_EXECUTABLE;
528 break;
529 }
530 }
522 } 531 }
523 if (reason != REASON_MAX) { 532 if (reason != REASON_MAX) {
524 RecordImprovedProtectionStats(reason); 533 RecordImprovedProtectionStats(reason);
525 CheckDigestList(); 534 CheckDigestList();
526 } else if (!pingback_enabled_) { 535 } else if (!pingback_enabled_) {
527 RecordImprovedProtectionStats(REASON_PING_DISABLED); 536 RecordImprovedProtectionStats(REASON_PING_DISABLED);
528 CheckDigestList(); 537 CheckDigestList();
529 } else { 538 } else {
530 // The URLFetcher is owned by the UI thread, so post a message to 539 // The URLFetcher is owned by the UI thread, so post a message to
531 // start the pingback. 540 // start the pingback.
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 } 616 }
608 617
609 void RecordImprovedProtectionStats(DownloadCheckResultReason reason) { 618 void RecordImprovedProtectionStats(DownloadCheckResultReason reason) {
610 VLOG(2) << "SafeBrowsing download verdict for: " 619 VLOG(2) << "SafeBrowsing download verdict for: "
611 << info_.DebugString() << " verdict:" << reason; 620 << info_.DebugString() << " verdict:" << reason;
612 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", 621 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
613 reason, 622 reason,
614 REASON_MAX); 623 REASON_MAX);
615 } 624 }
616 625
626 bool CertificateChainIsWhitelisted(
627 const ClientDownloadRequest_CertificateChain& chain) {
628 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
629 if (chain.element_size() < 2) {
630 // We need to have both a signing certificate and its issuer certificate
631 // present to construct a whitelist entry.
632 return false;
633 }
634 scoped_refptr<net::X509Certificate> cert =
635 net::X509Certificate::CreateFromBytes(
636 chain.element(0).certificate().data(),
637 chain.element(0).certificate().size());
638 if (!cert.get()) {
639 return false;
640 }
641
642 for (int i = 1; i < chain.element_size(); ++i) {
643 scoped_refptr<net::X509Certificate> issuer =
644 net::X509Certificate::CreateFromBytes(
645 chain.element(i).certificate().data(),
646 chain.element(i).certificate().size());
647 if (!issuer.get()) {
648 return false;
649 }
650 std::vector<std::string> whitelist_strings;
651 DownloadProtectionService::GetCertificateWhitelistStrings(
652 *cert, *issuer, &whitelist_strings);
653 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
654 if (sb_service_->MatchDownloadWhitelistString(whitelist_strings[j])) {
655 VLOG(2) << "Certificate matched whitelist, cert="
656 << cert->subject().GetDisplayName()
657 << " issuer=" << issuer->subject().GetDisplayName();
658 return true;
659 }
660 }
661 cert = issuer;
662 }
663 return false;
664 }
665
617 DownloadInfo info_; 666 DownloadInfo info_;
618 ClientDownloadRequest_SignatureInfo signature_info_; 667 ClientDownloadRequest_SignatureInfo signature_info_;
619 CheckDownloadCallback callback_; 668 CheckDownloadCallback callback_;
620 // Will be NULL if the request has been canceled. 669 // Will be NULL if the request has been canceled.
621 DownloadProtectionService* service_; 670 DownloadProtectionService* service_;
622 scoped_refptr<SignatureUtil> signature_util_; 671 scoped_refptr<SignatureUtil> signature_util_;
623 scoped_refptr<SafeBrowsingService> sb_service_; 672 scoped_refptr<SafeBrowsingService> sb_service_;
624 const bool pingback_enabled_; 673 const bool pingback_enabled_;
625 scoped_ptr<content::URLFetcher> fetcher_; 674 scoped_ptr<content::URLFetcher> fetcher_;
626 bool finished_; 675 bool finished_;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 } 746 }
698 747
699 void DownloadProtectionService::RequestFinished( 748 void DownloadProtectionService::RequestFinished(
700 CheckClientDownloadRequest* request) { 749 CheckClientDownloadRequest* request) {
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 750 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
702 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 751 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
703 download_requests_.find(request); 752 download_requests_.find(request);
704 DCHECK(it != download_requests_.end()); 753 DCHECK(it != download_requests_.end());
705 download_requests_.erase(*it); 754 download_requests_.erase(*it);
706 } 755 }
756
757 namespace {
758 // Escapes a certificate attribute so that it can be used in a whitelist
759 // entry. Currently, we only escape slashes, since they are used as a
760 // separator between attributes.
761 std::string EscapeCertAttribute(const std::string& attribute) {
762 std::string escaped;
763 for (size_t i = 0; i < attribute.size(); ++i) {
764 if (attribute[i] == '%') {
765 escaped.append("%25");
766 } else if (attribute[i] == '/') {
767 escaped.append("%2F");
768 } else {
769 escaped.push_back(attribute[i]);
770 }
771 }
772 return escaped;
773 }
774 } // namespace
775
776 // static
777 void DownloadProtectionService::GetCertificateWhitelistStrings(
778 const net::X509Certificate& certificate,
779 const net::X509Certificate& issuer,
780 std::vector<std::string>* whitelist_strings) {
781 // The whitelist paths are in the format:
782 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
783 //
784 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
785 // case they match anything. However, the attributes that do appear will
786 // always be in the order shown above. At least one attribute will always
787 // be present.
788
789 const net::CertPrincipal& subject = certificate.subject();
790 std::vector<std::string> ou_tokens;
791 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
792 ou_tokens.push_back(
793 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
794 }
795
796 std::vector<std::string> o_tokens;
797 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
798 o_tokens.push_back(
799 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
800 }
801
802 std::string cn_token;
803 if (!subject.common_name.empty()) {
804 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
805 }
806
807 std::set<std::string> paths_to_check;
808 if (!cn_token.empty()) {
809 paths_to_check.insert(cn_token);
810 }
811 for (size_t i = 0; i < o_tokens.size(); ++i) {
812 paths_to_check.insert(cn_token + o_tokens[i]);
813 paths_to_check.insert(o_tokens[i]);
814 for (size_t j = 0; j < ou_tokens.size(); ++j) {
815 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
816 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
817 }
818 }
819 for (size_t i = 0; i < ou_tokens.size(); ++i) {
820 paths_to_check.insert(cn_token + ou_tokens[i]);
821 paths_to_check.insert(ou_tokens[i]);
822 }
823
824 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
825 sizeof(issuer.fingerprint().data));
826 for (std::set<std::string>::iterator it = paths_to_check.begin();
827 it != paths_to_check.end(); ++it) {
828 whitelist_strings->push_back("cert/" + issuer_fp + *it);
829 }
830 }
831
707 } // namespace safe_browsing 832 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698