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