LCOV - code coverage report
Current view: top level - toolkit/components/downloads - ApplicationReputation.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 587 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 45 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : // See
       7             : // https://wiki.mozilla.org/Security/Features/Application_Reputation_Design_Doc
       8             : // for a description of Chrome's implementation of this feature.
       9             : #include "ApplicationReputation.h"
      10             : #include "chrome/common/safe_browsing/csd.pb.h"
      11             : 
      12             : #include "nsIArray.h"
      13             : #include "nsIApplicationReputation.h"
      14             : #include "nsIChannel.h"
      15             : #include "nsIHttpChannel.h"
      16             : #include "nsIIOService.h"
      17             : #include "nsIPrefService.h"
      18             : #include "nsISimpleEnumerator.h"
      19             : #include "nsIStreamListener.h"
      20             : #include "nsIStringStream.h"
      21             : #include "nsITimer.h"
      22             : #include "nsIUploadChannel2.h"
      23             : #include "nsIURI.h"
      24             : #include "nsIURL.h"
      25             : #include "nsIUrlClassifierDBService.h"
      26             : #include "nsIX509Cert.h"
      27             : #include "nsIX509CertDB.h"
      28             : #include "nsIX509CertList.h"
      29             : 
      30             : #include "mozilla/ArrayUtils.h"
      31             : #include "mozilla/BasePrincipal.h"
      32             : #include "mozilla/ErrorNames.h"
      33             : #include "mozilla/LoadContext.h"
      34             : #include "mozilla/Preferences.h"
      35             : #include "mozilla/Services.h"
      36             : #include "mozilla/SizePrintfMacros.h"
      37             : #include "mozilla/Telemetry.h"
      38             : #include "mozilla/TimeStamp.h"
      39             : #include "mozilla/intl/LocaleService.h"
      40             : 
      41             : #include "nsAutoPtr.h"
      42             : #include "nsCOMPtr.h"
      43             : #include "nsDebug.h"
      44             : #include "nsDependentSubstring.h"
      45             : #include "nsError.h"
      46             : #include "nsNetCID.h"
      47             : #include "nsReadableUtils.h"
      48             : #include "nsServiceManagerUtils.h"
      49             : #include "nsString.h"
      50             : #include "nsTArray.h"
      51             : #include "nsThreadUtils.h"
      52             : 
      53             : #include "nsIContentPolicy.h"
      54             : #include "nsICryptoHash.h"
      55             : #include "nsILoadInfo.h"
      56             : #include "nsContentUtils.h"
      57             : #include "nsWeakReference.h"
      58             : #include "nsIRedirectHistoryEntry.h"
      59             : 
      60             : using mozilla::ArrayLength;
      61             : using mozilla::BasePrincipal;
      62             : using mozilla::OriginAttributes;
      63             : using mozilla::Preferences;
      64             : using mozilla::TimeStamp;
      65             : using mozilla::Telemetry::Accumulate;
      66             : using mozilla::intl::LocaleService;
      67             : using safe_browsing::ClientDownloadRequest;
      68             : using safe_browsing::ClientDownloadRequest_CertificateChain;
      69             : using safe_browsing::ClientDownloadRequest_Resource;
      70             : using safe_browsing::ClientDownloadRequest_SignatureInfo;
      71             : 
      72             : // Preferences that we need to initialize the query.
      73             : #define PREF_SB_APP_REP_URL "browser.safebrowsing.downloads.remote.url"
      74             : #define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
      75             : #define PREF_SB_DOWNLOADS_ENABLED "browser.safebrowsing.downloads.enabled"
      76             : #define PREF_SB_DOWNLOADS_REMOTE_ENABLED "browser.safebrowsing.downloads.remote.enabled"
      77             : #define PREF_SB_DOWNLOADS_REMOTE_TIMEOUT "browser.safebrowsing.downloads.remote.timeout_ms"
      78             : #define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.downloadBlockTable"
      79             : #define PREF_DOWNLOAD_ALLOW_TABLE "urlclassifier.downloadAllowTable"
      80             : 
      81             : // Preferences that are needed to action the verdict.
      82             : #define PREF_BLOCK_DANGEROUS            "browser.safebrowsing.downloads.remote.block_dangerous"
      83             : #define PREF_BLOCK_DANGEROUS_HOST       "browser.safebrowsing.downloads.remote.block_dangerous_host"
      84             : #define PREF_BLOCK_POTENTIALLY_UNWANTED "browser.safebrowsing.downloads.remote.block_potentially_unwanted"
      85             : #define PREF_BLOCK_UNCOMMON             "browser.safebrowsing.downloads.remote.block_uncommon"
      86             : 
      87             : // MOZ_LOG=ApplicationReputation:5
      88             : mozilla::LazyLogModule ApplicationReputationService::prlog("ApplicationReputation");
      89             : #define LOG(args) MOZ_LOG(ApplicationReputationService::prlog, mozilla::LogLevel::Debug, args)
      90             : #define LOG_ENABLED() MOZ_LOG_TEST(ApplicationReputationService::prlog, mozilla::LogLevel::Debug)
      91             : 
      92             : class PendingDBLookup;
      93             : 
      94             : // A single use class private to ApplicationReputationService encapsulating an
      95             : // nsIApplicationReputationQuery and an nsIApplicationReputationCallback. Once
      96             : // created by ApplicationReputationService, it is guaranteed to call mCallback.
      97             : // This class is private to ApplicationReputationService.
      98             : class PendingLookup final : public nsIStreamListener,
      99             :                             public nsITimerCallback,
     100             :                             public nsIObserver,
     101             :                             public nsSupportsWeakReference
     102             : {
     103             : public:
     104             :   NS_DECL_ISUPPORTS
     105             :   NS_DECL_NSIREQUESTOBSERVER
     106             :   NS_DECL_NSISTREAMLISTENER
     107             :   NS_DECL_NSITIMERCALLBACK
     108             :   NS_DECL_NSIOBSERVER
     109             : 
     110             :   // Constructor and destructor.
     111             :   PendingLookup(nsIApplicationReputationQuery* aQuery,
     112             :                 nsIApplicationReputationCallback* aCallback);
     113             : 
     114             :   // Start the lookup. The lookup may have 2 parts: local and remote. In the
     115             :   // local lookup, PendingDBLookups are created to query the local allow and
     116             :   // blocklists for various URIs associated with this downloaded file. In the
     117             :   // event that no results are found, a remote lookup is sent to the Application
     118             :   // Reputation server.
     119             :   nsresult StartLookup();
     120             : 
     121             : private:
     122             :   ~PendingLookup();
     123             : 
     124             :   friend class PendingDBLookup;
     125             : 
     126             :   // Telemetry states.
     127             :   // Status of the remote response (valid or not).
     128             :   enum SERVER_RESPONSE_TYPES {
     129             :     SERVER_RESPONSE_VALID = 0,
     130             :     SERVER_RESPONSE_FAILED = 1,
     131             :     SERVER_RESPONSE_INVALID = 2,
     132             :   };
     133             : 
     134             :   // Number of blocklist and allowlist hits we have seen.
     135             :   uint32_t mBlocklistCount;
     136             :   uint32_t mAllowlistCount;
     137             : 
     138             :   // The query containing metadata about the downloaded file.
     139             :   nsCOMPtr<nsIApplicationReputationQuery> mQuery;
     140             : 
     141             :   // The callback with which to report the verdict.
     142             :   nsCOMPtr<nsIApplicationReputationCallback> mCallback;
     143             : 
     144             :   // An array of strings created from certificate information used to whitelist
     145             :   // the downloaded file.
     146             :   nsTArray<nsCString> mAllowlistSpecs;
     147             :   // The source URI of the download, the referrer and possibly any redirects.
     148             :   nsTArray<nsCString> mAnylistSpecs;
     149             : 
     150             :   // When we started this query
     151             :   TimeStamp mStartTime;
     152             : 
     153             :   // The channel used to talk to the remote lookup server
     154             :   nsCOMPtr<nsIChannel> mChannel;
     155             : 
     156             :   // Timer to abort this lookup if it takes too long
     157             :   nsCOMPtr<nsITimer> mTimeoutTimer;
     158             : 
     159             :   // A protocol buffer for storing things we need in the remote request. We
     160             :   // store the resource chain (redirect information) as well as signature
     161             :   // information extracted using the Windows Authenticode API, if the binary is
     162             :   // signed.
     163             :   ClientDownloadRequest mRequest;
     164             : 
     165             :   // The response from the application reputation query. This is read in chunks
     166             :   // as part of our nsIStreamListener implementation and may contain embedded
     167             :   // NULLs.
     168             :   nsCString mResponse;
     169             : 
     170             :   // Returns true if the file is likely to be binary.
     171             :   bool IsBinaryFile();
     172             : 
     173             :   // Returns the type of download binary for the file.
     174             :   ClientDownloadRequest::DownloadType GetDownloadType(const nsAString& aFilename);
     175             : 
     176             :   // Clean up and call the callback. PendingLookup must not be used after this
     177             :   // function is called.
     178             :   nsresult OnComplete(bool shouldBlock, nsresult rv,
     179             :     uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE);
     180             : 
     181             :   // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
     182             :   // guarantee calling the callback
     183             :   nsresult OnStopRequestInternal(nsIRequest *aRequest,
     184             :                                  nsISupports *aContext,
     185             :                                  nsresult aResult,
     186             :                                  bool* aShouldBlock,
     187             :                                  uint32_t* aVerdict);
     188             : 
     189             :   // Return the hex-encoded hash of the whole URI.
     190             :   nsresult GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash);
     191             : 
     192             :   // Strip url parameters, fragments, and user@pass fields from the URI spec
     193             :   // using nsIURL. Hash data URIs and return blob URIs unfiltered.
     194             :   nsresult GetStrippedSpec(nsIURI* aUri, nsACString& spec);
     195             : 
     196             :   // Escape '/' and '%' in certificate attribute values.
     197             :   nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
     198             : 
     199             :   // Escape ':' in fingerprint values.
     200             :   nsCString EscapeFingerprint(const nsACString& aAttribute);
     201             : 
     202             :   // Generate whitelist strings for the given certificate pair from the same
     203             :   // certificate chain.
     204             :   nsresult GenerateWhitelistStringsForPair(
     205             :     nsIX509Cert* certificate, nsIX509Cert* issuer);
     206             : 
     207             :   // Generate whitelist strings for the given certificate chain, which starts
     208             :   // with the signer and may go all the way to the root cert.
     209             :   nsresult GenerateWhitelistStringsForChain(
     210             :     const ClientDownloadRequest_CertificateChain& aChain);
     211             : 
     212             :   // For signed binaries, generate strings of the form:
     213             :   // http://sb-ssl.google.com/safebrowsing/csd/certificate/
     214             :   //   <issuer_cert_sha1_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
     215             :   // for each (cert, issuer) pair in each chain of certificates that is
     216             :   // associated with the binary.
     217             :   nsresult GenerateWhitelistStrings();
     218             : 
     219             :   // Parse the XPCOM certificate lists and stick them into the protocol buffer
     220             :   // version.
     221             :   nsresult ParseCertificates(nsIArray* aSigArray);
     222             : 
     223             :   // Adds the redirects to mAnylistSpecs to be looked up.
     224             :   nsresult AddRedirects(nsIArray* aRedirects);
     225             : 
     226             :   // Helper function to ensure that we call PendingLookup::LookupNext or
     227             :   // PendingLookup::OnComplete.
     228             :   nsresult DoLookupInternal();
     229             : 
     230             :   // Looks up all the URIs that may be responsible for allowlisting or
     231             :   // blocklisting the downloaded file. These URIs may include whitelist strings
     232             :   // generated by certificates verifying the binary as well as the target URI
     233             :   // from which the file was downloaded.
     234             :   nsresult LookupNext();
     235             : 
     236             :   // Sends a query to the remote application reputation service. Returns NS_OK
     237             :   // on success.
     238             :   nsresult SendRemoteQuery();
     239             : 
     240             :   // Helper function to ensure that we always call the callback.
     241             :   nsresult SendRemoteQueryInternal();
     242             : };
     243             : 
     244             : // A single-use class for looking up a single URI in the safebrowsing DB. This
     245             : // class is private to PendingLookup.
     246             : class PendingDBLookup final : public nsIUrlClassifierCallback
     247             : {
     248             : public:
     249             :   NS_DECL_ISUPPORTS
     250             :   NS_DECL_NSIURLCLASSIFIERCALLBACK
     251             : 
     252             :   // Constructor and destructor
     253             :   explicit PendingDBLookup(PendingLookup* aPendingLookup);
     254             : 
     255             :   // Look up the given URI in the safebrowsing DBs, optionally on both the allow
     256             :   // list and the blocklist. If there is a match, call
     257             :   // PendingLookup::OnComplete. Otherwise, call PendingLookup::LookupNext.
     258             :   nsresult LookupSpec(const nsACString& aSpec, bool aAllowlistOnly);
     259             : 
     260             : private:
     261             :   ~PendingDBLookup();
     262             : 
     263             :   // The download appeared on the allowlist, blocklist, or no list (and thus
     264             :   // could trigger a remote query.
     265             :   enum LIST_TYPES {
     266             :     ALLOW_LIST = 0,
     267             :     BLOCK_LIST = 1,
     268             :     NO_LIST = 2,
     269             :   };
     270             : 
     271             :   nsCString mSpec;
     272             :   bool mAllowlistOnly;
     273             :   RefPtr<PendingLookup> mPendingLookup;
     274             :   nsresult LookupSpecInternal(const nsACString& aSpec);
     275             : };
     276             : 
     277           0 : NS_IMPL_ISUPPORTS(PendingDBLookup,
     278             :                   nsIUrlClassifierCallback)
     279             : 
     280           0 : PendingDBLookup::PendingDBLookup(PendingLookup* aPendingLookup) :
     281             :   mAllowlistOnly(false),
     282           0 :   mPendingLookup(aPendingLookup)
     283             : {
     284           0 :   LOG(("Created pending DB lookup [this = %p]", this));
     285           0 : }
     286             : 
     287           0 : PendingDBLookup::~PendingDBLookup()
     288             : {
     289           0 :   LOG(("Destroying pending DB lookup [this = %p]", this));
     290           0 :   mPendingLookup = nullptr;
     291           0 : }
     292             : 
     293             : nsresult
     294           0 : PendingDBLookup::LookupSpec(const nsACString& aSpec,
     295             :                             bool aAllowlistOnly)
     296             : {
     297           0 :   LOG(("Checking principal %s [this=%p]", aSpec.Data(), this));
     298           0 :   mSpec = aSpec;
     299           0 :   mAllowlistOnly = aAllowlistOnly;
     300           0 :   nsresult rv = LookupSpecInternal(aSpec);
     301           0 :   if (NS_FAILED(rv)) {
     302           0 :     nsAutoCString errorName;
     303           0 :     mozilla::GetErrorName(rv, errorName);
     304           0 :     LOG(("Error in LookupSpecInternal() [rv = %s, this = %p]",
     305             :          errorName.get(), this));
     306           0 :     return mPendingLookup->LookupNext(); // ignore this lookup and move to next
     307             :   }
     308             :   // LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
     309             :   // guaranteed to call HandleEvent.
     310           0 :   return rv;
     311             : }
     312             : 
     313             : nsresult
     314           0 : PendingDBLookup::LookupSpecInternal(const nsACString& aSpec)
     315             : {
     316             :   nsresult rv;
     317             : 
     318           0 :   nsCOMPtr<nsIURI> uri;
     319           0 :   nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     320           0 :   rv = ios->NewURI(aSpec, nullptr, nullptr, getter_AddRefs(uri));
     321           0 :   NS_ENSURE_SUCCESS(rv, rv);
     322             : 
     323           0 :   OriginAttributes attrs;
     324             :   nsCOMPtr<nsIPrincipal> principal =
     325           0 :     BasePrincipal::CreateCodebasePrincipal(uri, attrs);
     326           0 :   if (!principal) {
     327           0 :     return NS_ERROR_FAILURE;
     328             :   }
     329             : 
     330             :   // Check local lists to see if the URI has already been whitelisted or
     331             :   // blacklisted.
     332           0 :   LOG(("Checking DB service for principal %s [this = %p]", mSpec.get(), this));
     333             :   nsCOMPtr<nsIUrlClassifierDBService> dbService =
     334           0 :     do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
     335           0 :   NS_ENSURE_SUCCESS(rv, rv);
     336             : 
     337           0 :   nsAutoCString tables;
     338           0 :   nsAutoCString allowlist;
     339           0 :   Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowlist);
     340           0 :   if (!allowlist.IsEmpty()) {
     341           0 :     tables.Append(allowlist);
     342             :   }
     343           0 :   nsAutoCString blocklist;
     344           0 :   Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blocklist);
     345           0 :   if (!mAllowlistOnly && !blocklist.IsEmpty()) {
     346           0 :     tables.Append(',');
     347           0 :     tables.Append(blocklist);
     348             :   }
     349           0 :   return dbService->Lookup(principal, tables, this);
     350             : }
     351             : 
     352             : NS_IMETHODIMP
     353           0 : PendingDBLookup::HandleEvent(const nsACString& tables)
     354             : {
     355             :   // HandleEvent is guaranteed to call either:
     356             :   // 1) PendingLookup::OnComplete if the URL matches the blocklist, or
     357             :   // 2) PendingLookup::LookupNext if the URL does not match the blocklist.
     358             :   // Blocklisting trumps allowlisting.
     359           0 :   nsAutoCString blockList;
     360           0 :   Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
     361           0 :   if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
     362           0 :     mPendingLookup->mBlocklistCount++;
     363           0 :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
     364           0 :     LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
     365           0 :     return mPendingLookup->OnComplete(true, NS_OK,
     366           0 :       nsIApplicationReputationService::VERDICT_DANGEROUS);
     367             :   }
     368             : 
     369           0 :   nsAutoCString allowList;
     370           0 :   Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
     371           0 :   if (FindInReadable(allowList, tables)) {
     372           0 :     mPendingLookup->mAllowlistCount++;
     373           0 :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
     374           0 :     LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
     375             :     // Don't call onComplete, since blocklisting trumps allowlisting
     376             :   } else {
     377           0 :     LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(),
     378             :          this));
     379           0 :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
     380             :   }
     381           0 :   return mPendingLookup->LookupNext();
     382             : }
     383             : 
     384           0 : NS_IMPL_ISUPPORTS(PendingLookup,
     385             :                   nsIStreamListener,
     386             :                   nsIRequestObserver,
     387             :                   nsIObserver,
     388             :                   nsISupportsWeakReference)
     389             : 
     390           0 : PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
     391           0 :                              nsIApplicationReputationCallback* aCallback) :
     392             :   mBlocklistCount(0),
     393             :   mAllowlistCount(0),
     394             :   mQuery(aQuery),
     395           0 :   mCallback(aCallback)
     396             : {
     397           0 :   LOG(("Created pending lookup [this = %p]", this));
     398           0 : }
     399             : 
     400           0 : PendingLookup::~PendingLookup()
     401             : {
     402           0 :   LOG(("Destroying pending lookup [this = %p]", this));
     403           0 : }
     404             : 
     405             : static const char16_t* const kBinaryFileExtensions[] = {
     406             :     // Extracted from the "File Type Policies" Chrome extension
     407             :     //u".001",
     408             :     //u".7z",
     409             :     //u".ace",
     410             :     //u".action", // Mac script
     411             :     //u".ad", // Windows
     412             :     u".ade", // MS Access
     413             :     u".adp", // MS Access
     414             :     u".apk", // Android package
     415             :     u".app", // Executable application
     416             :     u".application", // MS ClickOnce
     417             :     u".appref-ms", // MS ClickOnce
     418             :     //u".arc",
     419             :     //u".arj",
     420             :     u".as", // Mac archive
     421             :     u".asp", // Windows Server script
     422             :     u".asx", // Windows Media Player
     423             :     //u".b64",
     424             :     //u".balz",
     425             :     u".bas", // Basic script
     426             :     u".bash", // Linux shell
     427             :     u".bat", // Windows shell
     428             :     //u".bhx",
     429             :     //u".bin",
     430             :     u".bz", // Linux archive (bzip)
     431             :     u".bz2", // Linux archive (bzip2)
     432             :     u".bzip2", // Linux archive (bzip2)
     433             :     u".cab", // Windows archive
     434             :     u".cdr", // Mac disk image
     435             :     u".cfg", // Windows
     436             :     u".chi", // Windows Help
     437             :     u".chm", // Windows Help
     438             :     u".class", // Java
     439             :     u".cmd", // Windows executable
     440             :     u".com", // Windows executable
     441             :     u".command", // Mac script
     442             :     u".cpgz", // Mac archive
     443             :     //u".cpio",
     444             :     u".cpl", // Windows executable
     445             :     u".crt", // Windows signed certificate
     446             :     u".crx", // Chrome extensions
     447             :     u".csh", // Linux shell
     448             :     u".dart", // Mac disk image
     449             :     u".dc42", // Apple DiskCopy Image
     450             :     u".deb", // Linux package
     451             :     u".dex", // Android
     452             :     u".diskcopy42", // Apple DiskCopy Image
     453             :     u".dll", // Windows executable
     454             :     u".dmg", // Mac disk image
     455             :     u".dmgpart", // Mac disk image
     456             :     //u".docb", // MS Office
     457             :     //u".docm", // MS Word
     458             :     //u".docx", // MS Word
     459             :     //u".dotm", // MS Word
     460             :     //u".dott", // MS Office
     461             :     u".drv", // Windows driver
     462             :     u".dvdr", // Mac Disk image
     463             :     u".efi", // Firmware
     464             :     u".eml", // MS Outlook
     465             :     u".exe", // Windows executable
     466             :     //u".fat",
     467             :     u".fon", // Windows font
     468             :     u".fxp", // MS FoxPro
     469             :     u".gadget", // Windows
     470             :     u".grp", // Windows
     471             :     u".gz", // Linux archive (gzip)
     472             :     u".gzip", // Linux archive (gzip)
     473             :     u".hfs", // Mac disk image
     474             :     u".hlp", // Windows Help
     475             :     u".hqx", // Mac archive
     476             :     u".hta", // HTML trusted application
     477             :     u".htm",
     478             :     u".html",
     479             :     u".htt", // MS HTML template
     480             :     u".img", // Mac disk image
     481             :     u".imgpart", // Mac disk image
     482             :     u".inf", // Windows installer
     483             :     u".ini", // Generic config file
     484             :     u".ins", // IIS config
     485             :     //u".inx", // InstallShield
     486             :     u".iso", // CD image
     487             :     u".isp", // IIS config
     488             :     //u".isu", // InstallShield
     489             :     u".jar", // Java
     490             :     u".jnlp", // Java
     491             :     //u".job", // Windows
     492             :     u".js", // JavaScript script
     493             :     u".jse", // JScript
     494             :     u".ksh", // Linux shell
     495             :     //u".lha",
     496             :     u".lnk", // Windows
     497             :     u".local", // Windows
     498             :     //u".lpaq1",
     499             :     //u".lpaq5",
     500             :     //u".lpaq8",
     501             :     //u".lzh",
     502             :     //u".lzma",
     503             :     u".mad", // MS Access
     504             :     u".maf", // MS Access
     505             :     u".mag", // MS Access
     506             :     u".mam", // MS Access
     507             :     u".manifest", // Windows
     508             :     u".maq", // MS Access
     509             :     u".mar", // MS Access
     510             :     u".mas", // MS Access
     511             :     u".mat", // MS Access
     512             :     u".mau", // Media attachment
     513             :     u".mav", // MS Access
     514             :     u".maw", // MS Access
     515             :     u".mda", // MS Access
     516             :     u".mdb", // MS Access
     517             :     u".mde", // MS Access
     518             :     u".mdt", // MS Access
     519             :     u".mdw", // MS Access
     520             :     u".mdz", // MS Access
     521             :     u".mht", // MS HTML
     522             :     u".mhtml", // MS HTML
     523             :     u".mim", // MS Mail
     524             :     u".mmc", // MS Office
     525             :     u".mof", // Windows
     526             :     u".mpkg", // Mac installer
     527             :     u".msc", // Windows executable
     528             :     u".msg", // MS Outlook
     529             :     u".msh", // Windows shell
     530             :     u".msh1", // Windows shell
     531             :     u".msh1xml", // Windows shell
     532             :     u".msh2", // Windows shell
     533             :     u".msh2xml", // Windows shell
     534             :     u".mshxml", // Windows
     535             :     u".msi", // Windows installer
     536             :     u".msp", // Windows installer
     537             :     u".mst", // Windows installer
     538             :     u".ndif", // Mac disk image
     539             :     //u".ntfs", // 7z
     540             :     u".ocx", // ActiveX
     541             :     u".ops", // MS Office
     542             :     //u".out", // Linux binary
     543             :     //u".paf", // PortableApps package
     544             :     //u".paq8f",
     545             :     //u".paq8jd",
     546             :     //u".paq8l",
     547             :     //u".paq8o",
     548             :     u".partial", // Downloads
     549             :     u".pax", // Mac archive
     550             :     u".pcd", // Microsoft Visual Test
     551             :     u".pdf", // Adobe Acrobat
     552             :     //u".pea",
     553             :     u".pet", // Linux package
     554             :     u".pif", // Windows
     555             :     u".pkg", // Mac installer
     556             :     u".pl", // Perl script
     557             :     u".plg", // MS Visual Studio
     558             :     //u".potx", // MS PowerPoint
     559             :     //u".ppam", // MS PowerPoint
     560             :     //u".ppsx", // MS PowerPoint
     561             :     //u".pptm", // MS PowerPoint
     562             :     //u".pptx", // MS PowerPoint
     563             :     u".prf", // MS Outlook
     564             :     u".prg", // Windows
     565             :     u".ps1", // Windows shell
     566             :     u".ps1xml", // Windows shell
     567             :     u".ps2", // Windows shell
     568             :     u".ps2xml", // Windows shell
     569             :     u".psc1", // Windows shell
     570             :     u".psc2", // Windows shell
     571             :     u".pst", // MS Outlook
     572             :     u".pup", // Linux package
     573             :     u".py", // Python script
     574             :     u".pyc", // Python binary
     575             :     u".pyw", // Python GUI
     576             :     //u".quad",
     577             :     //u".r00",
     578             :     //u".r01",
     579             :     //u".r02",
     580             :     //u".r03",
     581             :     //u".r04",
     582             :     //u".r05",
     583             :     //u".r06",
     584             :     //u".r07",
     585             :     //u".r08",
     586             :     //u".r09",
     587             :     //u".r10",
     588             :     //u".r11",
     589             :     //u".r12",
     590             :     //u".r13",
     591             :     //u".r14",
     592             :     //u".r15",
     593             :     //u".r16",
     594             :     //u".r17",
     595             :     //u".r18",
     596             :     //u".r19",
     597             :     //u".r20",
     598             :     //u".r21",
     599             :     //u".r22",
     600             :     //u".r23",
     601             :     //u".r24",
     602             :     //u".r25",
     603             :     //u".r26",
     604             :     //u".r27",
     605             :     //u".r28",
     606             :     //u".r29",
     607             :     //u".rar",
     608             :     u".rb", // Ruby script
     609             :     u".reg", // Windows Registry
     610             :     u".rels", // MS Office
     611             :     //u".rgs", // Windows Registry
     612             :     u".rpm", // Linux package
     613             :     //u".rtf", // MS Office
     614             :     //u".run", // Linux shell
     615             :     u".scf", // Windows shell
     616             :     u".scr", // Windows
     617             :     u".sct", // Windows shell
     618             :     u".search-ms", // Windows
     619             :     u".sh", // Linux shell
     620             :     u".shar", // Linux shell
     621             :     u".shb", // Windows
     622             :     u".shs", // Windows shell
     623             :     //u".sldm", // MS PowerPoint
     624             :     //u".sldx", // MS PowerPoint
     625             :     u".slp", // Linux package
     626             :     u".smi", // Mac disk image
     627             :     u".sparsebundle", // Mac disk image
     628             :     u".sparseimage", // Mac disk image
     629             :     u".spl", // Adobe Flash
     630             :     //u".squashfs",
     631             :     u".svg",
     632             :     u".swf", // Adobe Flash
     633             :     u".swm", // Windows Imaging
     634             :     u".sys", // Windows
     635             :     u".tar", // Linux archive
     636             :     u".taz", // Linux archive (bzip2)
     637             :     u".tbz", // Linux archive (bzip2)
     638             :     u".tbz2", // Linux archive (bzip2)
     639             :     u".tcsh", // Linux shell
     640             :     u".tgz", // Linux archive (gzip)
     641             :     //u".toast", // Roxio disk image
     642             :     //u".torrent", // Bittorrent
     643             :     u".tpz", // Linux archive (gzip)
     644             :     u".txz", // Linux archive (xz)
     645             :     u".tz", // Linux archive (gzip)
     646             :     //u".u3p", // U3 Smart Apps
     647             :     u".udf", // MS Excel
     648             :     u".udif", // Mac disk image
     649             :     u".url", // Windows
     650             :     //u".uu",
     651             :     //u".uue",
     652             :     u".vb", // Visual Basic script
     653             :     u".vbe", // Visual Basic script
     654             :     u".vbs", // Visual Basic script
     655             :     //u".vbscript", // Visual Basic script
     656             :     u".vhd", // Windows virtual hard drive
     657             :     u".vhdx", // Windows virtual hard drive
     658             :     u".vmdk", // VMware virtual disk
     659             :     u".vsd", // MS Visio
     660             :     u".vsmacros", // MS Visual Studio
     661             :     u".vss", // MS Visio
     662             :     u".vst", // MS Visio
     663             :     u".vsw", // MS Visio
     664             :     u".website",  // Windows
     665             :     u".wim", // Windows Imaging
     666             :     //u".workflow", // Mac Automator
     667             :     //u".wrc", // FreeArc archive
     668             :     u".ws", // Windows script
     669             :     u".wsc", // Windows script
     670             :     u".wsf", // Windows script
     671             :     u".wsh", // Windows script
     672             :     u".xar", // MS Excel
     673             :     u".xbap", // XAML Browser Application
     674             :     u".xip", // Mac archive
     675             :     //u".xlsm", // MS Excel
     676             :     //u".xlsx", // MS Excel
     677             :     //u".xltm", // MS Excel
     678             :     //u".xltx", // MS Excel
     679             :     u".xml",
     680             :     u".xnk", // MS Exchange
     681             :     u".xrm-ms", // Windows
     682             :     u".xsl", // XML Stylesheet
     683             :     //u".xxe",
     684             :     u".xz", // Linux archive (xz)
     685             :     u".z", // InstallShield
     686             : #ifdef XP_WIN // disable on Mac/Linux, see 1167493
     687             :     u".zip", // Generic archive
     688             : #endif
     689             :     u".zipx", // WinZip
     690             :     //u".zpaq",
     691             : };
     692             : 
     693             : bool
     694           0 : PendingLookup::IsBinaryFile()
     695             : {
     696           0 :   nsString fileName;
     697           0 :   nsresult rv = mQuery->GetSuggestedFileName(fileName);
     698           0 :   if (NS_FAILED(rv)) {
     699           0 :     LOG(("No suggested filename [this = %p]", this));
     700           0 :     return false;
     701             :   }
     702           0 :   LOG(("Suggested filename: %s [this = %p]",
     703             :        NS_ConvertUTF16toUTF8(fileName).get(), this));
     704             : 
     705           0 :   for (size_t i = 0; i < ArrayLength(kBinaryFileExtensions); ++i) {
     706           0 :     if (StringEndsWith(fileName, nsDependentString(kBinaryFileExtensions[i]))) {
     707           0 :       return true;
     708             :     }
     709             :   }
     710             : 
     711           0 :   return false;
     712             : }
     713             : 
     714             : ClientDownloadRequest::DownloadType
     715           0 : PendingLookup::GetDownloadType(const nsAString& aFilename) {
     716           0 :   MOZ_ASSERT(IsBinaryFile());
     717             : 
     718             :   // From https://cs.chromium.org/chromium/src/chrome/common/safe_browsing/download_protection_util.cc?l=17
     719           0 :   if (StringEndsWith(aFilename, NS_LITERAL_STRING(".zip"))) {
     720           0 :     return ClientDownloadRequest::ZIPPED_EXECUTABLE;
     721           0 :   } else if (StringEndsWith(aFilename, NS_LITERAL_STRING(".apk"))) {
     722           0 :     return ClientDownloadRequest::ANDROID_APK;
     723           0 :   } else if (StringEndsWith(aFilename, NS_LITERAL_STRING(".app")) ||
     724           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".cdr")) ||
     725           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".dart")) ||
     726           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".dc42")) ||
     727           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".diskcopy42")) ||
     728           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".dmg")) ||
     729           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".dmgpart")) ||
     730           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".dvdr")) ||
     731           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".img")) ||
     732           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".imgpart")) ||
     733           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".iso")) ||
     734           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".mpkg")) ||
     735           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".ndif")) ||
     736           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".pkg")) ||
     737           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".smi")) ||
     738           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".sparsebundle")) ||
     739           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".sparseimage")) ||
     740           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".toast")) ||
     741           0 :              StringEndsWith(aFilename, NS_LITERAL_STRING(".udif"))) {
     742           0 :     return ClientDownloadRequest::MAC_EXECUTABLE;
     743             :   }
     744             : 
     745           0 :   return ClientDownloadRequest::WIN_EXECUTABLE; // default to Windows binaries
     746             : }
     747             : 
     748             : nsresult
     749           0 : PendingLookup::LookupNext()
     750             : {
     751             :   // We must call LookupNext or SendRemoteQuery upon return.
     752             :   // Look up all of the URLs that could allow or block this download.
     753             :   // Blocklist first.
     754           0 :   if (mBlocklistCount > 0) {
     755             :     return OnComplete(true, NS_OK,
     756           0 :                       nsIApplicationReputationService::VERDICT_DANGEROUS);
     757             :   }
     758           0 :   int index = mAnylistSpecs.Length() - 1;
     759           0 :   nsCString spec;
     760           0 :   if (index >= 0) {
     761             :     // Check the source URI, referrer and redirect chain.
     762           0 :     spec = mAnylistSpecs[index];
     763           0 :     mAnylistSpecs.RemoveElementAt(index);
     764           0 :     RefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
     765           0 :     return lookup->LookupSpec(spec, false);
     766             :   }
     767             :   // If any of mAnylistSpecs matched the blocklist, go ahead and block.
     768           0 :   if (mBlocklistCount > 0) {
     769             :     return OnComplete(true, NS_OK,
     770           0 :                       nsIApplicationReputationService::VERDICT_DANGEROUS);
     771             :   }
     772             :   // If any of mAnylistSpecs matched the allowlist, go ahead and pass.
     773           0 :   if (mAllowlistCount > 0) {
     774           0 :     return OnComplete(false, NS_OK);
     775             :   }
     776             :   // Only binary signatures remain.
     777           0 :   index = mAllowlistSpecs.Length() - 1;
     778           0 :   if (index >= 0) {
     779           0 :     spec = mAllowlistSpecs[index];
     780           0 :     LOG(("PendingLookup::LookupNext: checking %s on allowlist", spec.get()));
     781           0 :     mAllowlistSpecs.RemoveElementAt(index);
     782           0 :     RefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
     783           0 :     return lookup->LookupSpec(spec, true);
     784             :   }
     785             :   // There are no more URIs to check against local list. If the file is
     786             :   // not eligible for remote lookup, bail.
     787           0 :   if (!IsBinaryFile()) {
     788           0 :     LOG(("Not eligible for remote lookups [this=%p]", this));
     789           0 :     return OnComplete(false, NS_OK);
     790             :   }
     791           0 :   nsresult rv = SendRemoteQuery();
     792           0 :   if (NS_FAILED(rv)) {
     793           0 :     return OnComplete(false, rv);
     794             :   }
     795           0 :   return NS_OK;
     796             : }
     797             : 
     798             : nsCString
     799           0 : PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
     800             : {
     801             :   // Escape '/' because it's a field separator, and '%' because Chrome does
     802           0 :   nsCString escaped;
     803           0 :   escaped.SetCapacity(aAttribute.Length());
     804           0 :   for (unsigned int i = 0; i < aAttribute.Length(); ++i) {
     805           0 :     if (aAttribute.Data()[i] == '%') {
     806           0 :       escaped.AppendLiteral("%25");
     807           0 :     } else if (aAttribute.Data()[i] == '/') {
     808           0 :       escaped.AppendLiteral("%2F");
     809           0 :     } else if (aAttribute.Data()[i] == ' ') {
     810           0 :       escaped.AppendLiteral("%20");
     811             :     } else {
     812           0 :       escaped.Append(aAttribute.Data()[i]);
     813             :     }
     814             :   }
     815           0 :   return escaped;
     816             : }
     817             : 
     818             : nsCString
     819           0 : PendingLookup::EscapeFingerprint(const nsACString& aFingerprint)
     820             : {
     821             :   // Google's fingerprint doesn't have colons
     822           0 :   nsCString escaped;
     823           0 :   escaped.SetCapacity(aFingerprint.Length());
     824           0 :   for (unsigned int i = 0; i < aFingerprint.Length(); ++i) {
     825           0 :     if (aFingerprint.Data()[i] != ':') {
     826           0 :       escaped.Append(aFingerprint.Data()[i]);
     827             :     }
     828             :   }
     829           0 :   return escaped;
     830             : }
     831             : 
     832             : nsresult
     833           0 : PendingLookup::GenerateWhitelistStringsForPair(
     834             :   nsIX509Cert* certificate,
     835             :   nsIX509Cert* issuer)
     836             : {
     837             :   // The whitelist paths have format:
     838             :   // http://sb-ssl.google.com/safebrowsing/csd/certificate/<issuer_cert_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
     839             :   // Any of CN, O, or OU may be omitted from the whitelist entry. Unfortunately
     840             :   // this is not publicly documented, but the Chrome implementation can be found
     841             :   // here:
     842             :   // https://code.google.com/p/chromium/codesearch#search/&q=GetCertificateWhitelistStrings
     843             :   nsCString whitelistString(
     844           0 :     "http://sb-ssl.google.com/safebrowsing/csd/certificate/");
     845             : 
     846           0 :   nsString fingerprint;
     847           0 :   nsresult rv = issuer->GetSha1Fingerprint(fingerprint);
     848           0 :   NS_ENSURE_SUCCESS(rv, rv);
     849             :   whitelistString.Append(
     850           0 :     EscapeFingerprint(NS_ConvertUTF16toUTF8(fingerprint)));
     851             : 
     852           0 :   nsString commonName;
     853           0 :   rv = certificate->GetCommonName(commonName);
     854           0 :   NS_ENSURE_SUCCESS(rv, rv);
     855           0 :   if (!commonName.IsEmpty()) {
     856           0 :     whitelistString.AppendLiteral("/CN=");
     857             :     whitelistString.Append(
     858           0 :       EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(commonName)));
     859             :   }
     860             : 
     861           0 :   nsString organization;
     862           0 :   rv = certificate->GetOrganization(organization);
     863           0 :   NS_ENSURE_SUCCESS(rv, rv);
     864           0 :   if (!organization.IsEmpty()) {
     865           0 :     whitelistString.AppendLiteral("/O=");
     866             :     whitelistString.Append(
     867           0 :       EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organization)));
     868             :   }
     869             : 
     870           0 :   nsString organizationalUnit;
     871           0 :   rv = certificate->GetOrganizationalUnit(organizationalUnit);
     872           0 :   NS_ENSURE_SUCCESS(rv, rv);
     873           0 :   if (!organizationalUnit.IsEmpty()) {
     874           0 :     whitelistString.AppendLiteral("/OU=");
     875             :     whitelistString.Append(
     876           0 :       EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organizationalUnit)));
     877             :   }
     878           0 :   LOG(("Whitelisting %s", whitelistString.get()));
     879             : 
     880           0 :   mAllowlistSpecs.AppendElement(whitelistString);
     881           0 :   return NS_OK;
     882             : }
     883             : 
     884             : nsresult
     885           0 : PendingLookup::GenerateWhitelistStringsForChain(
     886             :   const safe_browsing::ClientDownloadRequest_CertificateChain& aChain)
     887             : {
     888             :   // We need a signing certificate and an issuer to construct a whitelist
     889             :   // entry.
     890           0 :   if (aChain.element_size() < 2) {
     891           0 :     return NS_OK;
     892             :   }
     893             : 
     894             :   // Get the signer.
     895             :   nsresult rv;
     896           0 :   nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
     897           0 :   NS_ENSURE_SUCCESS(rv, rv);
     898             : 
     899           0 :   nsCOMPtr<nsIX509Cert> signer;
     900             :   nsDependentCSubstring signerDER(
     901           0 :     const_cast<char *>(aChain.element(0).certificate().data()),
     902           0 :     aChain.element(0).certificate().size());
     903           0 :   rv = certDB->ConstructX509(signerDER, getter_AddRefs(signer));
     904           0 :   NS_ENSURE_SUCCESS(rv, rv);
     905             : 
     906           0 :   for (int i = 1; i < aChain.element_size(); ++i) {
     907             :     // Get the issuer.
     908           0 :     nsCOMPtr<nsIX509Cert> issuer;
     909             :     nsDependentCSubstring issuerDER(
     910           0 :       const_cast<char *>(aChain.element(i).certificate().data()),
     911           0 :       aChain.element(i).certificate().size());
     912           0 :     rv = certDB->ConstructX509(issuerDER, getter_AddRefs(issuer));
     913           0 :     NS_ENSURE_SUCCESS(rv, rv);
     914             : 
     915           0 :     rv = GenerateWhitelistStringsForPair(signer, issuer);
     916           0 :     NS_ENSURE_SUCCESS(rv, rv);
     917             :   }
     918           0 :   return NS_OK;
     919             : }
     920             : 
     921             : nsresult
     922           0 : PendingLookup::GenerateWhitelistStrings()
     923             : {
     924           0 :   for (int i = 0; i < mRequest.signature().certificate_chain_size(); ++i) {
     925           0 :     nsresult rv = GenerateWhitelistStringsForChain(
     926           0 :       mRequest.signature().certificate_chain(i));
     927           0 :     NS_ENSURE_SUCCESS(rv, rv);
     928             :   }
     929           0 :   return NS_OK;
     930             : }
     931             : 
     932             : nsresult
     933           0 : PendingLookup::AddRedirects(nsIArray* aRedirects)
     934             : {
     935           0 :   uint32_t length = 0;
     936           0 :   aRedirects->GetLength(&length);
     937           0 :   LOG(("ApplicationReputation: Got %u redirects", length));
     938           0 :   nsCOMPtr<nsISimpleEnumerator> iter;
     939           0 :   nsresult rv = aRedirects->Enumerate(getter_AddRefs(iter));
     940           0 :   NS_ENSURE_SUCCESS(rv, rv);
     941             : 
     942           0 :   bool hasMoreRedirects = false;
     943           0 :   rv = iter->HasMoreElements(&hasMoreRedirects);
     944           0 :   NS_ENSURE_SUCCESS(rv, rv);
     945             : 
     946           0 :   while (hasMoreRedirects) {
     947           0 :     nsCOMPtr<nsISupports> supports;
     948           0 :     rv = iter->GetNext(getter_AddRefs(supports));
     949           0 :     NS_ENSURE_SUCCESS(rv, rv);
     950             : 
     951           0 :     nsCOMPtr<nsIRedirectHistoryEntry> redirectEntry = do_QueryInterface(supports, &rv);
     952           0 :     NS_ENSURE_SUCCESS(rv, rv);
     953             : 
     954           0 :     nsCOMPtr<nsIPrincipal> principal;
     955           0 :     rv = redirectEntry->GetPrincipal(getter_AddRefs(principal));
     956           0 :     NS_ENSURE_SUCCESS(rv, rv);
     957             : 
     958           0 :     nsCOMPtr<nsIURI> uri;
     959           0 :     rv = principal->GetURI(getter_AddRefs(uri));
     960           0 :     NS_ENSURE_SUCCESS(rv, rv);
     961             : 
     962             :     // Add the spec to our list of local lookups. The most recent redirect is
     963             :     // the last element.
     964           0 :     nsCString spec;
     965           0 :     rv = GetStrippedSpec(uri, spec);
     966           0 :     NS_ENSURE_SUCCESS(rv, rv);
     967           0 :     mAnylistSpecs.AppendElement(spec);
     968           0 :     LOG(("ApplicationReputation: Appending redirect %s\n", spec.get()));
     969             : 
     970             :     // Store the redirect information in the remote request.
     971           0 :     ClientDownloadRequest_Resource* resource = mRequest.add_resources();
     972           0 :     resource->set_url(spec.get());
     973           0 :     resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
     974             : 
     975           0 :     rv = iter->HasMoreElements(&hasMoreRedirects);
     976           0 :     NS_ENSURE_SUCCESS(rv, rv);
     977             :   }
     978           0 :   return NS_OK;
     979             : }
     980             : 
     981             : nsresult
     982           0 : PendingLookup::StartLookup()
     983             : {
     984           0 :   mStartTime = TimeStamp::Now();
     985           0 :   nsresult rv = DoLookupInternal();
     986           0 :   if (NS_FAILED(rv)) {
     987           0 :     return OnComplete(false, NS_OK);
     988             :   }
     989           0 :   return rv;
     990             : }
     991             : 
     992             : nsresult
     993           0 : PendingLookup::GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash)
     994             : {
     995             :   nsresult rv;
     996             : 
     997             :   nsCOMPtr<nsICryptoHash> cryptoHash =
     998           0 :     do_CreateInstance("@mozilla.org/security/hash;1", &rv);
     999           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1000           0 :   rv = cryptoHash->Init(nsICryptoHash::SHA256);
    1001           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1002             : 
    1003           0 :   rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(aSpec.BeginReading()),
    1004           0 :                           aSpec.Length());
    1005           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1006             : 
    1007           0 :   nsAutoCString binaryHash;
    1008           0 :   rv = cryptoHash->Finish(false, binaryHash);
    1009           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1010             : 
    1011             :   // This needs to match HexEncode() in Chrome's
    1012             :   // src/base/strings/string_number_conversions.cc
    1013             :   static const char* const hex = "0123456789ABCDEF";
    1014           0 :   hexEncodedHash.SetCapacity(2 * binaryHash.Length());
    1015           0 :   for (size_t i = 0; i < binaryHash.Length(); ++i) {
    1016           0 :     auto c = static_cast<const unsigned char>(binaryHash[i]);
    1017           0 :     hexEncodedHash.Append(hex[(c >> 4) & 0x0F]);
    1018           0 :     hexEncodedHash.Append(hex[c & 0x0F]);
    1019             :   }
    1020             : 
    1021           0 :   return NS_OK;
    1022             : }
    1023             : 
    1024             : nsresult
    1025           0 : PendingLookup::GetStrippedSpec(nsIURI* aUri, nsACString& escaped)
    1026             : {
    1027           0 :   if (NS_WARN_IF(!aUri)) {
    1028           0 :     return NS_ERROR_INVALID_ARG;
    1029             :   }
    1030             : 
    1031             :   nsresult rv;
    1032           0 :   rv = aUri->GetScheme(escaped);
    1033           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1034             : 
    1035           0 :   if (escaped.EqualsLiteral("blob")) {
    1036           0 :     aUri->GetSpec(escaped);
    1037           0 :     LOG(("PendingLookup::GetStrippedSpec(): blob URL left unstripped as '%s' [this = %p]",
    1038             :          PromiseFlatCString(escaped).get(), this));
    1039           0 :     return NS_OK;
    1040             : 
    1041           0 :   } else if (escaped.EqualsLiteral("data")) {
    1042             :     // Replace URI with "data:<everything before comma>,SHA256(<whole URI>)"
    1043           0 :     aUri->GetSpec(escaped);
    1044           0 :     int32_t comma = escaped.FindChar(',');
    1045           0 :     if (comma > -1 &&
    1046           0 :         static_cast<nsCString::size_type>(comma) < escaped.Length() - 1) {
    1047           0 :       MOZ_ASSERT(comma > 4, "Data URIs start with 'data:'");
    1048           0 :       nsAutoCString hexEncodedHash;
    1049           0 :       rv = GetSpecHash(escaped, hexEncodedHash);
    1050           0 :       if (NS_SUCCEEDED(rv)) {
    1051           0 :         escaped.Truncate(comma + 1);
    1052           0 :         escaped.Append(hexEncodedHash);
    1053             :       }
    1054             :     }
    1055             : 
    1056           0 :     LOG(("PendingLookup::GetStrippedSpec(): data URL stripped to '%s' [this = %p]",
    1057             :          PromiseFlatCString(escaped).get(), this));
    1058           0 :     return NS_OK;
    1059             :   }
    1060             : 
    1061             :   // If aURI is not an nsIURL, we do not want to check the lists or send a
    1062             :   // remote query.
    1063           0 :   nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv);
    1064           0 :   if (NS_FAILED(rv)) {
    1065           0 :     LOG(("PendingLookup::GetStrippedSpec(): scheme '%s' is not supported [this = %p]",
    1066             :          PromiseFlatCString(escaped).get(), this));
    1067           0 :     return rv;
    1068             :   }
    1069             : 
    1070           0 :   nsCString temp;
    1071           0 :   rv = url->GetHostPort(temp);
    1072           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1073             : 
    1074           0 :   escaped.Append("://");
    1075           0 :   escaped.Append(temp);
    1076             : 
    1077           0 :   rv = url->GetFilePath(temp);
    1078           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1079             : 
    1080             :   // nsIUrl.filePath starts with '/'
    1081           0 :   escaped.Append(temp);
    1082             : 
    1083           0 :   LOG(("PendingLookup::GetStrippedSpec(): URL stripped to '%s' [this = %p]",
    1084             :        PromiseFlatCString(escaped).get(), this));
    1085           0 :   return NS_OK;
    1086             : }
    1087             : 
    1088             : nsresult
    1089           0 : PendingLookup::DoLookupInternal()
    1090             : {
    1091             :   // We want to check the target URI, its referrer, and associated redirects
    1092             :   // against the local lists.
    1093           0 :   nsCOMPtr<nsIURI> uri;
    1094           0 :   nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
    1095           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1096             : 
    1097           0 :   nsCString sourceSpec;
    1098           0 :   rv = GetStrippedSpec(uri, sourceSpec);
    1099           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1100             : 
    1101           0 :   mAnylistSpecs.AppendElement(sourceSpec);
    1102             : 
    1103           0 :   ClientDownloadRequest_Resource* resource = mRequest.add_resources();
    1104           0 :   resource->set_url(sourceSpec.get());
    1105           0 :   resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
    1106             : 
    1107           0 :   nsCOMPtr<nsIURI> referrer = nullptr;
    1108           0 :   rv = mQuery->GetReferrerURI(getter_AddRefs(referrer));
    1109           0 :   if (referrer) {
    1110           0 :     nsCString referrerSpec;
    1111           0 :     rv = GetStrippedSpec(referrer, referrerSpec);
    1112           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1113           0 :     mAnylistSpecs.AppendElement(referrerSpec);
    1114           0 :     resource->set_referrer(referrerSpec.get());
    1115             :   }
    1116           0 :   nsCOMPtr<nsIArray> redirects;
    1117           0 :   rv = mQuery->GetRedirects(getter_AddRefs(redirects));
    1118           0 :   if (redirects) {
    1119           0 :     AddRedirects(redirects);
    1120             :   } else {
    1121           0 :     LOG(("ApplicationReputation: Got no redirects [this=%p]", this));
    1122             :   }
    1123             : 
    1124             :   // Extract the signature and parse certificates so we can use it to check
    1125             :   // whitelists.
    1126           0 :   nsCOMPtr<nsIArray> sigArray;
    1127           0 :   rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
    1128           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1129             : 
    1130           0 :   if (sigArray) {
    1131           0 :     rv = ParseCertificates(sigArray);
    1132           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1133             :   }
    1134             : 
    1135           0 :   rv = GenerateWhitelistStrings();
    1136           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1137             : 
    1138             :   // Start the call chain.
    1139           0 :   return LookupNext();
    1140             : }
    1141             : 
    1142             : nsresult
    1143           0 : PendingLookup::OnComplete(bool shouldBlock, nsresult rv, uint32_t verdict)
    1144             : {
    1145           0 :   MOZ_ASSERT(!shouldBlock ||
    1146             :              verdict != nsIApplicationReputationService::VERDICT_SAFE);
    1147             : 
    1148           0 :   if (NS_FAILED(rv)) {
    1149           0 :     nsAutoCString errorName;
    1150           0 :     mozilla::GetErrorName(rv, errorName);
    1151           0 :     LOG(("Failed sending remote query for application reputation "
    1152             :          "[rv = %s, this = %p]", errorName.get(), this));
    1153             :   }
    1154             : 
    1155           0 :   if (mTimeoutTimer) {
    1156           0 :     mTimeoutTimer->Cancel();
    1157           0 :     mTimeoutTimer = nullptr;
    1158             :   }
    1159             : 
    1160           0 :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
    1161           0 :     shouldBlock);
    1162           0 :   double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
    1163           0 :   LOG(("Application Reputation verdict is %u, obtained in %f ms [this = %p]",
    1164             :        verdict, t, this));
    1165           0 :   if (shouldBlock) {
    1166           0 :     LOG(("Application Reputation check failed, blocking bad binary [this = %p]",
    1167             :         this));
    1168             :   } else {
    1169           0 :     LOG(("Application Reputation check passed [this = %p]", this));
    1170             :   }
    1171           0 :   nsresult res = mCallback->OnComplete(shouldBlock, rv, verdict);
    1172           0 :   return res;
    1173             : }
    1174             : 
    1175             : nsresult
    1176           0 : PendingLookup::ParseCertificates(nsIArray* aSigArray)
    1177             : {
    1178             :   // If we haven't been set for any reason, bail.
    1179           0 :   NS_ENSURE_ARG_POINTER(aSigArray);
    1180             : 
    1181             :   // Binaries may be signed by multiple chains of certificates. If there are no
    1182             :   // chains, the binary is unsigned (or we were unable to extract signature
    1183             :   // information on a non-Windows platform)
    1184           0 :   nsCOMPtr<nsISimpleEnumerator> chains;
    1185           0 :   nsresult rv = aSigArray->Enumerate(getter_AddRefs(chains));
    1186           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1187             : 
    1188           0 :   bool hasMoreChains = false;
    1189           0 :   rv = chains->HasMoreElements(&hasMoreChains);
    1190           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1191             : 
    1192           0 :   while (hasMoreChains) {
    1193           0 :     nsCOMPtr<nsISupports> chainSupports;
    1194           0 :     rv = chains->GetNext(getter_AddRefs(chainSupports));
    1195           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1196             : 
    1197           0 :     nsCOMPtr<nsIX509CertList> certList = do_QueryInterface(chainSupports, &rv);
    1198           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1199             : 
    1200             :     safe_browsing::ClientDownloadRequest_CertificateChain* certChain =
    1201           0 :       mRequest.mutable_signature()->add_certificate_chain();
    1202           0 :     nsCOMPtr<nsISimpleEnumerator> chainElt;
    1203           0 :     rv = certList->GetEnumerator(getter_AddRefs(chainElt));
    1204           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1205             : 
    1206             :     // Each chain may have multiple certificates.
    1207           0 :     bool hasMoreCerts = false;
    1208           0 :     rv = chainElt->HasMoreElements(&hasMoreCerts);
    1209           0 :     while (hasMoreCerts) {
    1210           0 :       nsCOMPtr<nsISupports> certSupports;
    1211           0 :       rv = chainElt->GetNext(getter_AddRefs(certSupports));
    1212           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1213             : 
    1214           0 :       nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certSupports, &rv);
    1215           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1216             : 
    1217           0 :       uint8_t* data = nullptr;
    1218           0 :       uint32_t len = 0;
    1219           0 :       rv = cert->GetRawDER(&len, &data);
    1220           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1221             : 
    1222             :       // Add this certificate to the protobuf to send remotely.
    1223           0 :       certChain->add_element()->set_certificate(data, len);
    1224           0 :       free(data);
    1225             : 
    1226           0 :       rv = chainElt->HasMoreElements(&hasMoreCerts);
    1227           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1228             :     }
    1229           0 :     rv = chains->HasMoreElements(&hasMoreChains);
    1230           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1231             :   }
    1232           0 :   if (mRequest.signature().certificate_chain_size() > 0) {
    1233           0 :     mRequest.mutable_signature()->set_trusted(true);
    1234             :   }
    1235           0 :   return NS_OK;
    1236             : }
    1237             : 
    1238             : nsresult
    1239           0 : PendingLookup::SendRemoteQuery()
    1240             : {
    1241           0 :   nsresult rv = SendRemoteQueryInternal();
    1242           0 :   if (NS_FAILED(rv)) {
    1243           0 :     return OnComplete(false, rv);
    1244             :   }
    1245             :   // SendRemoteQueryInternal has fired off the query and we call OnComplete in
    1246             :   // the nsIStreamListener.onStopRequest.
    1247           0 :   return rv;
    1248             : }
    1249             : 
    1250             : nsresult
    1251           0 : PendingLookup::SendRemoteQueryInternal()
    1252             : {
    1253             :   // If we aren't supposed to do remote lookups, bail.
    1254           0 :   if (!Preferences::GetBool(PREF_SB_DOWNLOADS_REMOTE_ENABLED, false)) {
    1255           0 :     LOG(("Remote lookups are disabled [this = %p]", this));
    1256           0 :     return NS_ERROR_NOT_AVAILABLE;
    1257             :   }
    1258             :   // If the remote lookup URL is empty or absent, bail.
    1259           0 :   nsCString serviceUrl;
    1260           0 :   NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
    1261             :                     NS_ERROR_NOT_AVAILABLE);
    1262           0 :   if (serviceUrl.IsEmpty()) {
    1263           0 :     LOG(("Remote lookup URL is empty [this = %p]", this));
    1264           0 :     return NS_ERROR_NOT_AVAILABLE;
    1265             :   }
    1266             : 
    1267             :   // If the blocklist or allowlist is empty (so we couldn't do local lookups),
    1268             :   // bail
    1269             :   {
    1270           0 :     nsAutoCString table;
    1271           0 :     NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE,
    1272             :                                               &table),
    1273             :                       NS_ERROR_NOT_AVAILABLE);
    1274           0 :     if (table.IsEmpty()) {
    1275           0 :       LOG(("Blocklist is empty [this = %p]", this));
    1276           0 :       return NS_ERROR_NOT_AVAILABLE;
    1277             :     }
    1278             :   }
    1279             :   {
    1280           0 :     nsAutoCString table;
    1281           0 :     NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE,
    1282             :                                               &table),
    1283             :                       NS_ERROR_NOT_AVAILABLE);
    1284           0 :     if (table.IsEmpty()) {
    1285           0 :       LOG(("Allowlist is empty [this = %p]", this));
    1286           0 :       return NS_ERROR_NOT_AVAILABLE;
    1287             :     }
    1288             :   }
    1289             : 
    1290           0 :   LOG(("Sending remote query for application reputation [this = %p]",
    1291             :        this));
    1292             :   // We did not find a local result, so fire off the query to the
    1293             :   // application reputation service.
    1294           0 :   nsCOMPtr<nsIURI> uri;
    1295             :   nsresult rv;
    1296           0 :   rv = mQuery->GetSourceURI(getter_AddRefs(uri));
    1297           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1298           0 :   nsCString spec;
    1299           0 :   rv = GetStrippedSpec(uri, spec);
    1300           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1301           0 :   mRequest.set_url(spec.get());
    1302             : 
    1303             :   uint32_t fileSize;
    1304           0 :   rv = mQuery->GetFileSize(&fileSize);
    1305           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1306           0 :   mRequest.set_length(fileSize);
    1307             :   // We have no way of knowing whether or not a user initiated the
    1308             :   // download. Set it to true to lessen the chance of false positives.
    1309           0 :   mRequest.set_user_initiated(true);
    1310             : 
    1311           0 :   nsCString locale;
    1312           0 :   rv = LocaleService::GetInstance()->GetAppLocaleAsLangTag(locale);
    1313           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1314           0 :   mRequest.set_locale(locale.get());
    1315           0 :   nsCString sha256Hash;
    1316           0 :   rv = mQuery->GetSha256Hash(sha256Hash);
    1317           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1318           0 :   mRequest.mutable_digests()->set_sha256(sha256Hash.Data());
    1319           0 :   nsString fileName;
    1320           0 :   rv = mQuery->GetSuggestedFileName(fileName);
    1321           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1322           0 :   mRequest.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
    1323           0 :   mRequest.set_download_type(GetDownloadType(fileName));
    1324             : 
    1325           0 :   if (mRequest.signature().trusted()) {
    1326           0 :     LOG(("Got signed binary for remote application reputation check "
    1327             :          "[this = %p]", this));
    1328             :   } else {
    1329           0 :     LOG(("Got unsigned binary for remote application reputation check "
    1330             :          "[this = %p]", this));
    1331             :   }
    1332             : 
    1333             :   // Serialize the protocol buffer to a string. This can only fail if we are
    1334             :   // out of memory, or if the protocol buffer req is missing required fields
    1335             :   // (only the URL for now).
    1336           0 :   std::string serialized;
    1337           0 :   if (!mRequest.SerializeToString(&serialized)) {
    1338           0 :     return NS_ERROR_UNEXPECTED;
    1339             :   }
    1340           0 :   LOG(("Serialized protocol buffer [this = %p]: (length=%" PRIuSIZE ") %s", this,
    1341             :        serialized.length(), serialized.c_str()));
    1342             : 
    1343             :   // Set the input stream to the serialized protocol buffer
    1344             :   nsCOMPtr<nsIStringInputStream> sstream =
    1345           0 :     do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
    1346           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1347             : 
    1348           0 :   rv = sstream->SetData(serialized.c_str(), serialized.length());
    1349           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1350             : 
    1351             :   // Set up the channel to transmit the request to the service.
    1352           0 :   nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
    1353           0 :   rv = ios->NewChannel2(serviceUrl,
    1354             :                         nullptr,
    1355             :                         nullptr,
    1356             :                         nullptr, // aLoadingNode
    1357             :                         nsContentUtils::GetSystemPrincipal(),
    1358             :                         nullptr, // aTriggeringPrincipal
    1359             :                         nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    1360             :                         nsIContentPolicy::TYPE_OTHER,
    1361           0 :                         getter_AddRefs(mChannel));
    1362           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1363             : 
    1364           0 :   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
    1365           0 :   if (loadInfo) {
    1366           0 :     mozilla::OriginAttributes attrs;
    1367           0 :     attrs.mFirstPartyDomain.AssignLiteral(NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN);
    1368           0 :     loadInfo->SetOriginAttributes(attrs);
    1369             :   }
    1370             : 
    1371           0 :   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel, &rv));
    1372           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1373             :   mozilla::Unused << httpChannel;
    1374             : 
    1375             :   // Upload the protobuf to the application reputation service.
    1376           0 :   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(mChannel, &rv);
    1377           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1378             : 
    1379           0 :   rv = uploadChannel->ExplicitSetUploadStream(sstream,
    1380           0 :     NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
    1381           0 :     NS_LITERAL_CSTRING("POST"), false);
    1382           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1383             : 
    1384           0 :   uint32_t timeoutMs = Preferences::GetUint(PREF_SB_DOWNLOADS_REMOTE_TIMEOUT, 10000);
    1385           0 :   mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    1386           0 :   mTimeoutTimer->InitWithCallback(this, timeoutMs, nsITimer::TYPE_ONE_SHOT);
    1387             : 
    1388           0 :   rv = mChannel->AsyncOpen2(this);
    1389           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1390             : 
    1391           0 :   return NS_OK;
    1392             : }
    1393             : 
    1394             : NS_IMETHODIMP
    1395           0 : PendingLookup::Notify(nsITimer* aTimer)
    1396             : {
    1397           0 :   LOG(("Remote lookup timed out [this = %p]", this));
    1398           0 :   MOZ_ASSERT(aTimer == mTimeoutTimer);
    1399             :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_REMOTE_LOOKUP_TIMEOUT,
    1400           0 :     true);
    1401           0 :   mChannel->Cancel(NS_ERROR_NET_TIMEOUT);
    1402           0 :   mTimeoutTimer->Cancel();
    1403           0 :   return NS_OK;
    1404             : }
    1405             : 
    1406             : ///////////////////////////////////////////////////////////////////////////////
    1407             : // nsIObserver implementation
    1408             : NS_IMETHODIMP
    1409           0 : PendingLookup::Observe(nsISupports *aSubject, const char *aTopic,
    1410             :                        const char16_t *aData)
    1411             : {
    1412           0 :   if (!strcmp(aTopic, "quit-application")) {
    1413           0 :     if (mTimeoutTimer) {
    1414           0 :       mTimeoutTimer->Cancel();
    1415           0 :       mTimeoutTimer = nullptr;
    1416             :     }
    1417           0 :     if (mChannel) {
    1418           0 :       mChannel->Cancel(NS_ERROR_ABORT);
    1419             :     }
    1420             :   }
    1421           0 :   return NS_OK;
    1422             : }
    1423             : 
    1424             : ////////////////////////////////////////////////////////////////////////////////
    1425             : //// nsIStreamListener
    1426             : static nsresult
    1427           0 : AppendSegmentToString(nsIInputStream* inputStream,
    1428             :                       void *closure,
    1429             :                       const char *rawSegment,
    1430             :                       uint32_t toOffset,
    1431             :                       uint32_t count,
    1432             :                       uint32_t *writeCount) {
    1433           0 :   nsAutoCString* decodedData = static_cast<nsAutoCString*>(closure);
    1434           0 :   decodedData->Append(rawSegment, count);
    1435           0 :   *writeCount = count;
    1436           0 :   return NS_OK;
    1437             : }
    1438             : 
    1439             : NS_IMETHODIMP
    1440           0 : PendingLookup::OnDataAvailable(nsIRequest *aRequest,
    1441             :                                nsISupports *aContext,
    1442             :                                nsIInputStream *aStream,
    1443             :                                uint64_t offset,
    1444             :                                uint32_t count) {
    1445             :   uint32_t read;
    1446           0 :   return aStream->ReadSegments(AppendSegmentToString, &mResponse, count, &read);
    1447             : }
    1448             : 
    1449             : NS_IMETHODIMP
    1450           0 : PendingLookup::OnStartRequest(nsIRequest *aRequest,
    1451             :                               nsISupports *aContext) {
    1452           0 :   return NS_OK;
    1453             : }
    1454             : 
    1455             : NS_IMETHODIMP
    1456           0 : PendingLookup::OnStopRequest(nsIRequest *aRequest,
    1457             :                              nsISupports *aContext,
    1458             :                              nsresult aResult) {
    1459           0 :   NS_ENSURE_STATE(mCallback);
    1460             : 
    1461           0 :   bool shouldBlock = false;
    1462           0 :   uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE;
    1463             :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_REMOTE_LOOKUP_TIMEOUT,
    1464           0 :     false);
    1465             : 
    1466             :   nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
    1467           0 :                                       &shouldBlock, &verdict);
    1468           0 :   OnComplete(shouldBlock, rv, verdict);
    1469           0 :   return rv;
    1470             : }
    1471             : 
    1472             : nsresult
    1473           0 : PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
    1474             :                                      nsISupports *aContext,
    1475             :                                      nsresult aResult,
    1476             :                                      bool* aShouldBlock,
    1477             :                                      uint32_t* aVerdict) {
    1478           0 :   if (NS_FAILED(aResult)) {
    1479             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1480           0 :       SERVER_RESPONSE_FAILED);
    1481           0 :     return aResult;
    1482             :   }
    1483             : 
    1484           0 :   *aShouldBlock = false;
    1485           0 :   *aVerdict = nsIApplicationReputationService::VERDICT_SAFE;
    1486             :   nsresult rv;
    1487           0 :   nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
    1488           0 :   if (NS_FAILED(rv)) {
    1489             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1490           0 :       SERVER_RESPONSE_FAILED);
    1491           0 :     return rv;
    1492             :   }
    1493             : 
    1494           0 :   uint32_t status = 0;
    1495           0 :   rv = channel->GetResponseStatus(&status);
    1496           0 :   if (NS_FAILED(rv)) {
    1497             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1498           0 :       SERVER_RESPONSE_FAILED);
    1499           0 :     return rv;
    1500             :   }
    1501             : 
    1502           0 :   if (status != 200) {
    1503             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1504           0 :       SERVER_RESPONSE_FAILED);
    1505           0 :     return NS_ERROR_NOT_AVAILABLE;
    1506             :   }
    1507             : 
    1508           0 :   std::string buf(mResponse.Data(), mResponse.Length());
    1509           0 :   safe_browsing::ClientDownloadResponse response;
    1510           0 :   if (!response.ParseFromString(buf)) {
    1511           0 :     LOG(("Invalid protocol buffer response [this = %p]: %s", this, buf.c_str()));
    1512             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1513           0 :                                    SERVER_RESPONSE_INVALID);
    1514           0 :     return NS_ERROR_CANNOT_CONVERT_DATA;
    1515             :   }
    1516             : 
    1517             :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
    1518           0 :     SERVER_RESPONSE_VALID);
    1519             :   // Clamp responses 0-7, we only know about 0-4 for now.
    1520           0 :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER_VERDICT,
    1521           0 :     std::min<uint32_t>(response.verdict(), 7));
    1522           0 :   switch(response.verdict()) {
    1523             :     case safe_browsing::ClientDownloadResponse::DANGEROUS:
    1524           0 :       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS, true);
    1525           0 :       *aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS;
    1526           0 :       break;
    1527             :     case safe_browsing::ClientDownloadResponse::DANGEROUS_HOST:
    1528           0 :       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS_HOST, true);
    1529           0 :       *aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS_HOST;
    1530           0 :       break;
    1531             :     case safe_browsing::ClientDownloadResponse::POTENTIALLY_UNWANTED:
    1532           0 :       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_POTENTIALLY_UNWANTED, false);
    1533           0 :       *aVerdict = nsIApplicationReputationService::VERDICT_POTENTIALLY_UNWANTED;
    1534           0 :       break;
    1535             :     case safe_browsing::ClientDownloadResponse::UNCOMMON:
    1536           0 :       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_UNCOMMON, false);
    1537           0 :       *aVerdict = nsIApplicationReputationService::VERDICT_UNCOMMON;
    1538           0 :       break;
    1539             :     default:
    1540             :       // Treat everything else as safe
    1541           0 :       break;
    1542             :   }
    1543             : 
    1544           0 :   return NS_OK;
    1545             : }
    1546             : 
    1547           0 : NS_IMPL_ISUPPORTS(ApplicationReputationService,
    1548             :                   nsIApplicationReputationService)
    1549             : 
    1550             : ApplicationReputationService*
    1551             :   ApplicationReputationService::gApplicationReputationService = nullptr;
    1552             : 
    1553             : ApplicationReputationService*
    1554           0 : ApplicationReputationService::GetSingleton()
    1555             : {
    1556           0 :   if (gApplicationReputationService) {
    1557           0 :     NS_ADDREF(gApplicationReputationService);
    1558           0 :     return gApplicationReputationService;
    1559             :   }
    1560             : 
    1561             :   // We're not initialized yet.
    1562           0 :   gApplicationReputationService = new ApplicationReputationService();
    1563           0 :   if (gApplicationReputationService) {
    1564           0 :     NS_ADDREF(gApplicationReputationService);
    1565             :   }
    1566             : 
    1567           0 :   return gApplicationReputationService;
    1568             : }
    1569             : 
    1570           0 : ApplicationReputationService::ApplicationReputationService()
    1571             : {
    1572           0 :   LOG(("Application reputation service started up"));
    1573           0 : }
    1574             : 
    1575           0 : ApplicationReputationService::~ApplicationReputationService() {
    1576           0 :   LOG(("Application reputation service shutting down"));
    1577           0 : }
    1578             : 
    1579             : NS_IMETHODIMP
    1580           0 : ApplicationReputationService::QueryReputation(
    1581             :     nsIApplicationReputationQuery* aQuery,
    1582             :     nsIApplicationReputationCallback* aCallback) {
    1583           0 :   LOG(("Starting application reputation check [query=%p]", aQuery));
    1584           0 :   NS_ENSURE_ARG_POINTER(aQuery);
    1585           0 :   NS_ENSURE_ARG_POINTER(aCallback);
    1586             : 
    1587           0 :   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
    1588           0 :   nsresult rv = QueryReputationInternal(aQuery, aCallback);
    1589           0 :   if (NS_FAILED(rv)) {
    1590             :     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
    1591           0 :       false);
    1592             :     aCallback->OnComplete(false, rv,
    1593           0 :                           nsIApplicationReputationService::VERDICT_SAFE);
    1594             :   }
    1595           0 :   return NS_OK;
    1596             : }
    1597             : 
    1598           0 : nsresult ApplicationReputationService::QueryReputationInternal(
    1599             :   nsIApplicationReputationQuery* aQuery,
    1600             :   nsIApplicationReputationCallback* aCallback) {
    1601             :   nsresult rv;
    1602             :   // If malware checks aren't enabled, don't query application reputation.
    1603           0 :   if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
    1604           0 :     return NS_ERROR_NOT_AVAILABLE;
    1605             :   }
    1606             : 
    1607           0 :   if (!Preferences::GetBool(PREF_SB_DOWNLOADS_ENABLED, false)) {
    1608           0 :     return NS_ERROR_NOT_AVAILABLE;
    1609             :   }
    1610             : 
    1611           0 :   nsCOMPtr<nsIURI> uri;
    1612           0 :   rv = aQuery->GetSourceURI(getter_AddRefs(uri));
    1613           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1614             :   // Bail if the URI hasn't been set.
    1615           0 :   NS_ENSURE_STATE(uri);
    1616             : 
    1617             :   // Create a new pending lookup and start the call chain.
    1618           0 :   RefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
    1619           0 :   NS_ENSURE_STATE(lookup);
    1620             : 
    1621             :   // Add an observer for shutdown
    1622             :   nsCOMPtr<nsIObserverService> observerService =
    1623           0 :     mozilla::services::GetObserverService();
    1624           0 :   if (!observerService) {
    1625           0 :     return NS_ERROR_FAILURE;
    1626             :   }
    1627             : 
    1628           0 :   observerService->AddObserver(lookup, "quit-application", true);
    1629           0 :   return lookup->StartLookup();
    1630             : }

Generated by: LCOV version 1.13