| 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
|
|
|