ApplicationReputation.cpp 51 KB

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