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

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: Fix unittest 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) {
noelutz 2011/12/01 06:46:13 add a comment for this case?
Brian Ryner 2011/12/01 22:35:12 Done.
630 return false;
631 }
632 scoped_refptr<net::X509Certificate> cert =
633 net::X509Certificate::CreateFromBytes(
634 chain.element(0).certificate().data(),
635 chain.element(0).certificate().size());
636 if (!cert.get()) {
637 return false;
638 }
639
640 for (int i = 1; i < chain.element_size(); ++i) {
641 scoped_refptr<net::X509Certificate> issuer =
642 net::X509Certificate::CreateFromBytes(
643 chain.element(i).certificate().data(),
644 chain.element(i).certificate().size());
645 if (!issuer.get()) {
646 return false;
647 }
648 std::vector<std::string> whitelist_strings;
649 DownloadProtectionService::GetCertificateWhitelistStrings(
650 *cert, *issuer, &whitelist_strings);
651 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
652 if (sb_service_->MatchDownloadWhitelistString(whitelist_strings[j])) {
653 VLOG(2) << "Certificate matched whitelist, cert="
654 << cert->subject().GetDisplayName()
655 << " issuer=" << issuer->subject().GetDisplayName();
656 return true;
657 }
658 }
659 cert = issuer;
660 }
661 return false;
662 }
663
617 DownloadInfo info_; 664 DownloadInfo info_;
618 ClientDownloadRequest_SignatureInfo signature_info_; 665 ClientDownloadRequest_SignatureInfo signature_info_;
619 CheckDownloadCallback callback_; 666 CheckDownloadCallback callback_;
620 // Will be NULL if the request has been canceled. 667 // Will be NULL if the request has been canceled.
621 DownloadProtectionService* service_; 668 DownloadProtectionService* service_;
622 scoped_refptr<SignatureUtil> signature_util_; 669 scoped_refptr<SignatureUtil> signature_util_;
623 scoped_refptr<SafeBrowsingService> sb_service_; 670 scoped_refptr<SafeBrowsingService> sb_service_;
624 const bool pingback_enabled_; 671 const bool pingback_enabled_;
625 scoped_ptr<content::URLFetcher> fetcher_; 672 scoped_ptr<content::URLFetcher> fetcher_;
626 bool finished_; 673 bool finished_;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 } 744 }
698 745
699 void DownloadProtectionService::RequestFinished( 746 void DownloadProtectionService::RequestFinished(
700 CheckClientDownloadRequest* request) { 747 CheckClientDownloadRequest* request) {
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 748 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
702 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 749 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
703 download_requests_.find(request); 750 download_requests_.find(request);
704 DCHECK(it != download_requests_.end()); 751 DCHECK(it != download_requests_.end());
705 download_requests_.erase(*it); 752 download_requests_.erase(*it);
706 } 753 }
754
755 namespace {
756 // Escapes a certificate attribute so that it can be used in a whitelist
757 // entry. Currently, we only escape slashes, since they are used as a
758 // separator between attributes.
759 std::string EscapeCertAttribute(const std::string& attribute) {
760 std::string escaped;
761 for (size_t i = 0; i < attribute.size(); ++i) {
762 if (attribute[i] == '%') {
763 escaped.append("%25");
764 } else if (attribute[i] == '/') {
765 escaped.append("%2F");
766 } else {
767 escaped.push_back(attribute[i]);
768 }
769 }
770 return escaped;
771 }
772 } // namespace
773
774 // static
775 void DownloadProtectionService::GetCertificateWhitelistStrings(
776 const net::X509Certificate& certificate,
777 const net::X509Certificate& issuer,
778 std::vector<std::string>* whitelist_strings) {
779 // The whitelist paths are in the format:
780 // cert/<ascii issuer fingerprint>
781 // [/CN=common_name][/O=organization][/OU=organizational unit]
mattm 2011/12/01 22:27:19 Was a bit worried until I realized this is suppose
mattm 2011/12/01 22:27:19 I would have expected CN to be listed last, but I
Brian Ryner 2011/12/01 22:35:12 Shortened the names a bit and moved everything ont
Brian Ryner 2011/12/01 22:35:12 Yeah, as long as we are consistent on the server s
782 //
783 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
784 // case they match anything. However, the attributes that do appear will
785 // always be in the order shown above.
mattm 2011/12/01 22:27:19 Mention that at least one attribute will always be
Brian Ryner 2011/12/01 22:35:12 Done.
786
787 const net::CertPrincipal& subject = certificate.subject();
788 std::vector<std::string> ou_tokens;
789 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
790 ou_tokens.push_back(
791 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
792 }
793
794 std::vector<std::string> o_tokens;
795 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
796 o_tokens.push_back(
797 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
798 }
799
800 std::string cn_token;
801 if (!subject.common_name.empty()) {
802 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
803 }
804
805 std::set<std::string> paths_to_check;
806 if (!cn_token.empty()) {
807 paths_to_check.insert(cn_token);
808 }
809 for (size_t i = 0; i < o_tokens.size(); ++i) {
810 paths_to_check.insert(cn_token + o_tokens[i]);
811 paths_to_check.insert(o_tokens[i]);
812 for (size_t j = 0; j < ou_tokens.size(); ++j) {
813 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
814 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
815 }
816 }
817 for (size_t i = 0; i < ou_tokens.size(); ++i) {
818 paths_to_check.insert(cn_token + ou_tokens[i]);
819 paths_to_check.insert(ou_tokens[i]);
820 }
821
822 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
823 sizeof(issuer.fingerprint().data));
824 for (std::set<std::string>::iterator it = paths_to_check.begin();
825 it != paths_to_check.end(); ++it) {
826 whitelist_strings->push_back("cert/" + issuer_fp + *it);
827 }
828 }
829
707 } // namespace safe_browsing 830 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698