I'm adding a helper function mozILocaleService::GetRequestedLocale to simplify most of the callsites that are looking for the first of the requested locales. In most cases, I'm just matching the behavior of the code with reusing LocaleService API instead of direct manipulation on the prefs. That includes how I handle error case scenarios. In case of sdk/l10n/locale.js I am reusing LocaleService heuristics over the custom one from the file since the ones in LocaleService are just more correct and unified accross the whole platform. In case of FallbackEncoding I have to turn it into a nsIObserver to listen to intl:requested-locales-changed. MozReview-Commit-ID: 7rOr2CovLK
1642 lines
52 KiB
C++
1642 lines
52 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
// See
|
|
// https://wiki.mozilla.org/Security/Features/Application_Reputation_Design_Doc
|
|
// for a description of Chrome's implementation of this feature.
|
|
#include "ApplicationReputation.h"
|
|
#include "chrome/common/safe_browsing/csd.pb.h"
|
|
|
|
#include "nsIArray.h"
|
|
#include "nsIApplicationReputation.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsIHttpChannel.h"
|
|
#include "nsIIOService.h"
|
|
#include "nsIPrefService.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIStreamListener.h"
|
|
#include "nsIStringStream.h"
|
|
#include "nsITimer.h"
|
|
#include "nsIUploadChannel2.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIURL.h"
|
|
#include "nsIUrlClassifierDBService.h"
|
|
#include "nsIX509Cert.h"
|
|
#include "nsIX509CertDB.h"
|
|
#include "nsIX509CertList.h"
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "mozilla/ErrorNames.h"
|
|
#include "mozilla/LoadContext.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/SizePrintfMacros.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/intl/LocaleService.h"
|
|
|
|
#include "nsAutoPtr.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsDebug.h"
|
|
#include "nsDependentSubstring.h"
|
|
#include "nsError.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsString.h"
|
|
#include "nsTArray.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsIContentPolicy.h"
|
|
#include "nsILoadInfo.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsWeakReference.h"
|
|
#include "nsCharSeparatedTokenizer.h"
|
|
|
|
using namespace mozilla::downloads;
|
|
using mozilla::ArrayLength;
|
|
using mozilla::BasePrincipal;
|
|
using mozilla::OriginAttributes;
|
|
using mozilla::Preferences;
|
|
using mozilla::TimeStamp;
|
|
using mozilla::Telemetry::Accumulate;
|
|
using mozilla::intl::LocaleService;
|
|
using safe_browsing::ClientDownloadRequest;
|
|
using safe_browsing::ClientDownloadRequest_CertificateChain;
|
|
using safe_browsing::ClientDownloadRequest_Resource;
|
|
using safe_browsing::ClientDownloadRequest_SignatureInfo;
|
|
|
|
// Preferences that we need to initialize the query.
|
|
#define PREF_SB_APP_REP_URL "browser.safebrowsing.downloads.remote.url"
|
|
#define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
|
|
#define PREF_SB_DOWNLOADS_ENABLED "browser.safebrowsing.downloads.enabled"
|
|
#define PREF_SB_DOWNLOADS_REMOTE_ENABLED "browser.safebrowsing.downloads.remote.enabled"
|
|
#define PREF_SB_DOWNLOADS_REMOTE_TIMEOUT "browser.safebrowsing.downloads.remote.timeout_ms"
|
|
#define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.downloadBlockTable"
|
|
#define PREF_DOWNLOAD_ALLOW_TABLE "urlclassifier.downloadAllowTable"
|
|
|
|
// Preferences that are needed to action the verdict.
|
|
#define PREF_BLOCK_DANGEROUS "browser.safebrowsing.downloads.remote.block_dangerous"
|
|
#define PREF_BLOCK_DANGEROUS_HOST "browser.safebrowsing.downloads.remote.block_dangerous_host"
|
|
#define PREF_BLOCK_POTENTIALLY_UNWANTED "browser.safebrowsing.downloads.remote.block_potentially_unwanted"
|
|
#define PREF_BLOCK_UNCOMMON "browser.safebrowsing.downloads.remote.block_uncommon"
|
|
|
|
// MOZ_LOG=ApplicationReputation:5
|
|
mozilla::LazyLogModule ApplicationReputationService::prlog("ApplicationReputation");
|
|
#define LOG(args) MOZ_LOG(ApplicationReputationService::prlog, mozilla::LogLevel::Debug, args)
|
|
#define LOG_ENABLED() MOZ_LOG_TEST(ApplicationReputationService::prlog, mozilla::LogLevel::Debug)
|
|
|
|
namespace mozilla {
|
|
namespace downloads {
|
|
|
|
enum class TelemetryMatchInfo : uint8_t
|
|
{
|
|
eNoMatch = 0x00,
|
|
eV2Match = 0x01,
|
|
eV4Match = 0x02,
|
|
eBothMatch = eV2Match | eV4Match,
|
|
};
|
|
|
|
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TelemetryMatchInfo)
|
|
|
|
// Given a comma-separated list of tables which matched a URL, check to see if
|
|
// at least one of these tables is present in the given pref.
|
|
bool
|
|
LookupTablesInPrefs(const nsACString& tables, const char* aPref)
|
|
{
|
|
nsAutoCString prefList;
|
|
Preferences::GetCString(aPref, &prefList);
|
|
if (prefList.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if V2 and V4 are enabled in preference
|
|
// If V2 and V4 are both enabled, then we should do a telemetry record
|
|
// Both V2 and V4 begin with "goog" but V4 ends with "-proto"
|
|
nsCCharSeparatedTokenizer prefTokens(prefList, ',');
|
|
nsCString prefToken;
|
|
bool isV4Enabled = false;
|
|
bool isV2Enabled = false;
|
|
|
|
while (prefTokens.hasMoreTokens()) {
|
|
prefToken = prefTokens.nextToken();
|
|
if (StringBeginsWith(prefToken, NS_LITERAL_CSTRING("goog"))) {
|
|
if (StringEndsWith(prefToken, NS_LITERAL_CSTRING("-proto"))) {
|
|
isV4Enabled = true;
|
|
} else {
|
|
isV2Enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool shouldRecordTelemetry = isV2Enabled && isV4Enabled;
|
|
TelemetryMatchInfo telemetryInfo = TelemetryMatchInfo::eNoMatch;
|
|
|
|
// Parsed tables separated by "," into tokens then lookup each token
|
|
// in preference list
|
|
nsCCharSeparatedTokenizer tokens(tables, ',');
|
|
nsCString table;
|
|
bool found = false;
|
|
|
|
while (tokens.hasMoreTokens()) {
|
|
table = tokens.nextToken();
|
|
if (table.IsEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
if (!FindInReadable(table, prefList)) {
|
|
continue;
|
|
}
|
|
found = true;
|
|
|
|
if (!shouldRecordTelemetry) {
|
|
return found;
|
|
}
|
|
|
|
// We are checking if the table found is V2 or V4 to record telemetry
|
|
// Both V2 and V4 begin with "goog" but V4 ends with "-proto"
|
|
if (StringBeginsWith(prefToken, NS_LITERAL_CSTRING("goog"))) {
|
|
if (StringEndsWith(prefToken, NS_LITERAL_CSTRING("-proto"))) {
|
|
telemetryInfo |= TelemetryMatchInfo::eV4Match;
|
|
} else {
|
|
telemetryInfo |= TelemetryMatchInfo::eV2Match;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Record telemetry for matching allow list and block list
|
|
if (!strcmp(aPref, PREF_DOWNLOAD_BLOCK_TABLE)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_BLOCKLIST_MATCH,
|
|
static_cast<uint8_t>(telemetryInfo));
|
|
} else if (!strcmp(aPref, PREF_DOWNLOAD_ALLOW_TABLE)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_ALLOWLIST_MATCH,
|
|
static_cast<uint8_t>(telemetryInfo));
|
|
}
|
|
|
|
return found;
|
|
}
|
|
} // namespace downloads
|
|
} // namespace mozilla
|
|
|
|
class PendingDBLookup;
|
|
|
|
// A single use class private to ApplicationReputationService encapsulating an
|
|
// nsIApplicationReputationQuery and an nsIApplicationReputationCallback. Once
|
|
// created by ApplicationReputationService, it is guaranteed to call mCallback.
|
|
// This class is private to ApplicationReputationService.
|
|
class PendingLookup final : public nsIStreamListener,
|
|
public nsITimerCallback,
|
|
public nsIObserver,
|
|
public nsSupportsWeakReference
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
NS_DECL_NSISTREAMLISTENER
|
|
NS_DECL_NSITIMERCALLBACK
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
// Constructor and destructor.
|
|
PendingLookup(nsIApplicationReputationQuery* aQuery,
|
|
nsIApplicationReputationCallback* aCallback);
|
|
|
|
// Start the lookup. The lookup may have 2 parts: local and remote. In the
|
|
// local lookup, PendingDBLookups are created to query the local allow and
|
|
// blocklists for various URIs associated with this downloaded file. In the
|
|
// event that no results are found, a remote lookup is sent to the Application
|
|
// Reputation server.
|
|
nsresult StartLookup();
|
|
|
|
private:
|
|
~PendingLookup();
|
|
|
|
friend class PendingDBLookup;
|
|
|
|
// Telemetry states.
|
|
// Status of the remote response (valid or not).
|
|
enum SERVER_RESPONSE_TYPES {
|
|
SERVER_RESPONSE_VALID = 0,
|
|
SERVER_RESPONSE_FAILED = 1,
|
|
SERVER_RESPONSE_INVALID = 2,
|
|
};
|
|
|
|
// Number of blocklist and allowlist hits we have seen.
|
|
uint32_t mBlocklistCount;
|
|
uint32_t mAllowlistCount;
|
|
|
|
// The query containing metadata about the downloaded file.
|
|
nsCOMPtr<nsIApplicationReputationQuery> mQuery;
|
|
|
|
// The callback with which to report the verdict.
|
|
nsCOMPtr<nsIApplicationReputationCallback> mCallback;
|
|
|
|
// An array of strings created from certificate information used to whitelist
|
|
// the downloaded file.
|
|
nsTArray<nsCString> mAllowlistSpecs;
|
|
// The source URI of the download, the referrer and possibly any redirects.
|
|
nsTArray<nsCString> mAnylistSpecs;
|
|
|
|
// When we started this query
|
|
TimeStamp mStartTime;
|
|
|
|
// The channel used to talk to the remote lookup server
|
|
nsCOMPtr<nsIChannel> mChannel;
|
|
|
|
// Timer to abort this lookup if it takes too long
|
|
nsCOMPtr<nsITimer> mTimeoutTimer;
|
|
|
|
// A protocol buffer for storing things we need in the remote request. We
|
|
// store the resource chain (redirect information) as well as signature
|
|
// information extracted using the Windows Authenticode API, if the binary is
|
|
// signed.
|
|
ClientDownloadRequest mRequest;
|
|
|
|
// The response from the application reputation query. This is read in chunks
|
|
// as part of our nsIStreamListener implementation and may contain embedded
|
|
// NULLs.
|
|
nsCString mResponse;
|
|
|
|
// Returns true if the file is likely to be binary.
|
|
bool IsBinaryFile();
|
|
|
|
// Returns the type of download binary for the file.
|
|
ClientDownloadRequest::DownloadType GetDownloadType(const nsAString& aFilename);
|
|
|
|
// Clean up and call the callback. PendingLookup must not be used after this
|
|
// function is called.
|
|
nsresult OnComplete(bool shouldBlock, nsresult rv,
|
|
uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE);
|
|
|
|
// Wrapper function for nsIStreamListener.onStopRequest to make it easy to
|
|
// guarantee calling the callback
|
|
nsresult OnStopRequestInternal(nsIRequest *aRequest,
|
|
nsISupports *aContext,
|
|
nsresult aResult,
|
|
bool* aShouldBlock,
|
|
uint32_t* aVerdict);
|
|
|
|
// Strip url parameters, fragments, and user@pass fields from the URI spec
|
|
// using nsIURL. If aURI is not an nsIURL, returns the original nsIURI.spec.
|
|
nsresult GetStrippedSpec(nsIURI* aUri, nsACString& spec);
|
|
|
|
// Escape '/' and '%' in certificate attribute values.
|
|
nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
|
|
|
|
// Escape ':' in fingerprint values.
|
|
nsCString EscapeFingerprint(const nsACString& aAttribute);
|
|
|
|
// Generate whitelist strings for the given certificate pair from the same
|
|
// certificate chain.
|
|
nsresult GenerateWhitelistStringsForPair(
|
|
nsIX509Cert* certificate, nsIX509Cert* issuer);
|
|
|
|
// Generate whitelist strings for the given certificate chain, which starts
|
|
// with the signer and may go all the way to the root cert.
|
|
nsresult GenerateWhitelistStringsForChain(
|
|
const ClientDownloadRequest_CertificateChain& aChain);
|
|
|
|
// For signed binaries, generate strings of the form:
|
|
// http://sb-ssl.google.com/safebrowsing/csd/certificate/
|
|
// <issuer_cert_sha1_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
|
|
// for each (cert, issuer) pair in each chain of certificates that is
|
|
// associated with the binary.
|
|
nsresult GenerateWhitelistStrings();
|
|
|
|
// Parse the XPCOM certificate lists and stick them into the protocol buffer
|
|
// version.
|
|
nsresult ParseCertificates(nsIArray* aSigArray);
|
|
|
|
// Adds the redirects to mAnylistSpecs to be looked up.
|
|
nsresult AddRedirects(nsIArray* aRedirects);
|
|
|
|
// Helper function to ensure that we call PendingLookup::LookupNext or
|
|
// PendingLookup::OnComplete.
|
|
nsresult DoLookupInternal();
|
|
|
|
// Looks up all the URIs that may be responsible for allowlisting or
|
|
// blocklisting the downloaded file. These URIs may include whitelist strings
|
|
// generated by certificates verifying the binary as well as the target URI
|
|
// from which the file was downloaded.
|
|
nsresult LookupNext();
|
|
|
|
// Sends a query to the remote application reputation service. Returns NS_OK
|
|
// on success.
|
|
nsresult SendRemoteQuery();
|
|
|
|
// Helper function to ensure that we always call the callback.
|
|
nsresult SendRemoteQueryInternal();
|
|
};
|
|
|
|
// A single-use class for looking up a single URI in the safebrowsing DB. This
|
|
// class is private to PendingLookup.
|
|
class PendingDBLookup final : public nsIUrlClassifierCallback
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIURLCLASSIFIERCALLBACK
|
|
|
|
// Constructor and destructor
|
|
explicit PendingDBLookup(PendingLookup* aPendingLookup);
|
|
|
|
// Look up the given URI in the safebrowsing DBs, optionally on both the allow
|
|
// list and the blocklist. If there is a match, call
|
|
// PendingLookup::OnComplete. Otherwise, call PendingLookup::LookupNext.
|
|
nsresult LookupSpec(const nsACString& aSpec, bool aAllowlistOnly);
|
|
|
|
private:
|
|
~PendingDBLookup();
|
|
|
|
// The download appeared on the allowlist, blocklist, or no list (and thus
|
|
// could trigger a remote query.
|
|
enum LIST_TYPES {
|
|
ALLOW_LIST = 0,
|
|
BLOCK_LIST = 1,
|
|
NO_LIST = 2,
|
|
};
|
|
|
|
nsCString mSpec;
|
|
bool mAllowlistOnly;
|
|
RefPtr<PendingLookup> mPendingLookup;
|
|
nsresult LookupSpecInternal(const nsACString& aSpec);
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(PendingDBLookup,
|
|
nsIUrlClassifierCallback)
|
|
|
|
PendingDBLookup::PendingDBLookup(PendingLookup* aPendingLookup) :
|
|
mAllowlistOnly(false),
|
|
mPendingLookup(aPendingLookup)
|
|
{
|
|
LOG(("Created pending DB lookup [this = %p]", this));
|
|
}
|
|
|
|
PendingDBLookup::~PendingDBLookup()
|
|
{
|
|
LOG(("Destroying pending DB lookup [this = %p]", this));
|
|
mPendingLookup = nullptr;
|
|
}
|
|
|
|
nsresult
|
|
PendingDBLookup::LookupSpec(const nsACString& aSpec,
|
|
bool aAllowlistOnly)
|
|
{
|
|
LOG(("Checking principal %s [this=%p]", aSpec.Data(), this));
|
|
mSpec = aSpec;
|
|
mAllowlistOnly = aAllowlistOnly;
|
|
nsresult rv = LookupSpecInternal(aSpec);
|
|
if (NS_FAILED(rv)) {
|
|
LOG(("Error in LookupSpecInternal"));
|
|
return mPendingLookup->OnComplete(false, NS_OK);
|
|
}
|
|
// LookupSpecInternal has called nsIUrlClassifierCallback.lookup, which is
|
|
// guaranteed to call HandleEvent.
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
PendingDBLookup::LookupSpecInternal(const nsACString& aSpec)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
|
|
rv = ios->NewURI(aSpec, nullptr, nullptr, getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
OriginAttributes attrs;
|
|
nsCOMPtr<nsIPrincipal> principal =
|
|
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
|
if (!principal) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Check local lists to see if the URI has already been whitelisted or
|
|
// blacklisted.
|
|
LOG(("Checking DB service for principal %s [this = %p]", mSpec.get(), this));
|
|
nsCOMPtr<nsIUrlClassifierDBService> dbService =
|
|
do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoCString tables;
|
|
nsAutoCString allowlist;
|
|
Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowlist);
|
|
if (!allowlist.IsEmpty()) {
|
|
tables.Append(allowlist);
|
|
}
|
|
nsAutoCString blocklist;
|
|
Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blocklist);
|
|
if (!mAllowlistOnly && !blocklist.IsEmpty()) {
|
|
tables.Append(',');
|
|
tables.Append(blocklist);
|
|
}
|
|
return dbService->Lookup(principal, tables, this);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PendingDBLookup::HandleEvent(const nsACString& tables)
|
|
{
|
|
// HandleEvent is guaranteed to call either:
|
|
// 1) PendingLookup::OnComplete if the URL matches the blocklist, or
|
|
// 2) PendingLookup::LookupNext if the URL does not match the blocklist.
|
|
// Blocklisting trumps allowlisting.
|
|
if (!mAllowlistOnly && LookupTablesInPrefs(tables, PREF_DOWNLOAD_BLOCK_TABLE)) {
|
|
mPendingLookup->mBlocklistCount++;
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
|
|
LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
|
|
return mPendingLookup->OnComplete(true, NS_OK,
|
|
nsIApplicationReputationService::VERDICT_DANGEROUS);
|
|
}
|
|
|
|
if (LookupTablesInPrefs(tables, PREF_DOWNLOAD_ALLOW_TABLE)) {
|
|
mPendingLookup->mAllowlistCount++;
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
|
|
LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
|
|
// Don't call onComplete, since blocklisting trumps allowlisting
|
|
} else {
|
|
LOG(("Didn't find principal %s on any list [this = %p]", mSpec.get(),
|
|
this));
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, NO_LIST);
|
|
}
|
|
return mPendingLookup->LookupNext();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(PendingLookup,
|
|
nsIStreamListener,
|
|
nsIRequestObserver,
|
|
nsIObserver,
|
|
nsISupportsWeakReference)
|
|
|
|
PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
|
|
nsIApplicationReputationCallback* aCallback) :
|
|
mBlocklistCount(0),
|
|
mAllowlistCount(0),
|
|
mQuery(aQuery),
|
|
mCallback(aCallback)
|
|
{
|
|
LOG(("Created pending lookup [this = %p]", this));
|
|
}
|
|
|
|
PendingLookup::~PendingLookup()
|
|
{
|
|
LOG(("Destroying pending lookup [this = %p]", this));
|
|
}
|
|
|
|
static const char16_t* const kBinaryFileExtensions[] = {
|
|
// Extracted from the "File Type Policies" Chrome extension
|
|
//u".001",
|
|
//u".7z",
|
|
//u".ace",
|
|
//u".action", // Mac script
|
|
//u".ad", // Windows
|
|
u".ade", // MS Access
|
|
u".adp", // MS Access
|
|
u".apk", // Android package
|
|
u".app", // Executable application
|
|
u".application", // MS ClickOnce
|
|
u".appref-ms", // MS ClickOnce
|
|
//u".arc",
|
|
//u".arj",
|
|
u".as", // Mac archive
|
|
u".asp", // Windows Server script
|
|
u".asx", // Windows Media Player
|
|
//u".b64",
|
|
//u".balz",
|
|
u".bas", // Basic script
|
|
u".bash", // Linux shell
|
|
u".bat", // Windows shell
|
|
//u".bhx",
|
|
//u".bin",
|
|
u".bz", // Linux archive (bzip)
|
|
u".bz2", // Linux archive (bzip2)
|
|
u".bzip2", // Linux archive (bzip2)
|
|
u".cab", // Windows archive
|
|
u".cdr", // Mac disk image
|
|
u".cfg", // Windows
|
|
u".chi", // Windows Help
|
|
u".chm", // Windows Help
|
|
u".class", // Java
|
|
u".cmd", // Windows executable
|
|
u".com", // Windows executable
|
|
u".command", // Mac script
|
|
u".cpgz", // Mac archive
|
|
//u".cpio",
|
|
u".cpl", // Windows executable
|
|
u".crt", // Windows signed certificate
|
|
u".crx", // Chrome extensions
|
|
u".csh", // Linux shell
|
|
u".dart", // Mac disk image
|
|
u".dc42", // Apple DiskCopy Image
|
|
u".deb", // Linux package
|
|
u".dex", // Android
|
|
u".diskcopy42", // Apple DiskCopy Image
|
|
u".dll", // Windows executable
|
|
u".dmg", // Mac disk image
|
|
u".dmgpart", // Mac disk image
|
|
//u".docb", // MS Office
|
|
//u".docm", // MS Word
|
|
//u".docx", // MS Word
|
|
//u".dotm", // MS Word
|
|
//u".dott", // MS Office
|
|
u".drv", // Windows driver
|
|
u".dvdr", // Mac Disk image
|
|
u".efi", // Firmware
|
|
u".eml", // MS Outlook
|
|
u".exe", // Windows executable
|
|
//u".fat",
|
|
u".fon", // Windows font
|
|
u".fxp", // MS FoxPro
|
|
u".gadget", // Windows
|
|
u".grp", // Windows
|
|
u".gz", // Linux archive (gzip)
|
|
u".gzip", // Linux archive (gzip)
|
|
u".hfs", // Mac disk image
|
|
u".hlp", // Windows Help
|
|
u".hqx", // Mac archive
|
|
u".hta", // HTML trusted application
|
|
u".htt", // MS HTML template
|
|
u".img", // Mac disk image
|
|
u".imgpart", // Mac disk image
|
|
u".inf", // Windows installer
|
|
u".ini", // Generic config file
|
|
u".ins", // IIS config
|
|
//u".inx", // InstallShield
|
|
u".iso", // CD image
|
|
u".isp", // IIS config
|
|
//u".isu", // InstallShield
|
|
u".jar", // Java
|
|
u".jnlp", // Java
|
|
//u".job", // Windows
|
|
u".js", // JavaScript script
|
|
u".jse", // JScript
|
|
u".ksh", // Linux shell
|
|
//u".lha",
|
|
u".lnk", // Windows
|
|
u".local", // Windows
|
|
//u".lpaq1",
|
|
//u".lpaq5",
|
|
//u".lpaq8",
|
|
//u".lzh",
|
|
//u".lzma",
|
|
u".mad", // MS Access
|
|
u".maf", // MS Access
|
|
u".mag", // MS Access
|
|
u".mam", // MS Access
|
|
u".manifest", // Windows
|
|
u".maq", // MS Access
|
|
u".mar", // MS Access
|
|
u".mas", // MS Access
|
|
u".mat", // MS Access
|
|
u".mau", // Media attachment
|
|
u".mav", // MS Access
|
|
u".maw", // MS Access
|
|
u".mda", // MS Access
|
|
u".mdb", // MS Access
|
|
u".mde", // MS Access
|
|
u".mdt", // MS Access
|
|
u".mdw", // MS Access
|
|
u".mdz", // MS Access
|
|
u".mht", // MS HTML
|
|
u".mhtml", // MS HTML
|
|
u".mim", // MS Mail
|
|
u".mmc", // MS Office
|
|
u".mof", // Windows
|
|
u".mpkg", // Mac installer
|
|
u".msc", // Windows executable
|
|
u".msg", // MS Outlook
|
|
u".msh", // Windows shell
|
|
u".msh1", // Windows shell
|
|
u".msh1xml", // Windows shell
|
|
u".msh2", // Windows shell
|
|
u".msh2xml", // Windows shell
|
|
u".mshxml", // Windows
|
|
u".msi", // Windows installer
|
|
u".msp", // Windows installer
|
|
u".mst", // Windows installer
|
|
u".ndif", // Mac disk image
|
|
//u".ntfs", // 7z
|
|
u".ocx", // ActiveX
|
|
u".ops", // MS Office
|
|
//u".out", // Linux binary
|
|
//u".paf", // PortableApps package
|
|
//u".paq8f",
|
|
//u".paq8jd",
|
|
//u".paq8l",
|
|
//u".paq8o",
|
|
u".partial", // Downloads
|
|
u".pax", // Mac archive
|
|
u".pcd", // Microsoft Visual Test
|
|
u".pdf", // Adobe Acrobat
|
|
//u".pea",
|
|
u".pet", // Linux package
|
|
u".pif", // Windows
|
|
u".pkg", // Mac installer
|
|
u".pl", // Perl script
|
|
u".plg", // MS Visual Studio
|
|
//u".potx", // MS PowerPoint
|
|
//u".ppam", // MS PowerPoint
|
|
//u".ppsx", // MS PowerPoint
|
|
//u".pptm", // MS PowerPoint
|
|
//u".pptx", // MS PowerPoint
|
|
u".prf", // MS Outlook
|
|
u".prg", // Windows
|
|
u".ps1", // Windows shell
|
|
u".ps1xml", // Windows shell
|
|
u".ps2", // Windows shell
|
|
u".ps2xml", // Windows shell
|
|
u".psc1", // Windows shell
|
|
u".psc2", // Windows shell
|
|
u".pst", // MS Outlook
|
|
u".pup", // Linux package
|
|
u".py", // Python script
|
|
u".pyc", // Python binary
|
|
u".pyw", // Python GUI
|
|
//u".quad",
|
|
//u".r00",
|
|
//u".r01",
|
|
//u".r02",
|
|
//u".r03",
|
|
//u".r04",
|
|
//u".r05",
|
|
//u".r06",
|
|
//u".r07",
|
|
//u".r08",
|
|
//u".r09",
|
|
//u".r10",
|
|
//u".r11",
|
|
//u".r12",
|
|
//u".r13",
|
|
//u".r14",
|
|
//u".r15",
|
|
//u".r16",
|
|
//u".r17",
|
|
//u".r18",
|
|
//u".r19",
|
|
//u".r20",
|
|
//u".r21",
|
|
//u".r22",
|
|
//u".r23",
|
|
//u".r24",
|
|
//u".r25",
|
|
//u".r26",
|
|
//u".r27",
|
|
//u".r28",
|
|
//u".r29",
|
|
//u".rar",
|
|
u".rb", // Ruby script
|
|
u".reg", // Windows Registry
|
|
u".rels", // MS Office
|
|
//u".rgs", // Windows Registry
|
|
u".rpm", // Linux package
|
|
//u".rtf", // MS Office
|
|
//u".run", // Linux shell
|
|
u".scf", // Windows shell
|
|
u".scr", // Windows
|
|
u".sct", // Windows shell
|
|
u".search-ms", // Windows
|
|
u".sh", // Linux shell
|
|
u".shar", // Linux shell
|
|
u".shb", // Windows
|
|
u".shs", // Windows shell
|
|
//u".sldm", // MS PowerPoint
|
|
//u".sldx", // MS PowerPoint
|
|
u".slp", // Linux package
|
|
u".smi", // Mac disk image
|
|
u".sparsebundle", // Mac disk image
|
|
u".sparseimage", // Mac disk image
|
|
u".spl", // Adobe Flash
|
|
//u".squashfs",
|
|
u".svg",
|
|
u".swf", // Adobe Flash
|
|
u".swm", // Windows Imaging
|
|
u".sys", // Windows
|
|
u".tar", // Linux archive
|
|
u".taz", // Linux archive (bzip2)
|
|
u".tbz", // Linux archive (bzip2)
|
|
u".tbz2", // Linux archive (bzip2)
|
|
u".tcsh", // Linux shell
|
|
u".tgz", // Linux archive (gzip)
|
|
//u".toast", // Roxio disk image
|
|
//u".torrent", // Bittorrent
|
|
u".tpz", // Linux archive (gzip)
|
|
u".txz", // Linux archive (xz)
|
|
u".tz", // Linux archive (gzip)
|
|
//u".u3p", // U3 Smart Apps
|
|
u".udf", // MS Excel
|
|
u".udif", // Mac disk image
|
|
u".url", // Windows
|
|
//u".uu",
|
|
//u".uue",
|
|
u".vb", // Visual Basic script
|
|
u".vbe", // Visual Basic script
|
|
u".vbs", // Visual Basic script
|
|
//u".vbscript", // Visual Basic script
|
|
u".vhd", // Windows virtual hard drive
|
|
u".vhdx", // Windows virtual hard drive
|
|
u".vmdk", // VMware virtual disk
|
|
u".vsd", // MS Visio
|
|
u".vsmacros", // MS Visual Studio
|
|
u".vss", // MS Visio
|
|
u".vst", // MS Visio
|
|
u".vsw", // MS Visio
|
|
u".website", // Windows
|
|
u".wim", // Windows Imaging
|
|
//u".workflow", // Mac Automator
|
|
//u".wrc", // FreeArc archive
|
|
u".ws", // Windows script
|
|
u".wsc", // Windows script
|
|
u".wsf", // Windows script
|
|
u".wsh", // Windows script
|
|
u".xar", // MS Excel
|
|
u".xbap", // XAML Browser Application
|
|
u".xip", // Mac archive
|
|
//u".xlsm", // MS Excel
|
|
//u".xlsx", // MS Excel
|
|
//u".xltm", // MS Excel
|
|
//u".xltx", // MS Excel
|
|
u".xml",
|
|
u".xnk", // MS Exchange
|
|
u".xrm-ms", // Windows
|
|
u".xsl", // XML Stylesheet
|
|
//u".xxe",
|
|
u".xz", // Linux archive (xz)
|
|
u".z", // InstallShield
|
|
#ifdef XP_WIN // disable on Mac/Linux, see 1167493
|
|
u".zip", // Generic archive
|
|
#endif
|
|
u".zipx", // WinZip
|
|
//u".zpaq",
|
|
};
|
|
|
|
bool
|
|
PendingLookup::IsBinaryFile()
|
|
{
|
|
nsString fileName;
|
|
nsresult rv = mQuery->GetSuggestedFileName(fileName);
|
|
if (NS_FAILED(rv)) {
|
|
LOG(("No suggested filename [this = %p]", this));
|
|
return false;
|
|
}
|
|
LOG(("Suggested filename: %s [this = %p]",
|
|
NS_ConvertUTF16toUTF8(fileName).get(), this));
|
|
|
|
for (size_t i = 0; i < ArrayLength(kBinaryFileExtensions); ++i) {
|
|
if (StringEndsWith(fileName, nsDependentString(kBinaryFileExtensions[i]))) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ClientDownloadRequest::DownloadType
|
|
PendingLookup::GetDownloadType(const nsAString& aFilename) {
|
|
MOZ_ASSERT(IsBinaryFile());
|
|
|
|
// From https://cs.chromium.org/chromium/src/chrome/common/safe_browsing/download_protection_util.cc?l=17
|
|
if (StringEndsWith(aFilename, NS_LITERAL_STRING(".zip"))) {
|
|
return ClientDownloadRequest::ZIPPED_EXECUTABLE;
|
|
} else if (StringEndsWith(aFilename, NS_LITERAL_STRING(".apk"))) {
|
|
return ClientDownloadRequest::ANDROID_APK;
|
|
} else if (StringEndsWith(aFilename, NS_LITERAL_STRING(".app")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".cdr")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".dart")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".dc42")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".diskcopy42")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".dmg")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".dmgpart")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".dvdr")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".img")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".imgpart")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".iso")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".mpkg")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".ndif")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".pkg")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".smi")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".sparsebundle")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".sparseimage")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".toast")) ||
|
|
StringEndsWith(aFilename, NS_LITERAL_STRING(".udif"))) {
|
|
return ClientDownloadRequest::MAC_EXECUTABLE;
|
|
}
|
|
|
|
return ClientDownloadRequest::WIN_EXECUTABLE; // default to Windows binaries
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::LookupNext()
|
|
{
|
|
// We must call LookupNext or SendRemoteQuery upon return.
|
|
// Look up all of the URLs that could allow or block this download.
|
|
// Blocklist first.
|
|
if (mBlocklistCount > 0) {
|
|
return OnComplete(true, NS_OK,
|
|
nsIApplicationReputationService::VERDICT_DANGEROUS);
|
|
}
|
|
int index = mAnylistSpecs.Length() - 1;
|
|
nsCString spec;
|
|
if (index >= 0) {
|
|
// Check the source URI, referrer and redirect chain.
|
|
spec = mAnylistSpecs[index];
|
|
mAnylistSpecs.RemoveElementAt(index);
|
|
RefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
|
|
return lookup->LookupSpec(spec, false);
|
|
}
|
|
// If any of mAnylistSpecs matched the blocklist, go ahead and block.
|
|
if (mBlocklistCount > 0) {
|
|
return OnComplete(true, NS_OK,
|
|
nsIApplicationReputationService::VERDICT_DANGEROUS);
|
|
}
|
|
// If any of mAnylistSpecs matched the allowlist, go ahead and pass.
|
|
if (mAllowlistCount > 0) {
|
|
return OnComplete(false, NS_OK);
|
|
}
|
|
// Only binary signatures remain.
|
|
index = mAllowlistSpecs.Length() - 1;
|
|
if (index >= 0) {
|
|
spec = mAllowlistSpecs[index];
|
|
LOG(("PendingLookup::LookupNext: checking %s on allowlist", spec.get()));
|
|
mAllowlistSpecs.RemoveElementAt(index);
|
|
RefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
|
|
return lookup->LookupSpec(spec, true);
|
|
}
|
|
// There are no more URIs to check against local list. If the file is
|
|
// not eligible for remote lookup, bail.
|
|
if (!IsBinaryFile()) {
|
|
LOG(("Not eligible for remote lookups [this=%p]", this));
|
|
return OnComplete(false, NS_OK);
|
|
}
|
|
nsresult rv = SendRemoteQuery();
|
|
if (NS_FAILED(rv)) {
|
|
return OnComplete(false, rv);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCString
|
|
PendingLookup::EscapeCertificateAttribute(const nsACString& aAttribute)
|
|
{
|
|
// Escape '/' because it's a field separator, and '%' because Chrome does
|
|
nsCString escaped;
|
|
escaped.SetCapacity(aAttribute.Length());
|
|
for (unsigned int i = 0; i < aAttribute.Length(); ++i) {
|
|
if (aAttribute.Data()[i] == '%') {
|
|
escaped.AppendLiteral("%25");
|
|
} else if (aAttribute.Data()[i] == '/') {
|
|
escaped.AppendLiteral("%2F");
|
|
} else if (aAttribute.Data()[i] == ' ') {
|
|
escaped.AppendLiteral("%20");
|
|
} else {
|
|
escaped.Append(aAttribute.Data()[i]);
|
|
}
|
|
}
|
|
return escaped;
|
|
}
|
|
|
|
nsCString
|
|
PendingLookup::EscapeFingerprint(const nsACString& aFingerprint)
|
|
{
|
|
// Google's fingerprint doesn't have colons
|
|
nsCString escaped;
|
|
escaped.SetCapacity(aFingerprint.Length());
|
|
for (unsigned int i = 0; i < aFingerprint.Length(); ++i) {
|
|
if (aFingerprint.Data()[i] != ':') {
|
|
escaped.Append(aFingerprint.Data()[i]);
|
|
}
|
|
}
|
|
return escaped;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::GenerateWhitelistStringsForPair(
|
|
nsIX509Cert* certificate,
|
|
nsIX509Cert* issuer)
|
|
{
|
|
// The whitelist paths have format:
|
|
// http://sb-ssl.google.com/safebrowsing/csd/certificate/<issuer_cert_fingerprint>[/CN=<cn>][/O=<org>][/OU=<unit>]
|
|
// Any of CN, O, or OU may be omitted from the whitelist entry. Unfortunately
|
|
// this is not publicly documented, but the Chrome implementation can be found
|
|
// here:
|
|
// https://code.google.com/p/chromium/codesearch#search/&q=GetCertificateWhitelistStrings
|
|
nsCString whitelistString(
|
|
"http://sb-ssl.google.com/safebrowsing/csd/certificate/");
|
|
|
|
nsString fingerprint;
|
|
nsresult rv = issuer->GetSha1Fingerprint(fingerprint);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
whitelistString.Append(
|
|
EscapeFingerprint(NS_ConvertUTF16toUTF8(fingerprint)));
|
|
|
|
nsString commonName;
|
|
rv = certificate->GetCommonName(commonName);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!commonName.IsEmpty()) {
|
|
whitelistString.AppendLiteral("/CN=");
|
|
whitelistString.Append(
|
|
EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(commonName)));
|
|
}
|
|
|
|
nsString organization;
|
|
rv = certificate->GetOrganization(organization);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!organization.IsEmpty()) {
|
|
whitelistString.AppendLiteral("/O=");
|
|
whitelistString.Append(
|
|
EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organization)));
|
|
}
|
|
|
|
nsString organizationalUnit;
|
|
rv = certificate->GetOrganizationalUnit(organizationalUnit);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!organizationalUnit.IsEmpty()) {
|
|
whitelistString.AppendLiteral("/OU=");
|
|
whitelistString.Append(
|
|
EscapeCertificateAttribute(NS_ConvertUTF16toUTF8(organizationalUnit)));
|
|
}
|
|
LOG(("Whitelisting %s", whitelistString.get()));
|
|
|
|
mAllowlistSpecs.AppendElement(whitelistString);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::GenerateWhitelistStringsForChain(
|
|
const safe_browsing::ClientDownloadRequest_CertificateChain& aChain)
|
|
{
|
|
// We need a signing certificate and an issuer to construct a whitelist
|
|
// entry.
|
|
if (aChain.element_size() < 2) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Get the signer.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIX509Cert> signer;
|
|
nsDependentCSubstring signerDER(
|
|
const_cast<char *>(aChain.element(0).certificate().data()),
|
|
aChain.element(0).certificate().size());
|
|
rv = certDB->ConstructX509(signerDER, getter_AddRefs(signer));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for (int i = 1; i < aChain.element_size(); ++i) {
|
|
// Get the issuer.
|
|
nsCOMPtr<nsIX509Cert> issuer;
|
|
nsDependentCSubstring issuerDER(
|
|
const_cast<char *>(aChain.element(i).certificate().data()),
|
|
aChain.element(i).certificate().size());
|
|
rv = certDB->ConstructX509(issuerDER, getter_AddRefs(issuer));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = GenerateWhitelistStringsForPair(signer, issuer);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::GenerateWhitelistStrings()
|
|
{
|
|
for (int i = 0; i < mRequest.signature().certificate_chain_size(); ++i) {
|
|
nsresult rv = GenerateWhitelistStringsForChain(
|
|
mRequest.signature().certificate_chain(i));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::AddRedirects(nsIArray* aRedirects)
|
|
{
|
|
uint32_t length = 0;
|
|
aRedirects->GetLength(&length);
|
|
LOG(("ApplicationReputation: Got %u redirects", length));
|
|
nsCOMPtr<nsISimpleEnumerator> iter;
|
|
nsresult rv = aRedirects->Enumerate(getter_AddRefs(iter));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool hasMoreRedirects = false;
|
|
rv = iter->HasMoreElements(&hasMoreRedirects);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
while (hasMoreRedirects) {
|
|
nsCOMPtr<nsISupports> supports;
|
|
rv = iter->GetNext(getter_AddRefs(supports));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(supports, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
rv = principal->GetURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Add the spec to our list of local lookups. The most recent redirect is
|
|
// the last element.
|
|
nsCString spec;
|
|
rv = GetStrippedSpec(uri, spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mAnylistSpecs.AppendElement(spec);
|
|
LOG(("ApplicationReputation: Appending redirect %s\n", spec.get()));
|
|
|
|
// Store the redirect information in the remote request.
|
|
ClientDownloadRequest_Resource* resource = mRequest.add_resources();
|
|
resource->set_url(spec.get());
|
|
resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
|
|
|
|
rv = iter->HasMoreElements(&hasMoreRedirects);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::StartLookup()
|
|
{
|
|
mStartTime = TimeStamp::Now();
|
|
nsresult rv = DoLookupInternal();
|
|
if (NS_FAILED(rv)) {
|
|
return OnComplete(false, NS_OK);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::GetStrippedSpec(nsIURI* aUri, nsACString& escaped)
|
|
{
|
|
// If aURI is not an nsIURL, we do not want to check the lists or send a
|
|
// remote query.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = url->GetScheme(escaped);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCString temp;
|
|
rv = url->GetHostPort(temp);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
escaped.Append("://");
|
|
escaped.Append(temp);
|
|
|
|
rv = url->GetFilePath(temp);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// nsIUrl.filePath starts with '/'
|
|
escaped.Append(temp);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::DoLookupInternal()
|
|
{
|
|
// We want to check the target URI, its referrer, and associated redirects
|
|
// against the local lists.
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = mQuery->GetSourceURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCString sourceSpec;
|
|
rv = GetStrippedSpec(uri, sourceSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mAnylistSpecs.AppendElement(sourceSpec);
|
|
|
|
ClientDownloadRequest_Resource* resource = mRequest.add_resources();
|
|
resource->set_url(sourceSpec.get());
|
|
resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
|
|
|
|
nsCOMPtr<nsIURI> referrer = nullptr;
|
|
rv = mQuery->GetReferrerURI(getter_AddRefs(referrer));
|
|
if (referrer) {
|
|
nsCString referrerSpec;
|
|
rv = GetStrippedSpec(referrer, referrerSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mAnylistSpecs.AppendElement(referrerSpec);
|
|
resource->set_referrer(referrerSpec.get());
|
|
}
|
|
nsCOMPtr<nsIArray> redirects;
|
|
rv = mQuery->GetRedirects(getter_AddRefs(redirects));
|
|
if (redirects) {
|
|
AddRedirects(redirects);
|
|
} else {
|
|
LOG(("ApplicationReputation: Got no redirects [this=%p]", this));
|
|
}
|
|
|
|
// Extract the signature and parse certificates so we can use it to check
|
|
// whitelists.
|
|
nsCOMPtr<nsIArray> sigArray;
|
|
rv = mQuery->GetSignatureInfo(getter_AddRefs(sigArray));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (sigArray) {
|
|
rv = ParseCertificates(sigArray);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
rv = GenerateWhitelistStrings();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Start the call chain.
|
|
return LookupNext();
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::OnComplete(bool shouldBlock, nsresult rv, uint32_t verdict)
|
|
{
|
|
MOZ_ASSERT(!shouldBlock ||
|
|
verdict != nsIApplicationReputationService::VERDICT_SAFE);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
nsAutoCString errorName;
|
|
mozilla::GetErrorName(rv, errorName);
|
|
LOG(("Failed sending remote query for application reputation "
|
|
"[rv = %s, this = %p]", errorName.get(), this));
|
|
}
|
|
|
|
if (mTimeoutTimer) {
|
|
mTimeoutTimer->Cancel();
|
|
mTimeoutTimer = nullptr;
|
|
}
|
|
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
|
|
shouldBlock);
|
|
double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
|
|
LOG(("Application Reputation verdict is %u, obtained in %f ms [this = %p]",
|
|
verdict, t, this));
|
|
if (shouldBlock) {
|
|
LOG(("Application Reputation check failed, blocking bad binary [this = %p]",
|
|
this));
|
|
} else {
|
|
LOG(("Application Reputation check passed [this = %p]", this));
|
|
}
|
|
nsresult res = mCallback->OnComplete(shouldBlock, rv, verdict);
|
|
return res;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::ParseCertificates(nsIArray* aSigArray)
|
|
{
|
|
// If we haven't been set for any reason, bail.
|
|
NS_ENSURE_ARG_POINTER(aSigArray);
|
|
|
|
// Binaries may be signed by multiple chains of certificates. If there are no
|
|
// chains, the binary is unsigned (or we were unable to extract signature
|
|
// information on a non-Windows platform)
|
|
nsCOMPtr<nsISimpleEnumerator> chains;
|
|
nsresult rv = aSigArray->Enumerate(getter_AddRefs(chains));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool hasMoreChains = false;
|
|
rv = chains->HasMoreElements(&hasMoreChains);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
while (hasMoreChains) {
|
|
nsCOMPtr<nsISupports> chainSupports;
|
|
rv = chains->GetNext(getter_AddRefs(chainSupports));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIX509CertList> certList = do_QueryInterface(chainSupports, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
safe_browsing::ClientDownloadRequest_CertificateChain* certChain =
|
|
mRequest.mutable_signature()->add_certificate_chain();
|
|
nsCOMPtr<nsISimpleEnumerator> chainElt;
|
|
rv = certList->GetEnumerator(getter_AddRefs(chainElt));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Each chain may have multiple certificates.
|
|
bool hasMoreCerts = false;
|
|
rv = chainElt->HasMoreElements(&hasMoreCerts);
|
|
while (hasMoreCerts) {
|
|
nsCOMPtr<nsISupports> certSupports;
|
|
rv = chainElt->GetNext(getter_AddRefs(certSupports));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certSupports, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
uint8_t* data = nullptr;
|
|
uint32_t len = 0;
|
|
rv = cert->GetRawDER(&len, &data);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Add this certificate to the protobuf to send remotely.
|
|
certChain->add_element()->set_certificate(data, len);
|
|
free(data);
|
|
|
|
rv = chainElt->HasMoreElements(&hasMoreCerts);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
rv = chains->HasMoreElements(&hasMoreChains);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
if (mRequest.signature().certificate_chain_size() > 0) {
|
|
mRequest.mutable_signature()->set_trusted(true);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::SendRemoteQuery()
|
|
{
|
|
nsresult rv = SendRemoteQueryInternal();
|
|
if (NS_FAILED(rv)) {
|
|
return OnComplete(false, rv);
|
|
}
|
|
// SendRemoteQueryInternal has fired off the query and we call OnComplete in
|
|
// the nsIStreamListener.onStopRequest.
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::SendRemoteQueryInternal()
|
|
{
|
|
// If we aren't supposed to do remote lookups, bail.
|
|
if (!Preferences::GetBool(PREF_SB_DOWNLOADS_REMOTE_ENABLED, false)) {
|
|
LOG(("Remote lookups are disabled [this = %p]", this));
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
// If the remote lookup URL is empty or absent, bail.
|
|
nsCString serviceUrl;
|
|
NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
|
|
NS_ERROR_NOT_AVAILABLE);
|
|
if (serviceUrl.IsEmpty()) {
|
|
LOG(("Remote lookup URL is empty [this = %p]", this));
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
// If the blocklist or allowlist is empty (so we couldn't do local lookups),
|
|
// bail
|
|
{
|
|
nsAutoCString table;
|
|
NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE,
|
|
&table),
|
|
NS_ERROR_NOT_AVAILABLE);
|
|
if (table.IsEmpty()) {
|
|
LOG(("Blocklist is empty [this = %p]", this));
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
}
|
|
#ifdef XP_WIN
|
|
// The allowlist is only needed to do signature verification on Windows
|
|
{
|
|
nsAutoCString table;
|
|
NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE,
|
|
&table),
|
|
NS_ERROR_NOT_AVAILABLE);
|
|
if (table.IsEmpty()) {
|
|
LOG(("Allowlist is empty [this = %p]", this));
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LOG(("Sending remote query for application reputation [this = %p]",
|
|
this));
|
|
// We did not find a local result, so fire off the query to the
|
|
// application reputation service.
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv;
|
|
rv = mQuery->GetSourceURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCString spec;
|
|
rv = GetStrippedSpec(uri, spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mRequest.set_url(spec.get());
|
|
|
|
uint32_t fileSize;
|
|
rv = mQuery->GetFileSize(&fileSize);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mRequest.set_length(fileSize);
|
|
// We have no way of knowing whether or not a user initiated the
|
|
// download. Set it to true to lessen the chance of false positives.
|
|
mRequest.set_user_initiated(true);
|
|
|
|
nsCString locale;
|
|
rv = LocaleService::GetInstance()->GetAppLocaleAsLangTag(locale);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mRequest.set_locale(locale.get());
|
|
nsCString sha256Hash;
|
|
rv = mQuery->GetSha256Hash(sha256Hash);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mRequest.mutable_digests()->set_sha256(sha256Hash.Data());
|
|
nsString fileName;
|
|
rv = mQuery->GetSuggestedFileName(fileName);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mRequest.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
|
|
mRequest.set_download_type(GetDownloadType(fileName));
|
|
|
|
if (mRequest.signature().trusted()) {
|
|
LOG(("Got signed binary for remote application reputation check "
|
|
"[this = %p]", this));
|
|
} else {
|
|
LOG(("Got unsigned binary for remote application reputation check "
|
|
"[this = %p]", this));
|
|
}
|
|
|
|
// Serialize the protocol buffer to a string. This can only fail if we are
|
|
// out of memory, or if the protocol buffer req is missing required fields
|
|
// (only the URL for now).
|
|
std::string serialized;
|
|
if (!mRequest.SerializeToString(&serialized)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
LOG(("Serialized protocol buffer [this = %p]: (length=%" PRIuSIZE ") %s", this,
|
|
serialized.length(), serialized.c_str()));
|
|
|
|
// Set the input stream to the serialized protocol buffer
|
|
nsCOMPtr<nsIStringInputStream> sstream =
|
|
do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = sstream->SetData(serialized.c_str(), serialized.length());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Set up the channel to transmit the request to the service.
|
|
nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
|
|
rv = ios->NewChannel2(serviceUrl,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr, // aLoadingNode
|
|
nsContentUtils::GetSystemPrincipal(),
|
|
nullptr, // aTriggeringPrincipal
|
|
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
|
nsIContentPolicy::TYPE_OTHER,
|
|
getter_AddRefs(mChannel));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
|
|
if (loadInfo) {
|
|
mozilla::OriginAttributes attrs;
|
|
attrs.mFirstPartyDomain.AssignLiteral(NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN);
|
|
loadInfo->SetOriginAttributes(attrs);
|
|
}
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel, &rv));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mozilla::Unused << httpChannel;
|
|
|
|
// Upload the protobuf to the application reputation service.
|
|
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(mChannel, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = uploadChannel->ExplicitSetUploadStream(sstream,
|
|
NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
|
|
NS_LITERAL_CSTRING("POST"), false);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
uint32_t timeoutMs = Preferences::GetUint(PREF_SB_DOWNLOADS_REMOTE_TIMEOUT, 10000);
|
|
mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
|
mTimeoutTimer->InitWithCallback(this, timeoutMs, nsITimer::TYPE_ONE_SHOT);
|
|
|
|
rv = mChannel->AsyncOpen2(this);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PendingLookup::Notify(nsITimer* aTimer)
|
|
{
|
|
LOG(("Remote lookup timed out [this = %p]", this));
|
|
MOZ_ASSERT(aTimer == mTimeoutTimer);
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_REMOTE_LOOKUP_TIMEOUT,
|
|
true);
|
|
mChannel->Cancel(NS_ERROR_NET_TIMEOUT);
|
|
mTimeoutTimer->Cancel();
|
|
return NS_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// nsIObserver implementation
|
|
NS_IMETHODIMP
|
|
PendingLookup::Observe(nsISupports *aSubject, const char *aTopic,
|
|
const char16_t *aData)
|
|
{
|
|
if (!strcmp(aTopic, "quit-application")) {
|
|
if (mTimeoutTimer) {
|
|
mTimeoutTimer->Cancel();
|
|
mTimeoutTimer = nullptr;
|
|
}
|
|
if (mChannel) {
|
|
mChannel->Cancel(NS_ERROR_ABORT);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// nsIStreamListener
|
|
static nsresult
|
|
AppendSegmentToString(nsIInputStream* inputStream,
|
|
void *closure,
|
|
const char *rawSegment,
|
|
uint32_t toOffset,
|
|
uint32_t count,
|
|
uint32_t *writeCount) {
|
|
nsAutoCString* decodedData = static_cast<nsAutoCString*>(closure);
|
|
decodedData->Append(rawSegment, count);
|
|
*writeCount = count;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PendingLookup::OnDataAvailable(nsIRequest *aRequest,
|
|
nsISupports *aContext,
|
|
nsIInputStream *aStream,
|
|
uint64_t offset,
|
|
uint32_t count) {
|
|
uint32_t read;
|
|
return aStream->ReadSegments(AppendSegmentToString, &mResponse, count, &read);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PendingLookup::OnStartRequest(nsIRequest *aRequest,
|
|
nsISupports *aContext) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PendingLookup::OnStopRequest(nsIRequest *aRequest,
|
|
nsISupports *aContext,
|
|
nsresult aResult) {
|
|
NS_ENSURE_STATE(mCallback);
|
|
|
|
bool shouldBlock = false;
|
|
uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE;
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_REMOTE_LOOKUP_TIMEOUT,
|
|
false);
|
|
|
|
nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
|
|
&shouldBlock, &verdict);
|
|
OnComplete(shouldBlock, rv, verdict);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
|
|
nsISupports *aContext,
|
|
nsresult aResult,
|
|
bool* aShouldBlock,
|
|
uint32_t* aVerdict) {
|
|
if (NS_FAILED(aResult)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_FAILED);
|
|
return aResult;
|
|
}
|
|
|
|
*aShouldBlock = false;
|
|
*aVerdict = nsIApplicationReputationService::VERDICT_SAFE;
|
|
nsresult rv;
|
|
nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_FAILED);
|
|
return rv;
|
|
}
|
|
|
|
uint32_t status = 0;
|
|
rv = channel->GetResponseStatus(&status);
|
|
if (NS_FAILED(rv)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_FAILED);
|
|
return rv;
|
|
}
|
|
|
|
if (status != 200) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_FAILED);
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
std::string buf(mResponse.Data(), mResponse.Length());
|
|
safe_browsing::ClientDownloadResponse response;
|
|
if (!response.ParseFromString(buf)) {
|
|
LOG(("Invalid protocol buffer response [this = %p]: %s", this, buf.c_str()));
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_INVALID);
|
|
return NS_ERROR_CANNOT_CONVERT_DATA;
|
|
}
|
|
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
|
|
SERVER_RESPONSE_VALID);
|
|
// Clamp responses 0-7, we only know about 0-4 for now.
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER_VERDICT,
|
|
std::min<uint32_t>(response.verdict(), 7));
|
|
switch(response.verdict()) {
|
|
case safe_browsing::ClientDownloadResponse::DANGEROUS:
|
|
*aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS, true);
|
|
*aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS;
|
|
break;
|
|
case safe_browsing::ClientDownloadResponse::DANGEROUS_HOST:
|
|
*aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS_HOST, true);
|
|
*aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS_HOST;
|
|
break;
|
|
case safe_browsing::ClientDownloadResponse::POTENTIALLY_UNWANTED:
|
|
*aShouldBlock = Preferences::GetBool(PREF_BLOCK_POTENTIALLY_UNWANTED, false);
|
|
*aVerdict = nsIApplicationReputationService::VERDICT_POTENTIALLY_UNWANTED;
|
|
break;
|
|
case safe_browsing::ClientDownloadResponse::UNCOMMON:
|
|
*aShouldBlock = Preferences::GetBool(PREF_BLOCK_UNCOMMON, false);
|
|
*aVerdict = nsIApplicationReputationService::VERDICT_UNCOMMON;
|
|
break;
|
|
default:
|
|
// Treat everything else as safe
|
|
break;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(ApplicationReputationService,
|
|
nsIApplicationReputationService)
|
|
|
|
ApplicationReputationService*
|
|
ApplicationReputationService::gApplicationReputationService = nullptr;
|
|
|
|
ApplicationReputationService*
|
|
ApplicationReputationService::GetSingleton()
|
|
{
|
|
if (gApplicationReputationService) {
|
|
NS_ADDREF(gApplicationReputationService);
|
|
return gApplicationReputationService;
|
|
}
|
|
|
|
// We're not initialized yet.
|
|
gApplicationReputationService = new ApplicationReputationService();
|
|
if (gApplicationReputationService) {
|
|
NS_ADDREF(gApplicationReputationService);
|
|
}
|
|
|
|
return gApplicationReputationService;
|
|
}
|
|
|
|
ApplicationReputationService::ApplicationReputationService()
|
|
{
|
|
LOG(("Application reputation service started up"));
|
|
}
|
|
|
|
ApplicationReputationService::~ApplicationReputationService() {
|
|
LOG(("Application reputation service shutting down"));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ApplicationReputationService::QueryReputation(
|
|
nsIApplicationReputationQuery* aQuery,
|
|
nsIApplicationReputationCallback* aCallback) {
|
|
LOG(("Starting application reputation check [query=%p]", aQuery));
|
|
NS_ENSURE_ARG_POINTER(aQuery);
|
|
NS_ENSURE_ARG_POINTER(aCallback);
|
|
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
|
|
nsresult rv = QueryReputationInternal(aQuery, aCallback);
|
|
if (NS_FAILED(rv)) {
|
|
Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
|
|
false);
|
|
aCallback->OnComplete(false, rv,
|
|
nsIApplicationReputationService::VERDICT_SAFE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult ApplicationReputationService::QueryReputationInternal(
|
|
nsIApplicationReputationQuery* aQuery,
|
|
nsIApplicationReputationCallback* aCallback) {
|
|
nsresult rv;
|
|
// If malware checks aren't enabled, don't query application reputation.
|
|
if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
if (!Preferences::GetBool(PREF_SB_DOWNLOADS_ENABLED, false)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
rv = aQuery->GetSourceURI(getter_AddRefs(uri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
// Bail if the URI hasn't been set.
|
|
NS_ENSURE_STATE(uri);
|
|
|
|
// Create a new pending lookup and start the call chain.
|
|
RefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
|
|
NS_ENSURE_STATE(lookup);
|
|
|
|
// Add an observer for shutdown
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (!observerService) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
observerService->AddObserver(lookup, "quit-application", true);
|
|
return lookup->StartLookup();
|
|
}
|