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

Unified 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, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/safe_browsing/download_protection_service.cc
diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc
index 7fe07580457dd4280c89dac7c48f23e5d9f89f45..5b37d670f6d05fada8ae0f4aaf6891546f088322 100644
--- a/chrome/browser/safe_browsing/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection_service.cc
@@ -24,6 +24,8 @@
#include "content/public/common/url_fetcher.h"
#include "content/public/common/url_fetcher_delegate.h"
#include "net/base/load_flags.h"
+#include "net/base/x509_cert_types.h"
+#include "net/base/x509_certificate.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
@@ -503,12 +505,15 @@ class DownloadProtectionService::CheckClientDownloadRequest
for (size_t i = 0; i < info_.download_url_chain.size(); ++i) {
const GURL& url = info_.download_url_chain[i];
if (url.is_valid() && sb_service_->MatchDownloadWhitelistUrl(url)) {
+ VLOG(2) << url << " is on the download whitelist.";
reason = REASON_WHITELISTED_URL;
break;
}
}
if (info_.referrer_url.is_valid() && reason == REASON_MAX &&
sb_service_->MatchDownloadWhitelistUrl(info_.referrer_url)) {
+ VLOG(2) << "Referrer url " << info_.referrer_url
+ << " is on the download whitelist.";
reason = REASON_WHITELISTED_REFERRER;
}
if (reason != REASON_MAX || signature_info_.trusted()) {
@@ -516,9 +521,13 @@ class DownloadProtectionService::CheckClientDownloadRequest
}
}
if (reason == REASON_MAX && signature_info_.trusted()) {
- // TODO(noelutz): implement a certificate whitelist and only whitelist
- // binaries whose certificate match the whitelist.
- reason = REASON_TRUSTED_EXECUTABLE;
+ for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
+ if (CertificateChainIsWhitelisted(
+ signature_info_.certificate_chain(i))) {
+ reason = REASON_TRUSTED_EXECUTABLE;
+ break;
+ }
+ }
}
if (reason != REASON_MAX) {
RecordImprovedProtectionStats(reason);
@@ -614,6 +623,46 @@ class DownloadProtectionService::CheckClientDownloadRequest
REASON_MAX);
}
+ bool CertificateChainIsWhitelisted(
+ const ClientDownloadRequest_CertificateChain& chain) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (chain.element_size() < 2) {
+ // We need to have both a signing certificate and its issuer certificate
+ // present to construct a whitelist entry.
+ return false;
+ }
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateFromBytes(
+ chain.element(0).certificate().data(),
+ chain.element(0).certificate().size());
+ if (!cert.get()) {
+ return false;
+ }
+
+ for (int i = 1; i < chain.element_size(); ++i) {
+ scoped_refptr<net::X509Certificate> issuer =
+ net::X509Certificate::CreateFromBytes(
+ chain.element(i).certificate().data(),
+ chain.element(i).certificate().size());
+ if (!issuer.get()) {
+ return false;
+ }
+ std::vector<std::string> whitelist_strings;
+ DownloadProtectionService::GetCertificateWhitelistStrings(
+ *cert, *issuer, &whitelist_strings);
+ for (size_t j = 0; j < whitelist_strings.size(); ++j) {
+ if (sb_service_->MatchDownloadWhitelistString(whitelist_strings[j])) {
+ VLOG(2) << "Certificate matched whitelist, cert="
+ << cert->subject().GetDisplayName()
+ << " issuer=" << issuer->subject().GetDisplayName();
+ return true;
+ }
+ }
+ cert = issuer;
+ }
+ return false;
+ }
+
DownloadInfo info_;
ClientDownloadRequest_SignatureInfo signature_info_;
CheckDownloadCallback callback_;
@@ -704,4 +753,80 @@ void DownloadProtectionService::RequestFinished(
DCHECK(it != download_requests_.end());
download_requests_.erase(*it);
}
+
+namespace {
+// Escapes a certificate attribute so that it can be used in a whitelist
+// entry. Currently, we only escape slashes, since they are used as a
+// separator between attributes.
+std::string EscapeCertAttribute(const std::string& attribute) {
+ std::string escaped;
+ for (size_t i = 0; i < attribute.size(); ++i) {
+ if (attribute[i] == '%') {
+ escaped.append("%25");
+ } else if (attribute[i] == '/') {
+ escaped.append("%2F");
+ } else {
+ escaped.push_back(attribute[i]);
+ }
+ }
+ return escaped;
+}
+} // namespace
+
+// static
+void DownloadProtectionService::GetCertificateWhitelistStrings(
+ const net::X509Certificate& certificate,
+ const net::X509Certificate& issuer,
+ std::vector<std::string>* whitelist_strings) {
+ // The whitelist paths are in the format:
+ // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
+ //
+ // Any of CN, O, or OU may be omitted from the whitelist entry, in which
+ // case they match anything. However, the attributes that do appear will
+ // always be in the order shown above. At least one attribute will always
+ // be present.
+
+ const net::CertPrincipal& subject = certificate.subject();
+ std::vector<std::string> ou_tokens;
+ for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
+ ou_tokens.push_back(
+ "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
+ }
+
+ std::vector<std::string> o_tokens;
+ for (size_t i = 0; i < subject.organization_names.size(); ++i) {
+ o_tokens.push_back(
+ "/O=" + EscapeCertAttribute(subject.organization_names[i]));
+ }
+
+ std::string cn_token;
+ if (!subject.common_name.empty()) {
+ cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
+ }
+
+ std::set<std::string> paths_to_check;
+ if (!cn_token.empty()) {
+ paths_to_check.insert(cn_token);
+ }
+ for (size_t i = 0; i < o_tokens.size(); ++i) {
+ paths_to_check.insert(cn_token + o_tokens[i]);
+ paths_to_check.insert(o_tokens[i]);
+ for (size_t j = 0; j < ou_tokens.size(); ++j) {
+ paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
+ paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
+ }
+ }
+ for (size_t i = 0; i < ou_tokens.size(); ++i) {
+ paths_to_check.insert(cn_token + ou_tokens[i]);
+ paths_to_check.insert(ou_tokens[i]);
+ }
+
+ std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
+ sizeof(issuer.fingerprint().data));
+ for (std::set<std::string>::iterator it = paths_to_check.begin();
+ it != paths_to_check.end(); ++it) {
+ whitelist_strings->push_back("cert/" + issuer_fp + *it);
+ }
+}
+
} // namespace safe_browsing

Powered by Google App Engine
This is Rietveld 408576698