12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "nsICryptoHash.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/TimeStamp.h"
- #include "nsAutoPtr.h"
- #include "nsCOMPtr.h"
- #include "nsDebug.h"
- #include "nsError.h"
- #include "nsNetCID.h"
- #include "nsReadableUtils.h"
- #include "nsServiceManagerUtils.h"
- #include "nsString.h"
- #include "nsTArray.h"
- #include "nsThreadUtils.h"
- #include "nsXPCOMStrings.h"
- #include "nsIContentPolicy.h"
- #include "nsILoadInfo.h"
- #include "nsContentUtils.h"
- using mozilla::ArrayLength;
- using mozilla::BasePrincipal;
- using mozilla::DocShellOriginAttributes;
- using mozilla::PrincipalOriginAttributes;
- using mozilla::Preferences;
- using mozilla::TimeStamp;
- 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_GENERAL_LOCALE "general.useragent.locale"
- #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)
- 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:
- 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;
- // 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);
- // Return the hex-encoded hash of the whole URI.
- nsresult GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash);
- // Strip url parameters, fragments, and user@pass fields from the URI spec
- // using nsIURL. Hash data URIs and return blob URIs unfiltered.
- 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)) {
- nsAutoCString errorName;
- mozilla::GetErrorName(rv, errorName);
- LOG(("Error in LookupSpecInternal() [rv = %s, this = %p]",
- errorName.get(), this));
- return mPendingLookup->LookupNext(); // ignore this lookup and move to next
- }
- // 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);
- PrincipalOriginAttributes 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.
- nsAutoCString blockList;
- Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
- if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
- mPendingLookup->mBlocklistCount++;
- LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
- return mPendingLookup->OnComplete(true, NS_OK,
- nsIApplicationReputationService::VERDICT_DANGEROUS);
- }
- nsAutoCString allowList;
- Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
- if (FindInReadable(allowList, tables)) {
- mPendingLookup->mAllowlistCount++;
- 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));
- }
- return mPendingLookup->LookupNext();
- }
- NS_IMPL_ISUPPORTS(PendingLookup,
- nsIStreamListener,
- nsIRequestObserver,
- nsIObserver)
- 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* 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=%x]", 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;
- rv = certDB->ConstructX509(
- const_cast<char *>(aChain.element(0).certificate().data()),
- aChain.element(0).certificate().size(), getter_AddRefs(signer));
- NS_ENSURE_SUCCESS(rv, rv);
- for (int i = 1; i < aChain.element_size(); ++i) {
- // Get the issuer.
- nsCOMPtr<nsIX509Cert> issuer;
- rv = certDB->ConstructX509(
- const_cast<char *>(aChain.element(i).certificate().data()),
- aChain.element(i).certificate().size(), 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::GetSpecHash(nsACString& aSpec, nsACString& hexEncodedHash)
- {
- nsresult rv;
- nsCOMPtr<nsICryptoHash> cryptoHash =
- do_CreateInstance("@mozilla.org/security/hash;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = cryptoHash->Init(nsICryptoHash::SHA256);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = cryptoHash->Update(reinterpret_cast<const uint8_t*>(aSpec.BeginReading()),
- aSpec.Length());
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString binaryHash;
- rv = cryptoHash->Finish(false, binaryHash);
- NS_ENSURE_SUCCESS(rv, rv);
- // This needs to match HexEncode() in Chrome's
- // src/base/strings/string_number_conversions.cc
- static const char* const hex = "0123456789ABCDEF";
- hexEncodedHash.SetCapacity(2 * binaryHash.Length());
- for (size_t i = 0; i < binaryHash.Length(); ++i) {
- auto c = static_cast<unsigned char>(binaryHash[i]);
- hexEncodedHash.Append(hex[(c >> 4) & 0x0F]);
- hexEncodedHash.Append(hex[c & 0x0F]);
- }
- return NS_OK;
- }
- nsresult
- PendingLookup::GetStrippedSpec(nsIURI* aUri, nsACString& escaped)
- {
- if (NS_WARN_IF(!aUri)) {
- return NS_ERROR_INVALID_ARG;
- }
- nsresult rv;
- rv = aUri->GetScheme(escaped);
- NS_ENSURE_SUCCESS(rv, rv);
- if (escaped.EqualsLiteral("blob")) {
- aUri->GetSpec(escaped);
- LOG(("PendingLookup::GetStrippedSpec(): blob URL left unstripped as '%s' [this = %p]",
- PromiseFlatCString(escaped).get(), this));
- return NS_OK;
- } else if (escaped.EqualsLiteral("data")) {
- // Replace URI with "data:<everything before comma>,SHA256(<whole URI>)"
- aUri->GetSpec(escaped);
- int32_t comma = escaped.FindChar(',');
- if (comma > -1 &&
- static_cast<nsCString::size_type>(comma) < escaped.Length() - 1) {
- MOZ_ASSERT(comma > 4, "Data URIs start with 'data:'");
- nsAutoCString hexEncodedHash;
- rv = GetSpecHash(escaped, hexEncodedHash);
- if (NS_SUCCEEDED(rv)) {
- escaped.Truncate(comma + 1);
- escaped.Append(hexEncodedHash);
- }
- }
- LOG(("PendingLookup::GetStrippedSpec(): data URL stripped to '%s' [this = %p]",
- PromiseFlatCString(escaped).get(), this));
- return NS_OK;
- }
- // If aURI is not an nsIURL, we do not want to check the lists or send a
- // remote query.
- nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv);
- if (NS_FAILED(rv)) {
- LOG(("PendingLookup::GetStrippedSpec(): scheme '%s' is not supported [this = %p]",
- PromiseFlatCString(escaped).get(), this));
- return 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);
- LOG(("PendingLookup::GetStrippedSpec(): URL stripped to '%s' [this = %p]",
- PromiseFlatCString(escaped).get(), this));
- 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;
- }
- double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
- LOG(("Application Reputation verdict is %lu, 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;
- NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_GENERAL_LOCALE, &locale),
- NS_ERROR_NOT_AVAILABLE);
- 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=%d) %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) {
- loadInfo->SetOriginAttributes(
- mozilla::NeckoOriginAttributes(NECKO_SAFEBROWSING_APP_ID, false));
- }
- 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);
- // Set the Safebrowsing cookie jar, so that the regular Google cookie is not
- // sent with this request. See bug 897516.
- DocShellOriginAttributes attrs;
- attrs.mAppId = NECKO_SAFEBROWSING_APP_ID;
- nsCOMPtr<nsIInterfaceRequestor> loadContext = new mozilla::LoadContext(attrs);
- rv = mChannel->SetNotificationCallbacks(loadContext);
- 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);
- 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;
- 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)) {
- return aResult;
- }
- *aShouldBlock = false;
- *aVerdict = nsIApplicationReputationService::VERDICT_SAFE;
- nsresult rv;
- nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
- if (NS_FAILED(rv)) {
- return rv;
- }
- uint32_t status = 0;
- rv = channel->GetResponseStatus(&status);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (status != 200) {
- 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()));
- return NS_ERROR_CANNOT_CONVERT_DATA;
- }
- 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"));
- MOZ_ASSERT(gApplicationReputationService == this);
- gApplicationReputationService = nullptr;
- }
- 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);
- nsresult rv = QueryReputationInternal(aQuery, aCallback);
- if (NS_FAILED(rv)) {
- 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", false);
- return lookup->StartLookup();
- }
|