Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 sts=2 ts=8 et 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 :
7 : #include "nsChannelClassifier.h"
8 :
9 : #include "mozIThirdPartyUtil.h"
10 : #include "nsCharSeparatedTokenizer.h"
11 : #include "nsContentUtils.h"
12 : #include "nsIAddonPolicyService.h"
13 : #include "nsICacheEntry.h"
14 : #include "nsICachingChannel.h"
15 : #include "nsIChannel.h"
16 : #include "nsIClassOfService.h"
17 : #include "nsIDocShell.h"
18 : #include "nsIDocument.h"
19 : #include "nsIDOMDocument.h"
20 : #include "nsIHttpChannel.h"
21 : #include "nsIHttpChannelInternal.h"
22 : #include "nsIIOService.h"
23 : #include "nsIParentChannel.h"
24 : #include "nsIPermissionManager.h"
25 : #include "nsIPrivateBrowsingTrackingProtectionWhitelist.h"
26 : #include "nsIProtocolHandler.h"
27 : #include "nsIScriptError.h"
28 : #include "nsIScriptSecurityManager.h"
29 : #include "nsISecureBrowserUI.h"
30 : #include "nsISecurityEventSink.h"
31 : #include "nsISupportsPriority.h"
32 : #include "nsIURL.h"
33 : #include "nsIWebProgressListener.h"
34 : #include "nsNetUtil.h"
35 : #include "nsPIDOMWindow.h"
36 : #include "nsXULAppAPI.h"
37 : #include "nsQueryObject.h"
38 :
39 : #include "mozilla/ErrorNames.h"
40 : #include "mozilla/Logging.h"
41 : #include "mozilla/Preferences.h"
42 : #include "mozilla/net/HttpBaseChannel.h"
43 : #include "mozilla/ClearOnShutdown.h"
44 : #include "mozilla/Unused.h"
45 :
46 : namespace mozilla {
47 : namespace net {
48 :
49 : //
50 : // MOZ_LOG=nsChannelClassifier:5
51 : //
52 : static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
53 :
54 :
55 : #undef LOG
56 : #define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
57 : #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
58 :
59 : #define URLCLASSIFIER_SKIP_HOSTNAMES "urlclassifier.skipHostnames"
60 : #define URLCLASSIFIER_TRACKING_WHITELIST "urlclassifier.trackingWhitelistTable"
61 : #define URLCLASSIFIER_TRACKING_TABLE "urlclassifier.trackingTable"
62 :
63 : // Put CachedPrefs in anonymous namespace to avoid any collision from outside of
64 : // this file.
65 : namespace {
66 :
67 : /**
68 : * It is not recommended to read from Preference everytime a channel is
69 : * connected.
70 : * That is not fast and we should cache preference values and reuse them
71 : */
72 : class CachedPrefs final
73 : {
74 : public:
75 : static CachedPrefs* GetInstance();
76 :
77 : void Init();
78 0 : bool IsAllowListExample() { return sAllowListExample;}
79 0 : bool IsLowerNetworkPriority() { return sLowerNetworkPriority;}
80 4 : bool IsAnnotateChannelEnabled() { return sAnnotateChannelEnabled;}
81 0 : nsCString GetTrackingWhiteList() { return mTrackingWhitelist; }
82 1 : void SetTrackingWhiteList(const nsACString& aList) { mTrackingWhitelist = aList; }
83 4 : nsCString GetSkipHostnames() { return mSkipHostnames; }
84 1 : void SetSkipHostnames(const nsACString& aHostnames) { mSkipHostnames = aHostnames; }
85 1 : void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; }
86 1 : nsCString GetTrackingBlackList() { return mTrackingBlacklist; }
87 :
88 : private:
89 : friend class StaticAutoPtr<CachedPrefs>;
90 : CachedPrefs();
91 : ~CachedPrefs();
92 :
93 : static void OnPrefsChange(const char* aPrefName, void* );
94 :
95 : // Whether channels should be annotated as being on the tracking protection
96 : // list.
97 : static bool sAnnotateChannelEnabled;
98 : // Whether the priority of the channels annotated as being on the tracking
99 : // protection list should be lowered.
100 : static bool sLowerNetworkPriority;
101 : static bool sAllowListExample;
102 :
103 : nsCString mTrackingWhitelist;
104 : nsCString mSkipHostnames;
105 : nsCString mTrackingBlacklist;
106 :
107 : static StaticAutoPtr<CachedPrefs> sInstance;
108 : };
109 :
110 : bool CachedPrefs::sAllowListExample = false;
111 : bool CachedPrefs::sLowerNetworkPriority = false;
112 : bool CachedPrefs::sAnnotateChannelEnabled = false;
113 :
114 3 : StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance;
115 :
116 : // static
117 : void
118 3 : CachedPrefs::OnPrefsChange(const char* aPref, void* aClosure)
119 : {
120 3 : CachedPrefs* prefs = static_cast<CachedPrefs*> (aClosure);
121 :
122 3 : if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
123 2 : nsCString skipHostnames = Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES);
124 1 : ToLowerCase(skipHostnames);
125 1 : prefs->SetSkipHostnames(skipHostnames);
126 2 : } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
127 2 : nsCString trackingWhitelist = Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST);
128 1 : prefs->SetTrackingWhiteList(trackingWhitelist);
129 1 : } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) {
130 2 : nsCString trackingBlacklist = Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE);
131 1 : prefs->SetTrackingBlackList(trackingBlacklist);
132 : }
133 3 : }
134 :
135 : void
136 1 : CachedPrefs::Init()
137 : {
138 : Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
139 1 : "privacy.trackingprotection.annotate_channels");
140 : Preferences::AddBoolVarCache(&sLowerNetworkPriority,
141 1 : "privacy.trackingprotection.lower_network_priority");
142 : Preferences::AddBoolVarCache(&sAllowListExample,
143 1 : "channelclassifier.allowlist_example");
144 : Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
145 1 : URLCLASSIFIER_SKIP_HOSTNAMES, this);
146 : Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
147 1 : URLCLASSIFIER_TRACKING_WHITELIST, this);
148 : Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange,
149 1 : URLCLASSIFIER_TRACKING_TABLE, this);
150 :
151 1 : }
152 :
153 : // static
154 : CachedPrefs*
155 9 : CachedPrefs::GetInstance()
156 : {
157 9 : if (!sInstance) {
158 1 : sInstance = new CachedPrefs();
159 1 : sInstance->Init();
160 1 : ClearOnShutdown(&sInstance);
161 : }
162 9 : MOZ_ASSERT(sInstance);
163 9 : return sInstance;
164 : }
165 :
166 1 : CachedPrefs::CachedPrefs()
167 : {
168 1 : MOZ_COUNT_CTOR(CachedPrefs);
169 1 : }
170 :
171 0 : CachedPrefs::~CachedPrefs()
172 : {
173 0 : MOZ_COUNT_DTOR(CachedPrefs);
174 :
175 0 : Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this);
176 0 : Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this);
177 0 : Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_TABLE, this);
178 0 : }
179 : } // anonymous namespace
180 :
181 : static void
182 0 : SetIsTrackingResourceHelper(nsIChannel* aChannel)
183 : {
184 0 : MOZ_ASSERT(aChannel);
185 :
186 0 : nsCOMPtr<nsIParentChannel> parentChannel;
187 0 : NS_QueryNotificationCallbacks(aChannel, parentChannel);
188 0 : if (parentChannel) {
189 : // This channel is a parent-process proxy for a child process
190 : // request. We should notify the child process as well.
191 0 : parentChannel->NotifyTrackingResource();
192 : }
193 :
194 0 : RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
195 0 : if (httpChannel) {
196 0 : httpChannel->SetIsTrackingResource();
197 : }
198 0 : }
199 :
200 : static void
201 0 : LowerPriorityHelper(nsIChannel* aChannel)
202 : {
203 0 : MOZ_ASSERT(aChannel);
204 :
205 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel);
206 0 : if (p) {
207 0 : p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
208 : }
209 0 : }
210 :
211 : static void
212 0 : SetThrottleableHelper(nsIChannel* aChannel)
213 : {
214 0 : MOZ_ASSERT(aChannel);
215 :
216 0 : nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(aChannel));
217 0 : if (cos) {
218 0 : cos->AddClassFlags(nsIClassOfService::Throttleable);
219 : }
220 0 : }
221 :
222 621 : NS_IMPL_ISUPPORTS(nsChannelClassifier,
223 : nsIURIClassifierCallback,
224 : nsIObserver)
225 :
226 67 : nsChannelClassifier::nsChannelClassifier(nsIChannel *aChannel)
227 : : mIsAllowListed(false),
228 : mSuspendedChannel(false),
229 : mChannel(aChannel),
230 67 : mTrackingProtectionEnabled(Nothing()),
231 134 : mTrackingAnnotationEnabled(Nothing())
232 : {
233 67 : MOZ_ASSERT(mChannel);
234 67 : }
235 :
236 : bool
237 13 : nsChannelClassifier::ShouldEnableTrackingProtection()
238 : {
239 13 : if (mTrackingProtectionEnabled) {
240 9 : return mTrackingProtectionEnabled.value();
241 : }
242 :
243 4 : mTrackingProtectionEnabled = Some(false);
244 :
245 8 : nsCOMPtr<nsILoadContext> loadContext;
246 4 : NS_QueryNotificationCallbacks(mChannel, loadContext);
247 4 : if (loadContext && loadContext->UseTrackingProtection()) {
248 0 : Unused << ShouldEnableTrackingProtectionInternal(
249 : mChannel, false, mTrackingProtectionEnabled.ptr());
250 : }
251 :
252 4 : return mTrackingProtectionEnabled.value();
253 : }
254 :
255 : bool
256 4 : nsChannelClassifier::ShouldEnableTrackingAnnotation()
257 : {
258 4 : if (mTrackingAnnotationEnabled) {
259 0 : return mTrackingAnnotationEnabled.value();
260 : }
261 :
262 4 : mTrackingAnnotationEnabled = Some(false);
263 :
264 4 : if (!CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) {
265 0 : return mTrackingAnnotationEnabled.value();
266 : }
267 :
268 : // If tracking protection is enabled, no need to do channel annotation.
269 4 : if (ShouldEnableTrackingProtection()) {
270 0 : return mTrackingAnnotationEnabled.value();
271 : }
272 :
273 : // To prevent calling ShouldEnableTrackingProtectionInternal() again,
274 : // check loadContext->UseTrackingProtection() here.
275 : // If loadContext->UseTrackingProtection() is true, here it means
276 : // ShouldEnableTrackingProtectionInternal() has been called before in
277 : // ShouldEnableTrackingProtection() above and the result is false.
278 : // So, we can just return false.
279 8 : nsCOMPtr<nsILoadContext> loadContext;
280 4 : NS_QueryNotificationCallbacks(mChannel, loadContext);
281 4 : if (loadContext && loadContext->UseTrackingProtection()) {
282 0 : return mTrackingAnnotationEnabled.value();
283 : }
284 :
285 4 : Unused << ShouldEnableTrackingProtectionInternal(
286 : mChannel, true, mTrackingAnnotationEnabled.ptr());
287 :
288 4 : return mTrackingAnnotationEnabled.value();
289 : }
290 :
291 : nsresult
292 4 : nsChannelClassifier::ShouldEnableTrackingProtectionInternal(
293 : nsIChannel *aChannel,
294 : bool aAnnotationsOnly,
295 : bool *result)
296 : {
297 : // Should only be called in the parent process.
298 4 : MOZ_ASSERT(XRE_IsParentProcess());
299 :
300 4 : NS_ENSURE_ARG(result);
301 4 : *result = false;
302 :
303 : nsresult rv;
304 : nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
305 8 : do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
306 4 : NS_ENSURE_SUCCESS(rv, rv);
307 :
308 8 : nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
309 4 : if (NS_FAILED(rv) || !chan) {
310 0 : LOG(("nsChannelClassifier[%p]: Not an HTTP channel", this));
311 0 : return NS_OK;
312 : }
313 :
314 8 : nsCOMPtr<nsIURI> topWinURI;
315 4 : rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
316 4 : NS_ENSURE_SUCCESS(rv, rv);
317 :
318 3 : if (!topWinURI) {
319 1 : LOG(("nsChannelClassifier[%p]: No window URI\n", this));
320 : }
321 :
322 6 : nsCOMPtr<nsIURI> chanURI;
323 3 : rv = aChannel->GetURI(getter_AddRefs(chanURI));
324 3 : NS_ENSURE_SUCCESS(rv, rv);
325 :
326 : // Third party checks don't work for chrome:// URIs in mochitests, so just
327 : // default to isThirdParty = true. We check isThirdPartyWindow to expand
328 : // the list of domains that are considered first party (e.g., if
329 : // facebook.com includes an iframe from fatratgames.com, all subsources
330 : // included in that iframe are considered third-party with
331 : // isThirdPartyChannel, even if they are not third-party w.r.t.
332 : // facebook.com), and isThirdPartyChannel to prevent top-level navigations
333 : // from being detected as third-party.
334 3 : bool isThirdPartyChannel = true;
335 3 : bool isThirdPartyWindow = true;
336 3 : thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
337 3 : thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
338 3 : if (!isThirdPartyWindow || !isThirdPartyChannel) {
339 2 : *result = false;
340 2 : if (LOG_ENABLED()) {
341 0 : LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks "
342 : "for first party or top-level load channel[%p] with uri %s",
343 : this, aChannel, chanURI->GetSpecOrDefault().get()));
344 : }
345 2 : return NS_OK;
346 : }
347 :
348 : // Unlike full Tracking Protection, annotations don't block anything
349 : // so we don't need to take into account add-ons or user exceptions.
350 1 : if (aAnnotationsOnly) {
351 1 : *result = true;
352 1 : return NS_OK;
353 : }
354 :
355 0 : if (AddonMayLoad(aChannel, chanURI)) {
356 0 : return NS_OK;
357 : }
358 :
359 0 : nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
360 0 : NS_ENSURE_SUCCESS(rv, rv);
361 :
362 0 : if (!topWinURI && CachedPrefs::GetInstance()->IsAllowListExample()) {
363 0 : LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
364 0 : rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
365 0 : nullptr, nullptr, getter_AddRefs(topWinURI));
366 0 : NS_ENSURE_SUCCESS(rv, rv);
367 : }
368 :
369 : // Take the host/port portion so we can allowlist by site. Also ignore the
370 : // scheme, since users who put sites on the allowlist probably don't expect
371 : // allowlisting to depend on scheme.
372 0 : nsCOMPtr<nsIURL> url = do_QueryInterface(topWinURI, &rv);
373 0 : if (NS_FAILED(rv)) {
374 0 : return rv; // normal for some loads, no need to print a warning
375 : }
376 :
377 0 : nsCString escaped(NS_LITERAL_CSTRING("https://"));
378 0 : nsAutoCString temp;
379 0 : rv = url->GetHostPort(temp);
380 0 : NS_ENSURE_SUCCESS(rv, rv);
381 0 : escaped.Append(temp);
382 :
383 : // Stuff the whole thing back into a URI for the permission manager.
384 0 : rv = ios->NewURI(escaped, nullptr, nullptr, getter_AddRefs(topWinURI));
385 0 : NS_ENSURE_SUCCESS(rv, rv);
386 :
387 : nsCOMPtr<nsIPermissionManager> permMgr =
388 0 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
389 0 : NS_ENSURE_SUCCESS(rv, rv);
390 :
391 0 : uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
392 0 : rv = permMgr->TestPermission(topWinURI, "trackingprotection", &permissions);
393 0 : NS_ENSURE_SUCCESS(rv, rv);
394 :
395 0 : if (permissions == nsIPermissionManager::ALLOW_ACTION) {
396 0 : LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] for %s", this,
397 : aChannel, escaped.get()));
398 0 : mIsAllowListed = true;
399 0 : *result = false;
400 : } else {
401 0 : *result = true;
402 : }
403 :
404 : // In Private Browsing Mode we also check against an in-memory list.
405 0 : if (NS_UsePrivateBrowsing(aChannel)) {
406 : nsCOMPtr<nsIPrivateBrowsingTrackingProtectionWhitelist> pbmtpWhitelist =
407 0 : do_GetService(NS_PBTRACKINGPROTECTIONWHITELIST_CONTRACTID, &rv);
408 0 : NS_ENSURE_SUCCESS(rv, rv);
409 :
410 0 : bool exists = false;
411 0 : rv = pbmtpWhitelist->ExistsInAllowList(topWinURI, &exists);
412 0 : NS_ENSURE_SUCCESS(rv, rv);
413 :
414 0 : if (exists) {
415 0 : mIsAllowListed = true;
416 0 : LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] in PBM for %s",
417 : this, aChannel, escaped.get()));
418 : }
419 :
420 0 : *result = !exists;
421 : }
422 :
423 : // Tracking protection will be enabled so return without updating
424 : // the security state. If any channels are subsequently cancelled
425 : // (page elements blocked) the state will be then updated.
426 0 : if (*result) {
427 0 : if (LOG_ENABLED()) {
428 0 : LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on "
429 : "channel[%p] with uri %s for toplevel window %s", this, aChannel,
430 : chanURI->GetSpecOrDefault().get(),
431 : topWinURI->GetSpecOrDefault().get()));
432 : }
433 0 : return NS_OK;
434 : }
435 :
436 : // Tracking protection will be disabled so update the security state
437 : // of the document and fire a secure change event. If we can't get the
438 : // window for the channel, then the shield won't show up so we can't send
439 : // an event to the securityUI anyway.
440 0 : return NotifyTrackingProtectionDisabled(aChannel);
441 : }
442 :
443 : bool
444 0 : nsChannelClassifier::AddonMayLoad(nsIChannel *aChannel, nsIURI *aUri)
445 : {
446 0 : nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
447 0 : if (!channelLoadInfo)
448 0 : return false;
449 :
450 : // loadingPrincipal is used here to ensure we are loading into an
451 : // addon principal. This allows an addon, with explicit permission, to
452 : // call out to API endpoints that may otherwise get blocked.
453 0 : nsIPrincipal* loadingPrincipal = channelLoadInfo->LoadingPrincipal();
454 0 : if (!loadingPrincipal)
455 0 : return false;
456 :
457 0 : return BasePrincipal::Cast(loadingPrincipal)->AddonAllowsLoad(aUri, true);
458 : }
459 :
460 : // static
461 : nsresult
462 0 : nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
463 : {
464 : // Can be called in EITHER the parent or child process.
465 0 : nsCOMPtr<nsIParentChannel> parentChannel;
466 0 : NS_QueryNotificationCallbacks(aChannel, parentChannel);
467 0 : if (parentChannel) {
468 : // This channel is a parent-process proxy for a child process request.
469 : // Tell the child process channel to do this instead.
470 0 : parentChannel->NotifyTrackingProtectionDisabled();
471 0 : return NS_OK;
472 : }
473 :
474 : nsresult rv;
475 : nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
476 0 : do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
477 0 : NS_ENSURE_SUCCESS(rv, rv);
478 :
479 0 : nsCOMPtr<mozIDOMWindowProxy> win;
480 0 : rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
481 0 : NS_ENSURE_SUCCESS(rv, rv);
482 :
483 0 : auto* pwin = nsPIDOMWindowOuter::From(win);
484 0 : nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
485 0 : if (!docShell) {
486 0 : return NS_OK;
487 : }
488 0 : nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
489 0 : NS_ENSURE_TRUE(doc, NS_OK);
490 :
491 : // Notify nsIWebProgressListeners of this security event.
492 : // Can be used to change the UI state.
493 0 : nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
494 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
495 0 : uint32_t state = 0;
496 0 : nsCOMPtr<nsISecureBrowserUI> securityUI;
497 0 : docShell->GetSecurityUI(getter_AddRefs(securityUI));
498 0 : if (!securityUI) {
499 0 : return NS_OK;
500 : }
501 0 : doc->SetHasTrackingContentLoaded(true);
502 0 : securityUI->GetState(&state);
503 0 : state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
504 0 : eventSink->OnSecurityChange(nullptr, state);
505 :
506 0 : return NS_OK;
507 : }
508 :
509 : void
510 67 : nsChannelClassifier::Start()
511 : {
512 67 : nsresult rv = StartInternal();
513 67 : if (NS_FAILED(rv)) {
514 : // If we aren't getting a callback for any reason, assume a good verdict and
515 : // make sure we resume the channel if necessary.
516 268 : OnClassifyComplete(NS_OK, NS_LITERAL_CSTRING(""),NS_LITERAL_CSTRING(""),
517 335 : NS_LITERAL_CSTRING(""));
518 : }
519 67 : }
520 :
521 : nsresult
522 67 : nsChannelClassifier::StartInternal()
523 : {
524 : // Should only be called in the parent process.
525 67 : MOZ_ASSERT(XRE_IsParentProcess());
526 :
527 : // Don't bother to run the classifier on a load that has already failed.
528 : // (this might happen after a redirect)
529 : nsresult status;
530 67 : mChannel->GetStatus(&status);
531 67 : if (NS_FAILED(status))
532 0 : return status;
533 :
534 : // Don't bother to run the classifier on a cached load that was
535 : // previously classified as good.
536 67 : if (HasBeenClassified(mChannel)) {
537 0 : return NS_ERROR_UNEXPECTED;
538 : }
539 :
540 134 : nsCOMPtr<nsIURI> uri;
541 67 : nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
542 67 : NS_ENSURE_SUCCESS(rv, rv);
543 :
544 : // Don't bother checking certain types of URIs.
545 67 : bool isAbout = false;
546 67 : rv = uri->SchemeIs("about", &isAbout);
547 67 : NS_ENSURE_SUCCESS(rv, rv);
548 67 : if (isAbout) return NS_ERROR_UNEXPECTED;
549 :
550 : bool hasFlags;
551 65 : rv = NS_URIChainHasFlags(uri,
552 : nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
553 : &hasFlags);
554 65 : NS_ENSURE_SUCCESS(rv, rv);
555 65 : if (hasFlags) return NS_ERROR_UNEXPECTED;
556 :
557 65 : rv = NS_URIChainHasFlags(uri,
558 : nsIProtocolHandler::URI_IS_LOCAL_FILE,
559 : &hasFlags);
560 65 : NS_ENSURE_SUCCESS(rv, rv);
561 65 : if (hasFlags) return NS_ERROR_UNEXPECTED;
562 :
563 4 : rv = NS_URIChainHasFlags(uri,
564 : nsIProtocolHandler::URI_IS_UI_RESOURCE,
565 : &hasFlags);
566 4 : NS_ENSURE_SUCCESS(rv, rv);
567 4 : if (hasFlags) return NS_ERROR_UNEXPECTED;
568 :
569 4 : rv = NS_URIChainHasFlags(uri,
570 : nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
571 : &hasFlags);
572 4 : NS_ENSURE_SUCCESS(rv, rv);
573 4 : if (hasFlags) return NS_ERROR_UNEXPECTED;
574 :
575 8 : nsCString skipHostnames = CachedPrefs::GetInstance()->GetSkipHostnames();
576 4 : if (!skipHostnames.IsEmpty()) {
577 0 : LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
578 : this, skipHostnames.get()));
579 0 : if (IsHostnameWhitelisted(uri, skipHostnames)) {
580 0 : return NS_ERROR_UNEXPECTED;
581 : }
582 : }
583 :
584 : nsCOMPtr<nsIURIClassifier> uriClassifier =
585 8 : do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
586 8 : if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
587 4 : rv == NS_ERROR_NOT_AVAILABLE) {
588 : // no URI classifier, ignore this failure.
589 0 : return NS_ERROR_NOT_AVAILABLE;
590 : }
591 4 : NS_ENSURE_SUCCESS(rv, rv);
592 :
593 : nsCOMPtr<nsIScriptSecurityManager> securityManager =
594 8 : do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
595 4 : NS_ENSURE_SUCCESS(rv, rv);
596 :
597 8 : nsCOMPtr<nsIPrincipal> principal;
598 4 : rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
599 4 : NS_ENSURE_SUCCESS(rv, rv);
600 :
601 : bool expectCallback;
602 4 : if (LOG_ENABLED()) {
603 0 : nsCOMPtr<nsIURI> principalURI;
604 0 : principal->GetURI(getter_AddRefs(principalURI));
605 0 : LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel with "
606 : "uri %s", this, principalURI->GetSpecOrDefault().get(),
607 : uri->GetSpecOrDefault().get()));
608 : }
609 : // The classify is running in parent process, no need to give a valid event
610 : // target
611 12 : rv = uriClassifier->Classify(principal, nullptr,
612 4 : ShouldEnableTrackingProtection(),
613 4 : this, &expectCallback);
614 4 : if (NS_FAILED(rv)) {
615 0 : return rv;
616 : }
617 :
618 4 : if (expectCallback) {
619 : // Suspend the channel, it will be resumed when we get the classifier
620 : // callback.
621 0 : rv = mChannel->Suspend();
622 0 : if (NS_FAILED(rv)) {
623 : // Some channels (including nsJSChannel) fail on Suspend. This
624 : // shouldn't be fatal, but will prevent malware from being
625 : // blocked on these channels.
626 0 : LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
627 0 : return rv;
628 : }
629 :
630 0 : mSuspendedChannel = true;
631 0 : LOG(("nsChannelClassifier[%p]: suspended channel %p",
632 : this, mChannel.get()));
633 : } else {
634 4 : LOG(("nsChannelClassifier[%p]: not expecting callback", this));
635 4 : return NS_ERROR_FAILURE;
636 : }
637 :
638 : // Add an observer for shutdown
639 0 : AddShutdownObserver();
640 0 : return NS_OK;
641 : }
642 :
643 : bool
644 0 : nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri,
645 : const nsACString &aWhitelisted)
646 : {
647 0 : nsAutoCString host;
648 0 : nsresult rv = aUri->GetHost(host);
649 0 : if (NS_FAILED(rv) || host.IsEmpty()) {
650 0 : return false;
651 : }
652 0 : ToLowerCase(host);
653 :
654 0 : nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
655 0 : while (tokenizer.hasMoreTokens()) {
656 0 : const nsACString& token = tokenizer.nextToken();
657 0 : if (token.Equals(host)) {
658 0 : LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
659 : this, host.get()));
660 0 : return true;
661 : }
662 : }
663 :
664 0 : return false;
665 : }
666 :
667 : // Note in the cache entry that this URL was classified, so that future
668 : // cached loads don't need to be checked.
669 : void
670 0 : nsChannelClassifier::MarkEntryClassified(nsresult status)
671 : {
672 : // Should only be called in the parent process.
673 0 : MOZ_ASSERT(XRE_IsParentProcess());
674 :
675 : // Don't cache tracking classifications because we support allowlisting.
676 0 : if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
677 0 : return;
678 : }
679 :
680 0 : if (LOG_ENABLED()) {
681 0 : nsAutoCString errorName;
682 0 : GetErrorName(status, errorName);
683 0 : nsCOMPtr<nsIURI> uri;
684 0 : mChannel->GetURI(getter_AddRefs(uri));
685 0 : nsAutoCString spec;
686 0 : uri->GetAsciiSpec(spec);
687 0 : LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s",
688 : errorName.get(), spec.get()));
689 : }
690 :
691 0 : nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
692 0 : if (!cachingChannel) {
693 0 : return;
694 : }
695 :
696 0 : nsCOMPtr<nsISupports> cacheToken;
697 0 : cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
698 0 : if (!cacheToken) {
699 0 : return;
700 : }
701 :
702 : nsCOMPtr<nsICacheEntry> cacheEntry =
703 0 : do_QueryInterface(cacheToken);
704 0 : if (!cacheEntry) {
705 0 : return;
706 : }
707 :
708 0 : cacheEntry->SetMetaDataElement("necko:classified",
709 0 : NS_SUCCEEDED(status) ? "1" : nullptr);
710 : }
711 :
712 : bool
713 67 : nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
714 : {
715 : // Should only be called in the parent process.
716 67 : MOZ_ASSERT(XRE_IsParentProcess());
717 :
718 : nsCOMPtr<nsICachingChannel> cachingChannel =
719 134 : do_QueryInterface(aChannel);
720 67 : if (!cachingChannel) {
721 63 : return false;
722 : }
723 :
724 : // Only check the tag if we are loading from the cache without
725 : // validation.
726 : bool fromCache;
727 4 : if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
728 4 : return false;
729 : }
730 :
731 0 : nsCOMPtr<nsISupports> cacheToken;
732 0 : cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
733 0 : if (!cacheToken) {
734 0 : return false;
735 : }
736 :
737 : nsCOMPtr<nsICacheEntry> cacheEntry =
738 0 : do_QueryInterface(cacheToken);
739 0 : if (!cacheEntry) {
740 0 : return false;
741 : }
742 :
743 0 : nsXPIDLCString tag;
744 0 : cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
745 0 : return tag.EqualsLiteral("1");
746 : }
747 :
748 : //static
749 : bool
750 0 : nsChannelClassifier::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
751 : {
752 0 : nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
753 0 : nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
754 0 : if (!channelLoadInfo || !docURI) {
755 0 : return false;
756 : }
757 :
758 0 : nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
759 0 : if (!channelLoadingPrincipal) {
760 : // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
761 : // loads should not be blocked by Tracking Protection, so we will return
762 : // false
763 0 : return false;
764 : }
765 0 : nsCOMPtr<nsIURI> channelLoadingURI;
766 0 : channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
767 0 : if (!channelLoadingURI) {
768 0 : return false;
769 : }
770 0 : bool equals = false;
771 0 : nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
772 0 : return NS_SUCCEEDED(rv) && equals;
773 : }
774 :
775 : // static
776 : nsresult
777 0 : nsChannelClassifier::SetBlockedContent(nsIChannel *channel,
778 : nsresult aErrorCode,
779 : const nsACString& aList,
780 : const nsACString& aProvider,
781 : const nsACString& aPrefix)
782 : {
783 0 : NS_ENSURE_ARG(!aList.IsEmpty());
784 0 : NS_ENSURE_ARG(!aPrefix.IsEmpty());
785 :
786 : // Can be called in EITHER the parent or child process.
787 0 : nsCOMPtr<nsIParentChannel> parentChannel;
788 0 : NS_QueryNotificationCallbacks(channel, parentChannel);
789 0 : if (parentChannel) {
790 : // This channel is a parent-process proxy for a child process request.
791 : // Tell the child process channel to do this instead.
792 0 : parentChannel->SetClassifierMatchedInfo(aList, aProvider, aPrefix);
793 0 : return NS_OK;
794 : }
795 :
796 : nsresult rv;
797 0 : nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel, &rv);
798 0 : NS_ENSURE_SUCCESS(rv, rv);
799 :
800 0 : if (classifiedChannel) {
801 0 : classifiedChannel->SetMatchedInfo(aList, aProvider, aPrefix);
802 : }
803 :
804 0 : nsCOMPtr<mozIDOMWindowProxy> win;
805 : nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
806 0 : do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
807 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
808 0 : rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
809 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
810 0 : auto* pwin = nsPIDOMWindowOuter::From(win);
811 0 : nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
812 0 : if (!docShell) {
813 0 : return NS_OK;
814 : }
815 0 : nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
816 0 : NS_ENSURE_TRUE(doc, NS_OK);
817 :
818 : // This event might come after the user has navigated to another page.
819 : // To prevent showing the TrackingProtection UI on the wrong page, we need to
820 : // check that the loading URI for the channel is the same as the URI currently
821 : // loaded in the document.
822 0 : if (!SameLoadingURI(doc, channel)) {
823 0 : return NS_OK;
824 : }
825 :
826 : // Notify nsIWebProgressListeners of this security event.
827 : // Can be used to change the UI state.
828 0 : nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
829 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
830 0 : uint32_t state = 0;
831 0 : nsCOMPtr<nsISecureBrowserUI> securityUI;
832 0 : docShell->GetSecurityUI(getter_AddRefs(securityUI));
833 0 : if (!securityUI) {
834 0 : return NS_OK;
835 : }
836 0 : securityUI->GetState(&state);
837 0 : if (aErrorCode == NS_ERROR_TRACKING_URI) {
838 0 : doc->SetHasTrackingContentBlocked(true);
839 0 : state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
840 : } else {
841 0 : state |= nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
842 : }
843 :
844 0 : eventSink->OnSecurityChange(nullptr, state);
845 :
846 : // Log a warning to the web console.
847 0 : nsCOMPtr<nsIURI> uri;
848 0 : channel->GetURI(getter_AddRefs(uri));
849 0 : NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
850 0 : const char16_t* params[] = { spec.get() };
851 0 : const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ?
852 0 : "TrackingUriBlocked" : "UnsafeUriBlocked";
853 : nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI) ?
854 0 : NS_LITERAL_CSTRING("Tracking Protection") :
855 0 : NS_LITERAL_CSTRING("Safe Browsing");
856 :
857 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
858 : category,
859 : doc,
860 : nsContentUtils::eNECKO_PROPERTIES,
861 : message,
862 0 : params, ArrayLength(params));
863 :
864 0 : return NS_OK;
865 : }
866 :
867 : namespace {
868 :
869 : // The purpose of this class is only for implementing all nsISupports methods.
870 : // This is a workaround for template derived class.
871 : class URIClassifierCallbackBase : public nsIURIClassifierCallback {
872 : public:
873 0 : URIClassifierCallbackBase() = default;
874 :
875 : NS_DECL_THREADSAFE_ISUPPORTS
876 :
877 : protected:
878 0 : virtual ~URIClassifierCallbackBase() = default;
879 : };
880 :
881 0 : NS_IMPL_ISUPPORTS(URIClassifierCallbackBase, nsIURIClassifierCallback)
882 :
883 : // A template class for reusing the code.
884 : // OnClassifyCompleteInternal will be called to pass the result.
885 : template<class T>
886 : class IsTrackerWhitelistedCallback final : public URIClassifierCallbackBase {
887 : public:
888 0 : explicit IsTrackerWhitelistedCallback(T* aClosure,
889 : const nsACString& aList,
890 : const nsACString& aProvider,
891 : const nsACString& aPrefix,
892 : nsIURI* aWhitelistURI)
893 : : mClosure(aClosure)
894 : , mWhitelistURI(aWhitelistURI)
895 : , mList(aList)
896 : , mProvider(aProvider)
897 0 : , mPrefix(aPrefix)
898 : {
899 0 : }
900 :
901 0 : NS_IMETHOD OnClassifyComplete(nsresult /*aErrorCode*/,
902 : const nsACString& aLists, // Only this matters.
903 : const nsACString& /*aProvider*/,
904 : const nsACString& /*aPrefix*/) override
905 : {
906 : nsresult rv;
907 0 : if (aLists.IsEmpty()) {
908 0 : if (LOG_ENABLED()) {
909 0 : MOZ_ASSERT(mWhitelistURI);
910 :
911 0 : LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
912 : mClosure.get(), mWhitelistURI->GetSpecOrDefault().get()));
913 : }
914 0 : rv = NS_ERROR_TRACKING_URI;
915 : } else {
916 0 : LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
917 : "in whitelist so we won't block it", mClosure.get()));
918 0 : rv = NS_OK;
919 : }
920 :
921 0 : rv = mClosure->OnClassifyCompleteInternal(rv, mList, mProvider, mPrefix);
922 0 : mClosure = nullptr;
923 0 : return rv;
924 : }
925 :
926 : private:
927 0 : ~IsTrackerWhitelistedCallback() = default;
928 :
929 : RefPtr<T> mClosure;
930 : nsCOMPtr<nsIURI> mWhitelistURI;
931 :
932 : // The following 3 values are for forwarding the callback.
933 : nsCString mList;
934 : nsCString mProvider;
935 : nsCString mPrefix;
936 : };
937 :
938 : // This class is designed to get the results of checking blacklist and whitelist.
939 : // 1. The result of local blacklist will be sent back via
940 : // OnClassifyComplete, which is called by nsIURIClassifier service.
941 : // 2. The result of local whitelist is got via OnClassifyCompleteInternal,
942 : // which is called by IsTrackerWhitelistedCallback::OnClassifyComplete.
943 : class IsTrackerBlacklistedCallback final : public nsIURIClassifierCallback {
944 : public:
945 1 : explicit IsTrackerBlacklistedCallback(nsChannelClassifier* aChannelClassifier,
946 : nsIURIClassifierCallback* aCallback)
947 1 : : mChannelClassifier(aChannelClassifier)
948 1 : , mChannelCallback(aCallback)
949 : {
950 1 : }
951 :
952 : NS_DECL_THREADSAFE_ISUPPORTS
953 : NS_DECL_NSIURICLASSIFIERCALLBACK
954 :
955 : nsresult OnClassifyCompleteInternal(nsresult aErrorCode,
956 : const nsACString& aList,
957 : const nsACString& aProvider,
958 : const nsACString& aPrefix);
959 :
960 : private:
961 1 : ~IsTrackerBlacklistedCallback() = default;
962 :
963 : RefPtr<nsChannelClassifier> mChannelClassifier;
964 : nsCOMPtr<nsIURIClassifierCallback> mChannelCallback;
965 : };
966 :
967 8 : NS_IMPL_ISUPPORTS(IsTrackerBlacklistedCallback, nsIURIClassifierCallback)
968 :
969 : /*virtual*/ nsresult
970 1 : IsTrackerBlacklistedCallback::OnClassifyComplete(nsresult aErrorCode,
971 : const nsACString& aLists,
972 : const nsACString& aProvider,
973 : const nsACString& aPrefix)
974 : {
975 1 : nsresult status = aLists.IsEmpty() ? NS_OK : NS_ERROR_TRACKING_URI;
976 1 : bool tpEnabled = mChannelClassifier->ShouldEnableTrackingProtection();
977 :
978 1 : LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete "
979 : " status=0x%" PRIx32 ", tpEnabled=%d",
980 : mChannelClassifier.get(), static_cast<uint32_t>(status), tpEnabled));
981 :
982 : // If this is not in local blacklist or tracking protection is enabled,
983 : // directly send the status back.
984 : // The whitelist will be checked at nsChannelClassifier::OnClassifyComplete
985 : // when tracking protection is enabled, so we can just return here.
986 1 : if (NS_SUCCEEDED(status) || tpEnabled) {
987 1 : return mChannelCallback->OnClassifyComplete(
988 1 : status, aLists, aProvider, aPrefix);
989 : }
990 :
991 0 : nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
992 0 : if (LOG_ENABLED()) {
993 0 : nsCOMPtr<nsIURI> uri;
994 0 : channel->GetURI(getter_AddRefs(uri));
995 0 : LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
996 : "uri=%s, is in blacklist. Start checking whitelist.",
997 : mChannelClassifier.get(), channel.get(),
998 : uri->GetSpecOrDefault().get()));
999 : }
1000 :
1001 0 : nsCOMPtr<nsIURI> whitelistURI = mChannelClassifier->CreateWhiteListURI();
1002 : nsCOMPtr<nsIURIClassifierCallback> callback =
1003 : new IsTrackerWhitelistedCallback<IsTrackerBlacklistedCallback>(
1004 0 : this, aLists, aProvider, aPrefix, whitelistURI);
1005 :
1006 : // If IsTrackerWhitelisted has failed, it means the uri is not in whitelist.
1007 0 : if (NS_FAILED(mChannelClassifier->IsTrackerWhitelisted(whitelistURI, callback))) {
1008 0 : LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyComplete channel [%p] "
1009 : "IsTrackerWhitelisted has failed.",
1010 : mChannelClassifier.get(), channel.get()));
1011 :
1012 0 : MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
1013 :
1014 0 : SetIsTrackingResourceHelper(channel);
1015 0 : if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
1016 0 : LowerPriorityHelper(channel);
1017 : }
1018 0 : SetThrottleableHelper(channel);
1019 :
1020 : // We don't want to disable speculative connection when tracking protection
1021 : // is disabled. So, change the status to NS_OK.
1022 0 : status = NS_OK;
1023 :
1024 0 : return mChannelCallback->OnClassifyComplete(
1025 0 : status, aLists, aProvider, aPrefix);
1026 : }
1027 :
1028 : // OnClassifyCompleteInternal() will be called once we know
1029 : // if the tracker is whitelisted.
1030 0 : return NS_OK;
1031 : }
1032 :
1033 : nsresult
1034 0 : IsTrackerBlacklistedCallback::OnClassifyCompleteInternal(nsresult aErrorCode,
1035 : const nsACString& aLists,
1036 : const nsACString& aProvider,
1037 : const nsACString& aPrefix)
1038 : {
1039 0 : LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal"
1040 : " status=0x%" PRIx32,
1041 : mChannelClassifier.get(), static_cast<uint32_t>(aErrorCode)));
1042 :
1043 0 : if (NS_SUCCEEDED(aErrorCode)) {
1044 0 : return mChannelCallback->OnClassifyComplete(
1045 0 : aErrorCode, aLists, aProvider, aPrefix);
1046 : }
1047 :
1048 0 : MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation());
1049 0 : MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_URI);
1050 :
1051 0 : nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel();
1052 0 : if (LOG_ENABLED()) {
1053 0 : nsCOMPtr<nsIURI> uri;
1054 0 : channel->GetURI(getter_AddRefs(uri));
1055 0 : LOG(("IsTrackerBlacklistedCallback[%p]:OnClassifyCompleteInternal "
1056 : "channel [%p] uri=%s, is not in whitelist",
1057 : mChannelClassifier.get(), channel.get(),
1058 : uri->GetSpecOrDefault().get()));
1059 : }
1060 :
1061 0 : SetIsTrackingResourceHelper(channel);
1062 0 : if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) {
1063 0 : LowerPriorityHelper(channel);
1064 : }
1065 0 : SetThrottleableHelper(channel);
1066 :
1067 0 : return mChannelCallback->OnClassifyComplete(
1068 0 : NS_OK, aLists, aProvider, aPrefix);
1069 : }
1070 :
1071 : } // end of unnamed namespace/
1072 :
1073 : already_AddRefed<nsIURI>
1074 0 : nsChannelClassifier::CreateWhiteListURI() const
1075 : {
1076 : nsresult rv;
1077 0 : nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
1078 0 : if (!chan) {
1079 0 : return nullptr;
1080 : }
1081 :
1082 0 : nsCOMPtr<nsIURI> topWinURI;
1083 0 : rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
1084 0 : NS_ENSURE_SUCCESS(rv, nullptr);
1085 0 : if (!topWinURI) {
1086 0 : LOG(("nsChannelClassifier[%p]: No window URI", this));
1087 0 : return nullptr;
1088 : }
1089 :
1090 : nsCOMPtr<nsIScriptSecurityManager> securityManager =
1091 0 : do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
1092 0 : NS_ENSURE_SUCCESS(rv, nullptr);
1093 0 : nsCOMPtr<nsIPrincipal> chanPrincipal;
1094 0 : rv = securityManager->GetChannelURIPrincipal(mChannel,
1095 0 : getter_AddRefs(chanPrincipal));
1096 0 : if (NS_FAILED(rv)) {
1097 0 : return nullptr;
1098 : }
1099 :
1100 : // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
1101 0 : nsAutoCString pageHostname, resourceDomain;
1102 0 : rv = topWinURI->GetHost(pageHostname);
1103 0 : NS_ENSURE_SUCCESS(rv, nullptr);
1104 0 : rv = chanPrincipal->GetBaseDomain(resourceDomain);
1105 0 : NS_ENSURE_SUCCESS(rv, nullptr);
1106 0 : nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
1107 0 : pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
1108 0 : LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
1109 : this, whitelistEntry.get()));
1110 :
1111 0 : nsCOMPtr<nsIURI> whitelistURI;
1112 0 : rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
1113 :
1114 0 : return NS_SUCCEEDED(rv) ? whitelistURI.forget() : nullptr;
1115 : }
1116 :
1117 : nsresult
1118 0 : nsChannelClassifier::IsTrackerWhitelisted(nsIURI* aWhiteListURI,
1119 : nsIURIClassifierCallback *aCallback)
1120 : {
1121 0 : if (!aCallback || !aWhiteListURI) {
1122 0 : return NS_ERROR_INVALID_ARG;
1123 : }
1124 :
1125 : nsresult rv;
1126 : nsCOMPtr<nsIURIClassifier> uriClassifier =
1127 0 : do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
1128 0 : NS_ENSURE_SUCCESS(rv, rv);
1129 :
1130 0 : nsCString trackingWhitelist = CachedPrefs::GetInstance()->GetTrackingWhiteList();
1131 0 : if (trackingWhitelist.IsEmpty()) {
1132 0 : LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
1133 : this));
1134 0 : return NS_ERROR_TRACKING_URI;
1135 : }
1136 :
1137 0 : return uriClassifier->AsyncClassifyLocalWithTables(aWhiteListURI, trackingWhitelist, aCallback);
1138 : }
1139 :
1140 : NS_IMETHODIMP
1141 67 : nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
1142 : const nsACString& aList,
1143 : const nsACString& aProvider,
1144 : const nsACString& aPrefix)
1145 : {
1146 : // Should only be called in the parent process.
1147 67 : MOZ_ASSERT(XRE_IsParentProcess());
1148 :
1149 67 : if (aErrorCode == NS_ERROR_TRACKING_URI) {
1150 0 : nsCOMPtr<nsIURI> whitelistURI = CreateWhiteListURI();
1151 : nsCOMPtr<nsIURIClassifierCallback> callback =
1152 : new IsTrackerWhitelistedCallback<nsChannelClassifier>(
1153 0 : this, aList, aProvider, aPrefix, whitelistURI);
1154 0 : if (whitelistURI &&
1155 0 : NS_SUCCEEDED(IsTrackerWhitelisted(whitelistURI, callback))) {
1156 : // OnClassifyCompleteInternal() will be called once we know
1157 : // if the tracker is whitelisted.
1158 0 : return NS_OK;
1159 : }
1160 : }
1161 :
1162 67 : return OnClassifyCompleteInternal(aErrorCode, aList, aProvider, aPrefix);
1163 : }
1164 :
1165 : nsresult
1166 67 : nsChannelClassifier::OnClassifyCompleteInternal(nsresult aErrorCode,
1167 : const nsACString& aList,
1168 : const nsACString& aProvider,
1169 : const nsACString& aPrefix)
1170 : {
1171 67 : if (mSuspendedChannel) {
1172 0 : nsAutoCString errorName;
1173 0 : if (LOG_ENABLED()) {
1174 0 : GetErrorName(aErrorCode, errorName);
1175 0 : LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
1176 : this, errorName.get()));
1177 : }
1178 0 : MarkEntryClassified(aErrorCode);
1179 :
1180 0 : if (NS_FAILED(aErrorCode)) {
1181 0 : if (LOG_ENABLED()) {
1182 0 : nsCOMPtr<nsIURI> uri;
1183 0 : mChannel->GetURI(getter_AddRefs(uri));
1184 0 : LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
1185 : "with error code %s", this, mChannel.get(),
1186 : uri->GetSpecOrDefault().get(), errorName.get()));
1187 : }
1188 :
1189 : // Channel will be cancelled (page element blocked) due to tracking
1190 : // protection or Safe Browsing.
1191 : // Do update the security state of the document and fire a security
1192 : // change event.
1193 0 : SetBlockedContent(mChannel, aErrorCode, aList, aProvider, aPrefix);
1194 :
1195 0 : mChannel->Cancel(aErrorCode);
1196 : }
1197 0 : LOG(("nsChannelClassifier[%p]: resuming channel %p from "
1198 : "OnClassifyComplete", this, mChannel.get()));
1199 0 : mChannel->Resume();
1200 : }
1201 :
1202 67 : mChannel = nullptr;
1203 67 : RemoveShutdownObserver();
1204 :
1205 67 : return NS_OK;
1206 : }
1207 :
1208 : nsresult
1209 4 : nsChannelClassifier::CheckIsTrackerWithLocalTable(nsIURIClassifierCallback* aCallback)
1210 : {
1211 : nsresult rv;
1212 :
1213 4 : if (!aCallback) {
1214 0 : return NS_ERROR_INVALID_ARG;
1215 : }
1216 :
1217 : nsCOMPtr<nsIURIClassifier> uriClassifier =
1218 8 : do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
1219 4 : if (NS_FAILED(rv)) {
1220 0 : return rv;
1221 : }
1222 :
1223 4 : if (!ShouldEnableTrackingProtection() && !ShouldEnableTrackingAnnotation()) {
1224 3 : return NS_ERROR_FAILURE;
1225 : }
1226 :
1227 2 : nsCOMPtr<nsIURI> uri;
1228 1 : rv = mChannel->GetURI(getter_AddRefs(uri));
1229 1 : if (NS_FAILED(rv) || !uri) {
1230 0 : return rv;
1231 : }
1232 :
1233 : nsCString trackingBlacklist =
1234 2 : CachedPrefs::GetInstance()->GetTrackingBlackList();
1235 1 : if (trackingBlacklist.IsEmpty()) {
1236 0 : LOG(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable blacklist is empty",
1237 : this));
1238 0 : return NS_ERROR_FAILURE;
1239 : }
1240 :
1241 : nsCOMPtr<nsIURIClassifierCallback> callback =
1242 2 : new IsTrackerBlacklistedCallback(this, aCallback);
1243 :
1244 1 : LOG(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable for uri=%s\n",
1245 : this, uri->GetSpecOrDefault().get()));
1246 2 : return uriClassifier->AsyncClassifyLocalWithTables(uri,
1247 : trackingBlacklist,
1248 2 : callback);
1249 : }
1250 :
1251 : already_AddRefed<nsIChannel>
1252 0 : nsChannelClassifier::GetChannel()
1253 : {
1254 0 : nsCOMPtr<nsIChannel> channel = mChannel;
1255 0 : return channel.forget();
1256 : }
1257 :
1258 : void
1259 0 : nsChannelClassifier::AddShutdownObserver()
1260 : {
1261 0 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
1262 0 : if (observerService) {
1263 0 : observerService->AddObserver(this, "profile-change-net-teardown", false);
1264 : }
1265 0 : }
1266 :
1267 : void
1268 67 : nsChannelClassifier::RemoveShutdownObserver()
1269 : {
1270 134 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
1271 67 : if (observerService) {
1272 67 : observerService->RemoveObserver(this, "profile-change-net-teardown");
1273 : }
1274 67 : }
1275 :
1276 : ///////////////////////////////////////////////////////////////////////////////
1277 : // nsIObserver implementation
1278 : NS_IMETHODIMP
1279 0 : nsChannelClassifier::Observe(nsISupports *aSubject, const char *aTopic,
1280 : const char16_t *aData)
1281 : {
1282 0 : if (!strcmp(aTopic, "profile-change-net-teardown")) {
1283 : // If we aren't getting a callback for any reason, make sure
1284 : // we resume the channel.
1285 :
1286 0 : if (mChannel && mSuspendedChannel) {
1287 0 : mSuspendedChannel = false;
1288 0 : mChannel->Cancel(NS_ERROR_ABORT);
1289 0 : mChannel->Resume();
1290 0 : mChannel = nullptr;
1291 : }
1292 :
1293 0 : RemoveShutdownObserver();
1294 : }
1295 :
1296 0 : return NS_OK;
1297 : }
1298 :
1299 : #undef LOG_ENABLED
1300 :
1301 : } // namespace net
1302 9 : } // namespace mozilla
|