toolkit/components/downloads/ApplicationReputation.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/downloads/ApplicationReputation.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,964 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +// See
    1.10 +// https://wiki.mozilla.org/Security/Features/Application_Reputation_Design_Doc
    1.11 +// for a description of Chrome's implementation of this feature.
    1.12 +#include "ApplicationReputation.h"
    1.13 +#include "csd.pb.h"
    1.14 +
    1.15 +#include "nsIArray.h"
    1.16 +#include "nsIApplicationReputation.h"
    1.17 +#include "nsIChannel.h"
    1.18 +#include "nsIHttpChannel.h"
    1.19 +#include "nsIIOService.h"
    1.20 +#include "nsIPrefService.h"
    1.21 +#include "nsIScriptSecurityManager.h"
    1.22 +#include "nsIStreamListener.h"
    1.23 +#include "nsIStringStream.h"
    1.24 +#include "nsIUploadChannel2.h"
    1.25 +#include "nsIURI.h"
    1.26 +#include "nsIUrlClassifierDBService.h"
    1.27 +#include "nsIX509Cert.h"
    1.28 +#include "nsIX509CertDB.h"
    1.29 +#include "nsIX509CertList.h"
    1.30 +
    1.31 +#include "mozilla/Preferences.h"
    1.32 +#include "mozilla/Services.h"
    1.33 +#include "mozilla/Telemetry.h"
    1.34 +#include "mozilla/TimeStamp.h"
    1.35 +#include "mozilla/LoadContext.h"
    1.36 +
    1.37 +#include "nsAutoPtr.h"
    1.38 +#include "nsCOMPtr.h"
    1.39 +#include "nsDebug.h"
    1.40 +#include "nsError.h"
    1.41 +#include "nsNetCID.h"
    1.42 +#include "nsReadableUtils.h"
    1.43 +#include "nsServiceManagerUtils.h"
    1.44 +#include "nsString.h"
    1.45 +#include "nsTArray.h"
    1.46 +#include "nsThreadUtils.h"
    1.47 +#include "nsXPCOMStrings.h"
    1.48 +
    1.49 +using mozilla::Preferences;
    1.50 +using mozilla::TimeStamp;
    1.51 +using mozilla::Telemetry::Accumulate;
    1.52 +using safe_browsing::ClientDownloadRequest;
    1.53 +using safe_browsing::ClientDownloadRequest_SignatureInfo;
    1.54 +using safe_browsing::ClientDownloadRequest_CertificateChain;
    1.55 +
    1.56 +// Preferences that we need to initialize the query.
    1.57 +#define PREF_SB_APP_REP_URL "browser.safebrowsing.appRepURL"
    1.58 +#define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
    1.59 +#define PREF_GENERAL_LOCALE "general.useragent.locale"
    1.60 +#define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.downloadBlockTable"
    1.61 +#define PREF_DOWNLOAD_ALLOW_TABLE "urlclassifier.downloadAllowTable"
    1.62 +
    1.63 +// NSPR_LOG_MODULES=ApplicationReputation:5
    1.64 +#if defined(PR_LOGGING)
    1.65 +PRLogModuleInfo *ApplicationReputationService::prlog = nullptr;
    1.66 +#define LOG(args) PR_LOG(ApplicationReputationService::prlog, PR_LOG_DEBUG, args)
    1.67 +#define LOG_ENABLED() PR_LOG_TEST(ApplicationReputationService::prlog, 4)
    1.68 +#else
    1.69 +#define LOG(args)
    1.70 +#define LOG_ENABLED() (false)
    1.71 +#endif
    1.72 +
    1.73 +class PendingDBLookup;
    1.74 +
    1.75 +// A single use class private to ApplicationReputationService encapsulating an
    1.76 +// nsIApplicationReputationQuery and an nsIApplicationReputationCallback. Once
    1.77 +// created by ApplicationReputationService, it is guaranteed to call mCallback.
    1.78 +// This class is private to ApplicationReputationService.
    1.79 +class PendingLookup MOZ_FINAL : public nsIStreamListener
    1.80 +{
    1.81 +public:
    1.82 +  NS_DECL_ISUPPORTS
    1.83 +  NS_DECL_NSIREQUESTOBSERVER
    1.84 +  NS_DECL_NSISTREAMLISTENER
    1.85 +
    1.86 +  // Constructor and destructor.
    1.87 +  PendingLookup(nsIApplicationReputationQuery* aQuery,
    1.88 +                nsIApplicationReputationCallback* aCallback);
    1.89 +  ~PendingLookup();
    1.90 +
    1.91 +  // Start the lookup. The lookup may have 2 parts: local and remote. In the
    1.92 +  // local lookup, PendingDBLookups are created to query the local allow and
    1.93 +  // blocklists for various URIs associated with this downloaded file. In the
    1.94 +  // event that no results are found, a remote lookup is sent to the Application
    1.95 +  // Reputation server.
    1.96 +  nsresult StartLookup();
    1.97 +
    1.98 +private:
    1.99 +  friend class PendingDBLookup;
   1.100 +
   1.101 +  // Telemetry states.
   1.102 +  // Status of the remote response (valid or not).
   1.103 +  enum SERVER_RESPONSE_TYPES {
   1.104 +    SERVER_RESPONSE_VALID = 0,
   1.105 +    SERVER_RESPONSE_FAILED = 1,
   1.106 +    SERVER_RESPONSE_INVALID = 2,
   1.107 +  };
   1.108 +
   1.109 +  // The query containing metadata about the downloaded file.
   1.110 +  nsCOMPtr<nsIApplicationReputationQuery> mQuery;
   1.111 +
   1.112 +  // The callback with which to report the verdict.
   1.113 +  nsCOMPtr<nsIApplicationReputationCallback> mCallback;
   1.114 +
   1.115 +  // An array of strings created from certificate information used to whitelist
   1.116 +  // the downloaded file.
   1.117 +  nsTArray<nsCString> mAllowlistSpecs;
   1.118 +  // The source URI of the download, the referrer and possibly any redirects.
   1.119 +  nsTArray<nsCString> mAnylistSpecs;
   1.120 +
   1.121 +  // When we started this query
   1.122 +  TimeStamp mStartTime;
   1.123 +
   1.124 +  // The protocol buffer used to store signature information extracted using
   1.125 +  // the Windows Authenticode API, if the binary is signed.
   1.126 +  ClientDownloadRequest_SignatureInfo mSignatureInfo;
   1.127 +
   1.128 +  // The response from the application reputation query. This is read in chunks
   1.129 +  // as part of our nsIStreamListener implementation and may contain embedded
   1.130 +  // NULLs.
   1.131 +  nsCString mResponse;
   1.132 +
   1.133 +  // Returns true if the file is likely to be binary on Windows.
   1.134 +  bool IsBinaryFile();
   1.135 +
   1.136 +  // Clean up and call the callback. PendingLookup must not be used after this
   1.137 +  // function is called.
   1.138 +  nsresult OnComplete(bool shouldBlock, nsresult rv);
   1.139 +
   1.140 +  // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
   1.141 +  // guarantee calling the callback
   1.142 +  nsresult OnStopRequestInternal(nsIRequest *aRequest,
   1.143 +                                 nsISupports *aContext,
   1.144 +                                 nsresult aResult,
   1.145 +                                 bool* aShouldBlock);
   1.146 +
   1.147 +  // Escape '/' and '%' in certificate attribute values.
   1.148 +  nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
   1.149 +
   1.150 +  // Escape ':' in fingerprint values.
   1.151 +  nsCString EscapeFingerprint(const nsACString& aAttribute);
   1.152 +
   1.153 +  // Generate whitelist strings for the given certificate pair from the same
   1.154 +  // certificate chain.
   1.155 +  nsresult GenerateWhitelistStringsForPair(
   1.156 +    nsIX509Cert* certificate, nsIX509Cert* issuer);
   1.157 +
   1.158 +  // Generate whitelist strings for the given certificate chain, which starts
   1.159 +  // with the signer and may go all the way to the root cert.
   1.160 +  nsresult GenerateWhitelistStringsForChain(
   1.161 +    const ClientDownloadRequest_CertificateChain& aChain);
   1.162 +
   1.163 +  // For signed binaries, generate strings of the form:
   1.164 +  // http://sb-ssl.google.com/safebrowsing/csd/certificate/
   1.165 +  //   <issuer_cert_sha1_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
   1.166 +  // for each (cert, issuer) pair in each chain of certificates that is
   1.167 +  // associated with the binary.
   1.168 +  nsresult GenerateWhitelistStrings(
   1.169 +    const ClientDownloadRequest_SignatureInfo& aSignatureInfo);
   1.170 +
   1.171 +  // Parse the XPCOM certificate lists and stick them into the protocol buffer
   1.172 +  // version.
   1.173 +  nsresult ParseCertificates(nsIArray* aSigArray,
   1.174 +                             ClientDownloadRequest_SignatureInfo* aSigInfo);
   1.175 +
   1.176 +  // Helper function to ensure that we call PendingLookup::LookupNext or
   1.177 +  // PendingLookup::OnComplete.
   1.178 +  nsresult DoLookupInternal();
   1.179 +
   1.180 +  // Looks up all the URIs that may be responsible for allowlisting or
   1.181 +  // blocklisting the downloaded file. These URIs may include whitelist strings
   1.182 +  // generated by certificates verifying the binary as well as the target URI
   1.183 +  // from which the file was downloaded.
   1.184 +  nsresult LookupNext();
   1.185 +
   1.186 +  // Sends a query to the remote application reputation service. Returns NS_OK
   1.187 +  // on success.
   1.188 +  nsresult SendRemoteQuery();
   1.189 +
   1.190 +  // Helper function to ensure that we always call the callback.
   1.191 +  nsresult SendRemoteQueryInternal();
   1.192 +};
   1.193 +
   1.194 +// A single-use class for looking up a single URI in the safebrowsing DB. This
   1.195 +// class is private to PendingLookup.
   1.196 +class PendingDBLookup MOZ_FINAL : public nsIUrlClassifierCallback
   1.197 +{
   1.198 +public:
   1.199 +  NS_DECL_ISUPPORTS
   1.200 +  NS_DECL_NSIURLCLASSIFIERCALLBACK
   1.201 +
   1.202 +  // Constructor and destructor
   1.203 +  PendingDBLookup(PendingLookup* aPendingLookup);
   1.204 +  ~PendingDBLookup();
   1.205 +
   1.206 +  // Look up the given URI in the safebrowsing DBs, optionally on both the allow
   1.207 +  // list and the blocklist. If there is a match, call
   1.208 +  // PendingLookup::OnComplete. Otherwise, call PendingLookup::LookupNext.
   1.209 +  nsresult LookupSpec(const nsACString& aSpec, bool aAllowlistOnly);
   1.210 +private:
   1.211 +  // The download appeared on the allowlist, blocklist, or no list (and thus
   1.212 +  // could trigger a remote query.
   1.213 +  enum LIST_TYPES {
   1.214 +    ALLOW_LIST = 0,
   1.215 +    BLOCK_LIST = 1,
   1.216 +    NO_LIST = 2,
   1.217 +  };
   1.218 +
   1.219 +  nsCString mSpec;
   1.220 +  bool mAllowlistOnly;
   1.221 +  nsRefPtr<PendingLookup> mPendingLookup;
   1.222 +  nsresult LookupSpecInternal(const nsACString& aSpec);
   1.223 +};
   1.224 +
   1.225 +NS_IMPL_ISUPPORTS(PendingDBLookup,
   1.226 +                  nsIUrlClassifierCallback)
   1.227 +
   1.228 +PendingDBLookup::PendingDBLookup(PendingLookup* aPendingLookup) :
   1.229 +  mAllowlistOnly(false),
   1.230 +  mPendingLookup(aPendingLookup)
   1.231 +{
   1.232 +  LOG(("Created pending DB lookup [this = %p]", this));
   1.233 +}
   1.234 +
   1.235 +PendingDBLookup::~PendingDBLookup()
   1.236 +{
   1.237 +  LOG(("Destroying pending DB lookup [this = %p]", this));
   1.238 +  mPendingLookup = nullptr;
   1.239 +}
   1.240 +
   1.241 +nsresult
   1.242 +PendingDBLookup::LookupSpec(const nsACString& aSpec,
   1.243 +                            bool aAllowlistOnly)
   1.244 +{
   1.245 +  LOG(("Checking principal %s", aSpec.Data()));
   1.246 +  mSpec = aSpec;
   1.247 +  mAllowlistOnly = aAllowlistOnly;
   1.248 +  nsresult rv = LookupSpecInternal(aSpec);
   1.249 +  if (NS_FAILED(rv)) {
   1.250 +    LOG(("Error in LookupSpecInternal"));
   1.251 +    return mPendingLookup->OnComplete(false, NS_OK);
   1.252 +  }
   1.253 +  // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
   1.254 +  // guaranteed to call HandleEvent.
   1.255 +  return rv;
   1.256 +}
   1.257 +
   1.258 +nsresult
   1.259 +PendingDBLookup::LookupSpecInternal(const nsACString& aSpec)
   1.260 +{
   1.261 +  nsresult rv;
   1.262 +
   1.263 +  nsCOMPtr<nsIURI> uri;
   1.264 +  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
   1.265 +  rv = ios->NewURI(aSpec, nullptr, nullptr, getter_AddRefs(uri));
   1.266 +  NS_ENSURE_SUCCESS(rv, rv);
   1.267 +
   1.268 +  nsCOMPtr<nsIPrincipal> principal;
   1.269 +  nsCOMPtr<nsIScriptSecurityManager> secMan =
   1.270 +    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   1.271 +  NS_ENSURE_SUCCESS(rv, rv);
   1.272 +
   1.273 +  rv = secMan->GetNoAppCodebasePrincipal(uri, getter_AddRefs(principal));
   1.274 +  NS_ENSURE_SUCCESS(rv, rv);
   1.275 +
   1.276 +  // Check local lists to see if the URI has already been whitelisted or
   1.277 +  // blacklisted.
   1.278 +  LOG(("Checking DB service for principal %s [this = %p]", mSpec.get(), this));
   1.279 +  nsCOMPtr<nsIUrlClassifierDBService> dbService =
   1.280 +    do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
   1.281 +  nsAutoCString tables;
   1.282 +  nsAutoCString allowlist;
   1.283 +  Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowlist);
   1.284 +  if (!allowlist.IsEmpty()) {
   1.285 +    tables.Append(allowlist);
   1.286 +  }
   1.287 +  nsAutoCString blocklist;
   1.288 +  Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blocklist);
   1.289 +  if (!mAllowlistOnly && !blocklist.IsEmpty()) {
   1.290 +    tables.Append(",");
   1.291 +    tables.Append(blocklist);
   1.292 +  }
   1.293 +  return dbService->Lookup(principal, tables, this);
   1.294 +}
   1.295 +
   1.296 +NS_IMETHODIMP
   1.297 +PendingDBLookup::HandleEvent(const nsACString& tables)
   1.298 +{
   1.299 +  // HandleEvent is guaranteed to call either:
   1.300 +  // 1) PendingLookup::OnComplete if the URL can be classified locally, or
   1.301 +  // 2) PendingLookup::LookupNext if the URL can be cannot classified locally.
   1.302 +  // Blocklisting trumps allowlisting.
   1.303 +  nsAutoCString blockList;
   1.304 +  Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
   1.305 +  if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
   1.306 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
   1.307 +    LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
   1.308 +    return mPendingLookup->OnComplete(true, NS_OK);
   1.309 +  }
   1.310 +
   1.311 +  nsAutoCString allowList;
   1.312 +  Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
   1.313 +  if (FindInReadable(allowList, tables)) {
   1.314 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
   1.315 +    LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
   1.316 +    return mPendingLookup->OnComplete(false, NS_OK);
   1.317 +  }
   1.318 +
   1.319 +  LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(), this));
   1.320 +  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
   1.321 +  return mPendingLookup->LookupNext();
   1.322 +}
   1.323 +
   1.324 +NS_IMPL_ISUPPORTS(PendingLookup,
   1.325 +                  nsIStreamListener,
   1.326 +                  nsIRequestObserver)
   1.327 +
   1.328 +PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
   1.329 +                             nsIApplicationReputationCallback* aCallback) :
   1.330 +  mQuery(aQuery),
   1.331 +  mCallback(aCallback)
   1.332 +{
   1.333 +  LOG(("Created pending lookup [this = %p]", this));
   1.334 +}
   1.335 +
   1.336 +PendingLookup::~PendingLookup()
   1.337 +{
   1.338 +  LOG(("Destroying pending lookup [this = %p]", this));
   1.339 +}
   1.340 +
   1.341 +bool
   1.342 +PendingLookup::IsBinaryFile()
   1.343 +{
   1.344 +  nsString fileName;
   1.345 +  nsresult rv = mQuery->GetSuggestedFileName(fileName);
   1.346 +  if (NS_FAILED(rv)) {
   1.347 +    return false;
   1.348 +  }
   1.349 +  return
   1.350 +    // Executable extensions for MS Windows, from
   1.351 +    // https://code.google.com/p/chromium/codesearch#chromium/src/chrome/common/safe_browsing/download_protection_util.cc&l=14
   1.352 +    StringEndsWith(fileName, NS_LITERAL_STRING(".apk")) ||
   1.353 +    StringEndsWith(fileName, NS_LITERAL_STRING(".bas")) ||
   1.354 +    StringEndsWith(fileName, NS_LITERAL_STRING(".bat")) ||
   1.355 +    StringEndsWith(fileName, NS_LITERAL_STRING(".cab")) ||
   1.356 +    StringEndsWith(fileName, NS_LITERAL_STRING(".cmd")) ||
   1.357 +    StringEndsWith(fileName, NS_LITERAL_STRING(".com")) ||
   1.358 +    StringEndsWith(fileName, NS_LITERAL_STRING(".exe")) ||
   1.359 +    StringEndsWith(fileName, NS_LITERAL_STRING(".hta")) ||
   1.360 +    StringEndsWith(fileName, NS_LITERAL_STRING(".msi")) ||
   1.361 +    StringEndsWith(fileName, NS_LITERAL_STRING(".pif")) ||
   1.362 +    StringEndsWith(fileName, NS_LITERAL_STRING(".reg")) ||
   1.363 +    StringEndsWith(fileName, NS_LITERAL_STRING(".scr")) ||
   1.364 +    StringEndsWith(fileName, NS_LITERAL_STRING(".vb")) ||
   1.365 +    StringEndsWith(fileName, NS_LITERAL_STRING(".vbs")) ||
   1.366 +    StringEndsWith(fileName, NS_LITERAL_STRING(".zip"));
   1.367 +}
   1.368 +
   1.369 +nsresult
   1.370 +PendingLookup::LookupNext()
   1.371 +{
   1.372 +  // We must call LookupNext or SendRemoteQuery upon return.
   1.373 +  // Look up all of the URLs that could whitelist this download.
   1.374 +  // Blacklist first.
   1.375 +  int index = mAnylistSpecs.Length() - 1;
   1.376 +  nsCString spec;
   1.377 +  bool allowlistOnly = false;
   1.378 +  if (index >= 0) {
   1.379 +    // Check the source URI and referrer.
   1.380 +    spec = mAnylistSpecs[index];
   1.381 +    mAnylistSpecs.RemoveElementAt(index);
   1.382 +  } else {
   1.383 +    // Check the allowlists next.
   1.384 +    index = mAllowlistSpecs.Length() - 1;
   1.385 +    if (index >= 0) {
   1.386 +      allowlistOnly = true;
   1.387 +      spec = mAllowlistSpecs[index];
   1.388 +      mAllowlistSpecs.RemoveElementAt(index);
   1.389 +    }
   1.390 +  }
   1.391 +  if (index >= 0) {
   1.392 +    nsRefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
   1.393 +    return lookup->LookupSpec(spec, allowlistOnly);
   1.394 +  }
   1.395 +#ifdef XP_WIN
   1.396 +  // There are no more URIs to check against local list. If the file is not
   1.397 +  // eligible for remote lookup, bail.
   1.398 +  if (!IsBinaryFile()) {
   1.399 +    LOG(("Not eligible for remote lookups [this=%x]", this));
   1.400 +    return OnComplete(false, NS_OK);
   1.401 +  }
   1.402 +  // Send the remote query if we are on Windows.
   1.403 +  nsresult rv = SendRemoteQuery();
   1.404 +  if (NS_FAILED(rv)) {
   1.405 +    return OnComplete(false, rv);
   1.406 +  }
   1.407 +  return NS_OK;
   1.408 +#else
   1.409 +  return OnComplete(false, NS_OK);
   1.410 +#endif
   1.411 +}
   1.412 +
   1.413 +nsCString
   1.414 +PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
   1.415 +{
   1.416 +  // Escape '/' because it's a field separator, and '%' because Chrome does
   1.417 +  nsCString escaped;
   1.418 +  escaped.SetCapacity(aAttribute.Length());
   1.419 +  for (unsigned int i = 0; i < aAttribute.Length(); ++i) {
   1.420 +    if (aAttribute.Data()[i] == '%') {
   1.421 +      escaped.Append("%25");
   1.422 +    } else if (aAttribute.Data()[i] == '/') {
   1.423 +      escaped.Append("%2F");
   1.424 +    } else if (aAttribute.Data()[i] == ' ') {
   1.425 +      escaped.Append("%20");
   1.426 +    } else {
   1.427 +      escaped.Append(aAttribute.Data()[i]);
   1.428 +    }
   1.429 +  }
   1.430 +  return escaped;
   1.431 +}
   1.432 +
   1.433 +nsCString
   1.434 +PendingLookup::EscapeFingerprint(const nsACString& aFingerprint)
   1.435 +{
   1.436 +  // Google's fingerprint doesn't have colons
   1.437 +  nsCString escaped;
   1.438 +  escaped.SetCapacity(aFingerprint.Length());
   1.439 +  for (unsigned int i = 0; i < aFingerprint.Length(); ++i) {
   1.440 +    if (aFingerprint.Data()[i] != ':') {
   1.441 +      escaped.Append(aFingerprint.Data()[i]);
   1.442 +    }
   1.443 +  }
   1.444 +  return escaped;
   1.445 +}
   1.446 +
   1.447 +nsresult
   1.448 +PendingLookup::GenerateWhitelistStringsForPair(
   1.449 +  nsIX509Cert* certificate,
   1.450 +  nsIX509Cert* issuer)
   1.451 +{
   1.452 +  // The whitelist paths have format:
   1.453 +  // http://sb-ssl.google.com/safebrowsing/csd/certificate/<issuer_cert_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
   1.454 +  // Any of CN, O, or OU may be omitted from the whitelist entry. Unfortunately
   1.455 +  // this is not publicly documented, but the Chrome implementation can be found
   1.456 +  // here:
   1.457 +  // https://code.google.com/p/chromium/codesearch#search/&q=GetCertificateWhitelistStrings
   1.458 +  nsCString whitelistString(
   1.459 +    "http://sb-ssl.google.com/safebrowsing/csd/certificate/");
   1.460 +
   1.461 +  nsString fingerprint;
   1.462 +  nsresult rv = issuer->GetSha1Fingerprint(fingerprint);
   1.463 +  NS_ENSURE_SUCCESS(rv, rv);
   1.464 +  whitelistString.Append(
   1.465 +    EscapeFingerprint(NS_ConvertUTF16toUTF8(fingerprint)));
   1.466 +
   1.467 +  nsString commonName;
   1.468 +  rv = certificate->GetCommonName(commonName);
   1.469 +  NS_ENSURE_SUCCESS(rv, rv);
   1.470 +  if (!commonName.IsEmpty()) {
   1.471 +    whitelistString.Append("/CN=");
   1.472 +    whitelistString.Append(
   1.473 +      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(commonName)));
   1.474 +  }
   1.475 +
   1.476 +  nsString organization;
   1.477 +  rv = certificate->GetOrganization(organization);
   1.478 +  NS_ENSURE_SUCCESS(rv, rv);
   1.479 +  if (!organization.IsEmpty()) {
   1.480 +    whitelistString.Append("/O=");
   1.481 +    whitelistString.Append(
   1.482 +      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organization)));
   1.483 +  }
   1.484 +
   1.485 +  nsString organizationalUnit;
   1.486 +  rv = certificate->GetOrganizationalUnit(organizationalUnit);
   1.487 +  NS_ENSURE_SUCCESS(rv, rv);
   1.488 +  if (!organizationalUnit.IsEmpty()) {
   1.489 +    whitelistString.Append("/OU=");
   1.490 +    whitelistString.Append(
   1.491 +      EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organizationalUnit)));
   1.492 +  }
   1.493 +  LOG(("Whitelisting %s", whitelistString.get()));
   1.494 +
   1.495 +  mAllowlistSpecs.AppendElement(whitelistString);
   1.496 +  return NS_OK;
   1.497 +}
   1.498 +
   1.499 +nsresult
   1.500 +PendingLookup::GenerateWhitelistStringsForChain(
   1.501 +  const safe_browsing::ClientDownloadRequest_CertificateChain& aChain)
   1.502 +{
   1.503 +  // We need a signing certificate and an issuer to construct a whitelist
   1.504 +  // entry.
   1.505 +  if (aChain.element_size() < 2) {
   1.506 +    return NS_OK;
   1.507 +  }
   1.508 +
   1.509 +  // Get the signer.
   1.510 +  nsresult rv;
   1.511 +  nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
   1.512 +  NS_ENSURE_SUCCESS(rv, rv);
   1.513 +
   1.514 +  nsCOMPtr<nsIX509Cert> signer;
   1.515 +  rv = certDB->ConstructX509(
   1.516 +    const_cast<char *>(aChain.element(0).certificate().data()),
   1.517 +    aChain.element(0).certificate().size(), getter_AddRefs(signer));
   1.518 +  NS_ENSURE_SUCCESS(rv, rv);
   1.519 +
   1.520 +  for (int i = 1; i < aChain.element_size(); ++i) {
   1.521 +    // Get the issuer.
   1.522 +    nsCOMPtr<nsIX509Cert> issuer;
   1.523 +    rv = certDB->ConstructX509(
   1.524 +      const_cast<char *>(aChain.element(i).certificate().data()),
   1.525 +      aChain.element(i).certificate().size(), getter_AddRefs(issuer));
   1.526 +    NS_ENSURE_SUCCESS(rv, rv);
   1.527 +
   1.528 +    nsresult rv = GenerateWhitelistStringsForPair(signer, issuer);
   1.529 +    NS_ENSURE_SUCCESS(rv, rv);
   1.530 +  }
   1.531 +  return NS_OK;
   1.532 +}
   1.533 +
   1.534 +nsresult
   1.535 +PendingLookup::GenerateWhitelistStrings(
   1.536 +  const safe_browsing::ClientDownloadRequest_SignatureInfo& aSignatureInfo)
   1.537 +{
   1.538 +  for (int i = 0; i < aSignatureInfo.certificate_chain_size(); ++i) {
   1.539 +    nsresult rv = GenerateWhitelistStringsForChain(
   1.540 +      aSignatureInfo.certificate_chain(i));
   1.541 +    NS_ENSURE_SUCCESS(rv, rv);
   1.542 +  }
   1.543 +  return NS_OK;
   1.544 +}
   1.545 +
   1.546 +nsresult
   1.547 +PendingLookup::StartLookup()
   1.548 +{
   1.549 +  mStartTime = TimeStamp::Now();
   1.550 +  nsresult rv = DoLookupInternal();
   1.551 +  if (NS_FAILED(rv)) {
   1.552 +    return OnComplete(false, NS_OK);
   1.553 +  };
   1.554 +  return rv;
   1.555 +}
   1.556 +
   1.557 +nsresult
   1.558 +PendingLookup::DoLookupInternal()
   1.559 +{
   1.560 +  // We want to check the target URI against the local lists.
   1.561 +  nsCOMPtr<nsIURI> uri;
   1.562 +  nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
   1.563 +  NS_ENSURE_SUCCESS(rv, rv);
   1.564 +
   1.565 +  nsCString spec;
   1.566 +  rv = uri->GetSpec(spec);
   1.567 +  NS_ENSURE_SUCCESS(rv, rv);
   1.568 +  mAnylistSpecs.AppendElement(spec);
   1.569 +
   1.570 +  nsCOMPtr<nsIURI> referrer = nullptr;
   1.571 +  rv = mQuery->GetReferrerURI(getter_AddRefs(referrer));
   1.572 +  if (referrer) {
   1.573 +    nsCString spec;
   1.574 +    rv = referrer->GetSpec(spec);
   1.575 +    NS_ENSURE_SUCCESS(rv, rv);
   1.576 +    mAnylistSpecs.AppendElement(spec);
   1.577 +  }
   1.578 +
   1.579 +  // Extract the signature and parse certificates so we can use it to check
   1.580 +  // whitelists.
   1.581 +  nsCOMPtr<nsIArray> sigArray;
   1.582 +  rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
   1.583 +  NS_ENSURE_SUCCESS(rv, rv);
   1.584 +
   1.585 +  if (sigArray) {
   1.586 +    rv = ParseCertificates(sigArray, &mSignatureInfo);
   1.587 +    NS_ENSURE_SUCCESS(rv, rv);
   1.588 +  }
   1.589 +
   1.590 +  rv = GenerateWhitelistStrings(mSignatureInfo);
   1.591 +  NS_ENSURE_SUCCESS(rv, rv);
   1.592 +
   1.593 +  // Start the call chain.
   1.594 +  return LookupNext();
   1.595 +}
   1.596 +
   1.597 +nsresult
   1.598 +PendingLookup::OnComplete(bool shouldBlock, nsresult rv)
   1.599 +{
   1.600 +  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
   1.601 +    shouldBlock);
   1.602 +#if defined(PR_LOGGING)
   1.603 +  double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
   1.604 +#endif
   1.605 +  if (shouldBlock) {
   1.606 +    LOG(("Application Reputation check failed, blocking bad binary in %f ms "
   1.607 +         "[this = %p]", t, this));
   1.608 +  } else {
   1.609 +    LOG(("Application Reputation check passed in %f ms [this = %p]", t, this));
   1.610 +  }
   1.611 +  nsresult res = mCallback->OnComplete(shouldBlock, rv);
   1.612 +  return res;
   1.613 +}
   1.614 +
   1.615 +nsresult
   1.616 +PendingLookup::ParseCertificates(
   1.617 +  nsIArray* aSigArray,
   1.618 +  ClientDownloadRequest_SignatureInfo* aSignatureInfo)
   1.619 +{
   1.620 +  // If we haven't been set for any reason, bail.
   1.621 +  NS_ENSURE_ARG_POINTER(aSigArray);
   1.622 +
   1.623 +  // Binaries may be signed by multiple chains of certificates. If there are no
   1.624 +  // chains, the binary is unsigned (or we were unable to extract signature
   1.625 +  // information on a non-Windows platform)
   1.626 +  nsCOMPtr<nsISimpleEnumerator> chains;
   1.627 +  nsresult rv = aSigArray->Enumerate(getter_AddRefs(chains));
   1.628 +  NS_ENSURE_SUCCESS(rv, rv);
   1.629 +
   1.630 +  bool hasMoreChains = false;
   1.631 +  rv = chains->HasMoreElements(&hasMoreChains);
   1.632 +  NS_ENSURE_SUCCESS(rv, rv);
   1.633 +
   1.634 +  while (hasMoreChains) {
   1.635 +    nsCOMPtr<nsISupports> supports;
   1.636 +    rv = chains->GetNext(getter_AddRefs(supports));
   1.637 +    NS_ENSURE_SUCCESS(rv, rv);
   1.638 +
   1.639 +    nsCOMPtr<nsIX509CertList> certList = do_QueryInterface(supports, &rv);
   1.640 +    NS_ENSURE_SUCCESS(rv, rv);
   1.641 +
   1.642 +    safe_browsing::ClientDownloadRequest_CertificateChain* certChain =
   1.643 +      aSignatureInfo->add_certificate_chain();
   1.644 +    nsCOMPtr<nsISimpleEnumerator> chainElt;
   1.645 +    rv = certList->GetEnumerator(getter_AddRefs(chainElt));
   1.646 +    NS_ENSURE_SUCCESS(rv, rv);
   1.647 +
   1.648 +    // Each chain may have multiple certificates.
   1.649 +    bool hasMoreCerts = false;
   1.650 +    rv = chainElt->HasMoreElements(&hasMoreCerts);
   1.651 +    while (hasMoreCerts) {
   1.652 +      nsCOMPtr<nsISupports> supports;
   1.653 +      rv = chainElt->GetNext(getter_AddRefs(supports));
   1.654 +      NS_ENSURE_SUCCESS(rv, rv);
   1.655 +
   1.656 +      nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(supports, &rv);
   1.657 +      NS_ENSURE_SUCCESS(rv, rv);
   1.658 +
   1.659 +      uint8_t* data = nullptr;
   1.660 +      uint32_t len = 0;
   1.661 +      rv = cert->GetRawDER(&len, &data);
   1.662 +      NS_ENSURE_SUCCESS(rv, rv);
   1.663 +
   1.664 +      // Add this certificate to the protobuf to send remotely.
   1.665 +      certChain->add_element()->set_certificate(data, len);
   1.666 +      nsMemory::Free(data);
   1.667 +
   1.668 +      rv = chainElt->HasMoreElements(&hasMoreCerts);
   1.669 +      NS_ENSURE_SUCCESS(rv, rv);
   1.670 +    }
   1.671 +    rv = chains->HasMoreElements(&hasMoreChains);
   1.672 +    NS_ENSURE_SUCCESS(rv, rv);
   1.673 +  }
   1.674 +  if (aSignatureInfo->certificate_chain_size() > 0) {
   1.675 +    aSignatureInfo->set_trusted(true);
   1.676 +  }
   1.677 +  return NS_OK;
   1.678 +}
   1.679 +
   1.680 +nsresult
   1.681 +PendingLookup::SendRemoteQuery()
   1.682 +{
   1.683 +  nsresult rv = SendRemoteQueryInternal();
   1.684 +  if (NS_FAILED(rv)) {
   1.685 +    return OnComplete(false, NS_OK);
   1.686 +  }
   1.687 +  // SendRemoteQueryInternal has fired off the query and we call OnComplete in
   1.688 +  // the nsIStreamListener.onStopRequest.
   1.689 +  return rv;
   1.690 +}
   1.691 +
   1.692 +nsresult
   1.693 +PendingLookup::SendRemoteQueryInternal()
   1.694 +{
   1.695 +  LOG(("Sending remote query for application reputation [this = %p]", this));
   1.696 +  // We did not find a local result, so fire off the query to the application
   1.697 +  // reputation service.
   1.698 +  safe_browsing::ClientDownloadRequest req;
   1.699 +  nsCOMPtr<nsIURI> uri;
   1.700 +  nsresult rv;
   1.701 +  rv = mQuery->GetSourceURI(getter_AddRefs(uri));
   1.702 +  NS_ENSURE_SUCCESS(rv, rv);
   1.703 +  nsCString spec;
   1.704 +  rv = uri->GetSpec(spec);
   1.705 +  NS_ENSURE_SUCCESS(rv, rv);
   1.706 +  req.set_url(spec.get());
   1.707 +
   1.708 +  uint32_t fileSize;
   1.709 +  rv = mQuery->GetFileSize(&fileSize);
   1.710 +  NS_ENSURE_SUCCESS(rv, rv);
   1.711 +  req.set_length(fileSize);
   1.712 +  // We have no way of knowing whether or not a user initiated the download.
   1.713 +  req.set_user_initiated(false);
   1.714 +
   1.715 +  nsCString locale;
   1.716 +  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_GENERAL_LOCALE, &locale),
   1.717 +                    NS_ERROR_NOT_AVAILABLE);
   1.718 +  req.set_locale(locale.get());
   1.719 +  nsCString sha256Hash;
   1.720 +  rv = mQuery->GetSha256Hash(sha256Hash);
   1.721 +  NS_ENSURE_SUCCESS(rv, rv);
   1.722 +  req.mutable_digests()->set_sha256(sha256Hash.Data());
   1.723 +  nsString fileName;
   1.724 +  rv = mQuery->GetSuggestedFileName(fileName);
   1.725 +  NS_ENSURE_SUCCESS(rv, rv);
   1.726 +  req.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
   1.727 +  req.mutable_signature()->CopyFrom(mSignatureInfo);
   1.728 +
   1.729 +  if (req.signature().trusted()) {
   1.730 +    LOG(("Got signed binary for remote application reputation check "
   1.731 +         "[this = %p]", this));
   1.732 +  } else {
   1.733 +    LOG(("Got unsigned binary for remote application reputation check "
   1.734 +         "[this = %p]", this));
   1.735 +  }
   1.736 +
   1.737 +  // Serialize the protocol buffer to a string. This can only fail if we are
   1.738 +  // out of memory, or if the protocol buffer req is missing required fields
   1.739 +  // (only the URL for now).
   1.740 +  std::string serialized;
   1.741 +  if (!req.SerializeToString(&serialized)) {
   1.742 +    return NS_ERROR_UNEXPECTED;
   1.743 +  }
   1.744 +
   1.745 +  // Set the input stream to the serialized protocol buffer
   1.746 +  nsCOMPtr<nsIStringInputStream> sstream =
   1.747 +    do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   1.748 +  NS_ENSURE_SUCCESS(rv, rv);
   1.749 +
   1.750 +  rv = sstream->SetData(serialized.c_str(), serialized.length());
   1.751 +  NS_ENSURE_SUCCESS(rv, rv);
   1.752 +
   1.753 +  // Set up the channel to transmit the request to the service.
   1.754 +  nsCOMPtr<nsIChannel> channel;
   1.755 +  nsCString serviceUrl;
   1.756 +  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
   1.757 +                    NS_ERROR_NOT_AVAILABLE);
   1.758 +  nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
   1.759 +  rv = ios->NewChannel(serviceUrl, nullptr, nullptr, getter_AddRefs(channel));
   1.760 +  NS_ENSURE_SUCCESS(rv, rv);
   1.761 +
   1.762 +  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
   1.763 +  NS_ENSURE_SUCCESS(rv, rv);
   1.764 +
   1.765 +  // Upload the protobuf to the application reputation service.
   1.766 +  nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel, &rv);
   1.767 +  NS_ENSURE_SUCCESS(rv, rv);
   1.768 +
   1.769 +  rv = uploadChannel->ExplicitSetUploadStream(sstream,
   1.770 +    NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
   1.771 +    NS_LITERAL_CSTRING("POST"), false);
   1.772 +  NS_ENSURE_SUCCESS(rv, rv);
   1.773 +
   1.774 +  // Set the Safebrowsing cookie jar, so that the regular Google cookie is not
   1.775 +  // sent with this request. See bug 897516.
   1.776 +  nsCOMPtr<nsIInterfaceRequestor> loadContext =
   1.777 +    new mozilla::LoadContext(NECKO_SAFEBROWSING_APP_ID);
   1.778 +  rv = channel->SetNotificationCallbacks(loadContext);
   1.779 +  NS_ENSURE_SUCCESS(rv, rv);
   1.780 +
   1.781 +  rv = channel->AsyncOpen(this, nullptr);
   1.782 +  NS_ENSURE_SUCCESS(rv, rv);
   1.783 +
   1.784 +  return NS_OK;
   1.785 +}
   1.786 +
   1.787 +////////////////////////////////////////////////////////////////////////////////
   1.788 +//// nsIStreamListener
   1.789 +static NS_METHOD
   1.790 +AppendSegmentToString(nsIInputStream* inputStream,
   1.791 +                      void *closure,
   1.792 +                      const char *rawSegment,
   1.793 +                      uint32_t toOffset,
   1.794 +                      uint32_t count,
   1.795 +                      uint32_t *writeCount) {
   1.796 +  nsAutoCString* decodedData = static_cast<nsAutoCString*>(closure);
   1.797 +  decodedData->Append(rawSegment, count);
   1.798 +  *writeCount = count;
   1.799 +  return NS_OK;
   1.800 +}
   1.801 +
   1.802 +NS_IMETHODIMP
   1.803 +PendingLookup::OnDataAvailable(nsIRequest *aRequest,
   1.804 +                               nsISupports *aContext,
   1.805 +                               nsIInputStream *aStream,
   1.806 +                               uint64_t offset,
   1.807 +                               uint32_t count) {
   1.808 +  uint32_t read;
   1.809 +  return aStream->ReadSegments(AppendSegmentToString, &mResponse, count, &read);
   1.810 +}
   1.811 +
   1.812 +NS_IMETHODIMP
   1.813 +PendingLookup::OnStartRequest(nsIRequest *aRequest,
   1.814 +                              nsISupports *aContext) {
   1.815 +  return NS_OK;
   1.816 +}
   1.817 +
   1.818 +NS_IMETHODIMP
   1.819 +PendingLookup::OnStopRequest(nsIRequest *aRequest,
   1.820 +                             nsISupports *aContext,
   1.821 +                             nsresult aResult) {
   1.822 +  NS_ENSURE_STATE(mCallback);
   1.823 +
   1.824 +  bool shouldBlock = false;
   1.825 +  nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
   1.826 +                                      &shouldBlock);
   1.827 +  OnComplete(shouldBlock, rv);
   1.828 +  return rv;
   1.829 +}
   1.830 +
   1.831 +nsresult
   1.832 +PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
   1.833 +                                     nsISupports *aContext,
   1.834 +                                     nsresult aResult,
   1.835 +                                     bool* aShouldBlock) {
   1.836 +  if (NS_FAILED(aResult)) {
   1.837 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.838 +      SERVER_RESPONSE_FAILED);
   1.839 +    return aResult;
   1.840 +  }
   1.841 +
   1.842 +  *aShouldBlock = false;
   1.843 +  nsresult rv;
   1.844 +  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
   1.845 +  if (NS_FAILED(rv)) {
   1.846 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.847 +      SERVER_RESPONSE_FAILED);
   1.848 +    return rv;
   1.849 +  }
   1.850 +
   1.851 +  uint32_t status = 0;
   1.852 +  rv = channel->GetResponseStatus(&status);
   1.853 +  if (NS_FAILED(rv)) {
   1.854 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.855 +      SERVER_RESPONSE_FAILED);
   1.856 +    return rv;
   1.857 +  }
   1.858 +
   1.859 +  if (status != 200) {
   1.860 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.861 +      SERVER_RESPONSE_FAILED);
   1.862 +    return NS_ERROR_NOT_AVAILABLE;
   1.863 +  }
   1.864 +
   1.865 +  std::string buf(mResponse.Data(), mResponse.Length());
   1.866 +  safe_browsing::ClientDownloadResponse response;
   1.867 +  if (!response.ParseFromString(buf)) {
   1.868 +    NS_WARNING("Could not parse protocol buffer");
   1.869 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.870 +                                   SERVER_RESPONSE_INVALID);
   1.871 +    return NS_ERROR_CANNOT_CONVERT_DATA;
   1.872 +  }
   1.873 +
   1.874 +  // There are several more verdicts, but we only respect one for now and treat
   1.875 +  // everything else as SAFE.
   1.876 +  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
   1.877 +    SERVER_RESPONSE_VALID);
   1.878 +  if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) {
   1.879 +    *aShouldBlock = true;
   1.880 +  }
   1.881 +
   1.882 +  return NS_OK;
   1.883 +}
   1.884 +
   1.885 +NS_IMPL_ISUPPORTS(ApplicationReputationService,
   1.886 +                  nsIApplicationReputationService)
   1.887 +
   1.888 +ApplicationReputationService*
   1.889 +  ApplicationReputationService::gApplicationReputationService = nullptr;
   1.890 +
   1.891 +ApplicationReputationService*
   1.892 +ApplicationReputationService::GetSingleton()
   1.893 +{
   1.894 +  if (gApplicationReputationService) {
   1.895 +    NS_ADDREF(gApplicationReputationService);
   1.896 +    return gApplicationReputationService;
   1.897 +  }
   1.898 +
   1.899 +  // We're not initialized yet.
   1.900 +  gApplicationReputationService = new ApplicationReputationService();
   1.901 +  if (gApplicationReputationService) {
   1.902 +    NS_ADDREF(gApplicationReputationService);
   1.903 +  }
   1.904 +
   1.905 +  return gApplicationReputationService;
   1.906 +}
   1.907 +
   1.908 +ApplicationReputationService::ApplicationReputationService()
   1.909 +{
   1.910 +#if defined(PR_LOGGING)
   1.911 +  if (!prlog) {
   1.912 +    prlog = PR_NewLogModule("ApplicationReputation");
   1.913 +  }
   1.914 +#endif
   1.915 +  LOG(("Application reputation service started up"));
   1.916 +}
   1.917 +
   1.918 +ApplicationReputationService::~ApplicationReputationService() {
   1.919 +  LOG(("Application reputation service shutting down"));
   1.920 +}
   1.921 +
   1.922 +NS_IMETHODIMP
   1.923 +ApplicationReputationService::QueryReputation(
   1.924 +    nsIApplicationReputationQuery* aQuery,
   1.925 +    nsIApplicationReputationCallback* aCallback) {
   1.926 +  NS_ENSURE_ARG_POINTER(aQuery);
   1.927 +  NS_ENSURE_ARG_POINTER(aCallback);
   1.928 +
   1.929 +  Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
   1.930 +  nsresult rv = QueryReputationInternal(aQuery, aCallback);
   1.931 +  if (NS_FAILED(rv)) {
   1.932 +    Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
   1.933 +      false);
   1.934 +    aCallback->OnComplete(false, rv);
   1.935 +  }
   1.936 +  return NS_OK;
   1.937 +}
   1.938 +
   1.939 +nsresult ApplicationReputationService::QueryReputationInternal(
   1.940 +  nsIApplicationReputationQuery* aQuery,
   1.941 +  nsIApplicationReputationCallback* aCallback) {
   1.942 +  nsresult rv;
   1.943 +  // If malware checks aren't enabled, don't query application reputation.
   1.944 +  if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
   1.945 +    return NS_ERROR_NOT_AVAILABLE;
   1.946 +  }
   1.947 +
   1.948 +  // If there is no service URL for querying application reputation, abort.
   1.949 +  nsCString serviceUrl;
   1.950 +  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
   1.951 +                    NS_ERROR_NOT_AVAILABLE);
   1.952 +  if (serviceUrl.EqualsLiteral("")) {
   1.953 +    return NS_ERROR_NOT_AVAILABLE;
   1.954 +  }
   1.955 +
   1.956 +  nsCOMPtr<nsIURI> uri;
   1.957 +  rv = aQuery->GetSourceURI(getter_AddRefs(uri));
   1.958 +  NS_ENSURE_SUCCESS(rv, rv);
   1.959 +  // Bail if the URI hasn't been set.
   1.960 +  NS_ENSURE_STATE(uri);
   1.961 +
   1.962 +  // Create a new pending lookup and start the call chain.
   1.963 +  nsRefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
   1.964 +  NS_ENSURE_STATE(lookup);
   1.965 +
   1.966 +  return lookup->StartLookup();
   1.967 +}

mercurial