/src/mozilla-central/netwerk/base/nsChannelClassifier.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsICookieService.h" |
16 | | #include "nsIChannel.h" |
17 | | #include "nsIClassOfService.h" |
18 | | #include "nsIDocShell.h" |
19 | | #include "nsIDocument.h" |
20 | | #include "nsIHttpChannel.h" |
21 | | #include "nsIHttpChannelInternal.h" |
22 | | #include "nsIIOService.h" |
23 | | #include "nsIParentChannel.h" |
24 | | #include "nsIPermissionManager.h" |
25 | | #include "nsIProtocolHandler.h" |
26 | | #include "nsIScriptError.h" |
27 | | #include "nsIScriptSecurityManager.h" |
28 | | #include "nsISecureBrowserUI.h" |
29 | | #include "nsISecurityEventSink.h" |
30 | | #include "nsISupportsPriority.h" |
31 | | #include "nsIURL.h" |
32 | | #include "nsIWebProgressListener.h" |
33 | | #include "nsNetUtil.h" |
34 | | #include "nsPIDOMWindow.h" |
35 | | #include "nsXULAppAPI.h" |
36 | | #include "nsQueryObject.h" |
37 | | #include "nsIUrlClassifierDBService.h" |
38 | | #include "nsIURLFormatter.h" |
39 | | |
40 | | #include "mozilla/AntiTrackingCommon.h" |
41 | | #include "mozilla/ErrorNames.h" |
42 | | #include "mozilla/Logging.h" |
43 | | #include "mozilla/Preferences.h" |
44 | | #include "mozilla/net/HttpBaseChannel.h" |
45 | | #include "mozilla/ClearOnShutdown.h" |
46 | | #include "mozilla/Services.h" |
47 | | #include "mozilla/StaticPrefs.h" |
48 | | #include "mozilla/Unused.h" |
49 | | |
50 | | namespace mozilla { |
51 | | namespace net { |
52 | | |
53 | | // |
54 | | // MOZ_LOG=nsChannelClassifier:5 |
55 | | // |
56 | | static LazyLogModule gChannelClassifierLog("nsChannelClassifier"); |
57 | | |
58 | | |
59 | | #undef LOG |
60 | 0 | #define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Info, args) |
61 | 0 | #define LOG_DEBUG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args) |
62 | 0 | #define LOG_WARN(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Warning, args) |
63 | 0 | #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Info) |
64 | | |
65 | 0 | #define URLCLASSIFIER_SKIP_HOSTNAMES "urlclassifier.skipHostnames" |
66 | 0 | #define URLCLASSIFIER_ANNOTATION_TABLE "urlclassifier.trackingAnnotationTable" |
67 | 0 | #define URLCLASSIFIER_ANNOTATION_TABLE_TEST_ENTRIES "urlclassifier.trackingAnnotationTable.testEntries" |
68 | 0 | #define URLCLASSIFIER_ANNOTATION_WHITELIST "urlclassifier.trackingAnnotationWhitelistTable" |
69 | 0 | #define URLCLASSIFIER_ANNOTATION_WHITELIST_TEST_ENTRIES "urlclassifier.trackingAnnotationWhitelistTable.testEntries" |
70 | 0 | #define URLCLASSIFIER_TRACKING_TABLE "urlclassifier.trackingTable" |
71 | 0 | #define URLCLASSIFIER_TRACKING_TABLE_TEST_ENTRIES "urlclassifier.trackingTable.testEntries" |
72 | 0 | #define URLCLASSIFIER_TRACKING_WHITELIST "urlclassifier.trackingWhitelistTable" |
73 | 0 | #define URLCLASSIFIER_TRACKING_WHITELIST_TEST_ENTRIES "urlclassifier.trackingWhitelistTable.testEntries" |
74 | | |
75 | 0 | #define TABLE_TRACKING_BLACKLIST_PREF "tracking-blacklist-pref" |
76 | 0 | #define TABLE_TRACKING_WHITELIST_PREF "tracking-whitelist-pref" |
77 | 0 | #define TABLE_ANNOTATION_BLACKLIST_PREF "annotation-blacklist-pref" |
78 | 0 | #define TABLE_ANNOTATION_WHITELIST_PREF "annotation-whitelist-pref" |
79 | | |
80 | | static const nsCString::size_type sMaxSpecLength = 128; |
81 | | |
82 | | // Put CachedPrefs in anonymous namespace to avoid any collision from outside of |
83 | | // this file. |
84 | | namespace { |
85 | | |
86 | | /** |
87 | | * It is not recommended to read from Preference everytime a channel is |
88 | | * connected. |
89 | | * That is not fast and we should cache preference values and reuse them |
90 | | */ |
91 | | class CachedPrefs final |
92 | | { |
93 | | public: |
94 | | static CachedPrefs* GetInstance(); |
95 | | |
96 | | void Init(); |
97 | 0 | bool IsAllowListExample() { return sAllowListExample;} |
98 | 0 | bool IsLowerNetworkPriority() { return sLowerNetworkPriority;} |
99 | 0 | bool IsAnnotateChannelEnabled() { return sAnnotateChannelEnabled;} |
100 | | |
101 | 0 | nsCString GetSkipHostnames() const { return mSkipHostnames; } |
102 | 0 | nsCString GetAnnotationBlackList() const { return mAnnotationBlacklist; } |
103 | 0 | nsCString GetAnnotationBlackListExtraEntries() const { return mAnnotationBlacklistExtraEntries; } |
104 | 0 | nsCString GetAnnotationWhiteList() const { return mAnnotationWhitelist; } |
105 | 0 | nsCString GetAnnotationWhiteListExtraEntries() const { return mAnnotationWhitelistExtraEntries; } |
106 | 0 | nsCString GetTrackingBlackList() const { return mTrackingBlacklist; } |
107 | 0 | nsCString GetTrackingBlackListExtraEntries() { return mTrackingBlacklistExtraEntries; } |
108 | 0 | nsCString GetTrackingWhiteList() const { return mTrackingWhitelist; } |
109 | 0 | nsCString GetTrackingWhiteListExtraEntries() { return mTrackingWhitelistExtraEntries; } |
110 | | |
111 | 0 | void SetSkipHostnames(const nsACString& aHostnames) { mSkipHostnames = aHostnames; } |
112 | 0 | void SetAnnotationBlackList(const nsACString& aList) { mAnnotationBlacklist = aList; } |
113 | 0 | void SetAnnotationBlackListExtraEntries(const nsACString& aList) { mAnnotationBlacklistExtraEntries = aList; } |
114 | 0 | void SetAnnotationWhiteList(const nsACString& aList) { mAnnotationWhitelist = aList; } |
115 | 0 | void SetAnnotationWhiteListExtraEntries(const nsACString& aList) { mAnnotationWhitelistExtraEntries = aList; } |
116 | 0 | void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; } |
117 | 0 | void SetTrackingBlackListExtraEntries(const nsACString& aList) { mTrackingBlacklistExtraEntries = aList; } |
118 | 0 | void SetTrackingWhiteList(const nsACString& aList) { mTrackingWhitelist = aList; } |
119 | 0 | void SetTrackingWhiteListExtraEntries(const nsACString& aList) { mTrackingWhitelistExtraEntries = aList; } |
120 | | |
121 | | private: |
122 | | friend class StaticAutoPtr<CachedPrefs>; |
123 | | CachedPrefs(); |
124 | | ~CachedPrefs(); |
125 | | |
126 | | static void OnPrefsChange(const char* aPrefName, CachedPrefs*); |
127 | | |
128 | | // Whether channels should be annotated as being on the tracking protection |
129 | | // list. |
130 | | static bool sAnnotateChannelEnabled; |
131 | | // Whether the priority of the channels annotated as being on the tracking |
132 | | // protection list should be lowered. |
133 | | static bool sLowerNetworkPriority; |
134 | | static bool sAllowListExample; |
135 | | |
136 | | nsCString mSkipHostnames; |
137 | | nsCString mAnnotationBlacklist; |
138 | | nsCString mAnnotationBlacklistExtraEntries; |
139 | | nsCString mAnnotationWhitelist; |
140 | | nsCString mAnnotationWhitelistExtraEntries; |
141 | | nsCString mTrackingBlacklist; |
142 | | nsCString mTrackingBlacklistExtraEntries; |
143 | | nsCString mTrackingWhitelist; |
144 | | nsCString mTrackingWhitelistExtraEntries; |
145 | | |
146 | | static StaticAutoPtr<CachedPrefs> sInstance; |
147 | | }; |
148 | | |
149 | | bool CachedPrefs::sAllowListExample = false; |
150 | | bool CachedPrefs::sLowerNetworkPriority = false; |
151 | | bool CachedPrefs::sAnnotateChannelEnabled = false; |
152 | | |
153 | | StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance; |
154 | | |
155 | | // static |
156 | | void |
157 | | CachedPrefs::OnPrefsChange(const char* aPref, CachedPrefs* aPrefs) |
158 | 0 | { |
159 | 0 | if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) { |
160 | 0 | nsCString skipHostnames; |
161 | 0 | Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES, skipHostnames); |
162 | 0 | ToLowerCase(skipHostnames); |
163 | 0 | aPrefs->SetSkipHostnames(skipHostnames); |
164 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_ANNOTATION_TABLE)) { |
165 | 0 | nsAutoCString annotationBlacklist; |
166 | 0 | Preferences::GetCString(URLCLASSIFIER_ANNOTATION_TABLE, |
167 | 0 | annotationBlacklist); |
168 | 0 | aPrefs->SetAnnotationBlackList(annotationBlacklist); |
169 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_ANNOTATION_TABLE_TEST_ENTRIES)) { |
170 | 0 | nsAutoCString annotationBlacklistExtraEntries; |
171 | 0 | Preferences::GetCString(URLCLASSIFIER_ANNOTATION_TABLE_TEST_ENTRIES, |
172 | 0 | annotationBlacklistExtraEntries); |
173 | 0 | aPrefs->SetAnnotationBlackListExtraEntries(annotationBlacklistExtraEntries); |
174 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_ANNOTATION_WHITELIST)) { |
175 | 0 | nsAutoCString annotationWhitelist; |
176 | 0 | Preferences::GetCString(URLCLASSIFIER_ANNOTATION_WHITELIST, |
177 | 0 | annotationWhitelist); |
178 | 0 | aPrefs->SetAnnotationWhiteList(annotationWhitelist); |
179 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_ANNOTATION_WHITELIST_TEST_ENTRIES)) { |
180 | 0 | nsAutoCString annotationWhitelistExtraEntries; |
181 | 0 | Preferences::GetCString(URLCLASSIFIER_ANNOTATION_WHITELIST_TEST_ENTRIES, |
182 | 0 | annotationWhitelistExtraEntries); |
183 | 0 | aPrefs->SetAnnotationWhiteListExtraEntries(annotationWhitelistExtraEntries); |
184 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) { |
185 | 0 | nsCString trackingWhitelist; |
186 | 0 | Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST, |
187 | 0 | trackingWhitelist); |
188 | 0 | aPrefs->SetTrackingWhiteList(trackingWhitelist); |
189 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST_TEST_ENTRIES)) { |
190 | 0 | nsCString trackingWhitelistExtraEntries; |
191 | 0 | Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST_TEST_ENTRIES, |
192 | 0 | trackingWhitelistExtraEntries); |
193 | 0 | aPrefs->SetTrackingWhiteListExtraEntries(trackingWhitelistExtraEntries); |
194 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) { |
195 | 0 | nsCString trackingBlacklist; |
196 | 0 | Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE, trackingBlacklist); |
197 | 0 | aPrefs->SetTrackingBlackList(trackingBlacklist); |
198 | 0 | } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE_TEST_ENTRIES)) { |
199 | 0 | nsCString trackingBlacklistExtraEntries; |
200 | 0 | Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE_TEST_ENTRIES, trackingBlacklistExtraEntries); |
201 | 0 | aPrefs->SetTrackingBlackListExtraEntries(trackingBlacklistExtraEntries); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | void |
206 | | CachedPrefs::Init() |
207 | 0 | { |
208 | 0 | Preferences::AddBoolVarCache(&sAnnotateChannelEnabled, |
209 | 0 | "privacy.trackingprotection.annotate_channels"); |
210 | 0 | Preferences::AddBoolVarCache(&sLowerNetworkPriority, |
211 | 0 | "privacy.trackingprotection.lower_network_priority"); |
212 | 0 | Preferences::AddBoolVarCache(&sAllowListExample, |
213 | 0 | "channelclassifier.allowlist_example"); |
214 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
215 | 0 | URLCLASSIFIER_SKIP_HOSTNAMES, this); |
216 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
217 | 0 | URLCLASSIFIER_ANNOTATION_TABLE, this); |
218 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
219 | 0 | URLCLASSIFIER_ANNOTATION_TABLE_TEST_ENTRIES, this); |
220 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
221 | 0 | URLCLASSIFIER_ANNOTATION_WHITELIST, this); |
222 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
223 | 0 | URLCLASSIFIER_ANNOTATION_WHITELIST_TEST_ENTRIES, this); |
224 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
225 | 0 | URLCLASSIFIER_TRACKING_WHITELIST, this); |
226 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
227 | 0 | URLCLASSIFIER_TRACKING_WHITELIST_TEST_ENTRIES, this); |
228 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
229 | 0 | URLCLASSIFIER_TRACKING_TABLE, this); |
230 | 0 | Preferences::RegisterCallbackAndCall(CachedPrefs::OnPrefsChange, |
231 | 0 | URLCLASSIFIER_TRACKING_TABLE_TEST_ENTRIES, this); |
232 | 0 | } |
233 | | |
234 | | // static |
235 | | CachedPrefs* |
236 | | CachedPrefs::GetInstance() |
237 | 0 | { |
238 | 0 | if (!sInstance) { |
239 | 0 | sInstance = new CachedPrefs(); |
240 | 0 | sInstance->Init(); |
241 | 0 | ClearOnShutdown(&sInstance); |
242 | 0 | } |
243 | 0 | MOZ_ASSERT(sInstance); |
244 | 0 | return sInstance; |
245 | 0 | } |
246 | | |
247 | | CachedPrefs::CachedPrefs() |
248 | 0 | { |
249 | 0 | MOZ_COUNT_CTOR(CachedPrefs); |
250 | 0 | } |
251 | | |
252 | | CachedPrefs::~CachedPrefs() |
253 | 0 | { |
254 | 0 | MOZ_COUNT_DTOR(CachedPrefs); |
255 | 0 |
|
256 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_SKIP_HOSTNAMES, this); |
257 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_TABLE, this); |
258 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_TABLE_TEST_ENTRIES, this); |
259 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_WHITELIST, this); |
260 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_ANNOTATION_WHITELIST_TEST_ENTRIES, this); |
261 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST, this); |
262 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_WHITELIST_TEST_ENTRIES, this); |
263 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_TABLE, this); |
264 | 0 | Preferences::UnregisterCallback(CachedPrefs::OnPrefsChange, URLCLASSIFIER_TRACKING_TABLE_TEST_ENTRIES, this); |
265 | 0 | } |
266 | | } // anonymous namespace |
267 | | |
268 | | static nsresult |
269 | | IsThirdParty(nsIChannel* aChannel, bool* aResult) |
270 | 0 | { |
271 | 0 | NS_ENSURE_ARG(aResult); |
272 | 0 | *aResult = false; |
273 | 0 |
|
274 | 0 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil(); |
275 | 0 | if (NS_WARN_IF(!thirdPartyUtil)) { |
276 | 0 | return NS_ERROR_FAILURE; |
277 | 0 | } |
278 | 0 | |
279 | 0 | nsresult rv; |
280 | 0 | nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv); |
281 | 0 | if (NS_FAILED(rv) || !chan) { |
282 | 0 | LOG(("nsChannelClassifier: Not an HTTP channel")); |
283 | 0 | return NS_OK; |
284 | 0 | } |
285 | 0 | nsCOMPtr<nsIURI> chanURI; |
286 | 0 | rv = aChannel->GetURI(getter_AddRefs(chanURI)); |
287 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
288 | 0 |
|
289 | 0 | nsCOMPtr<nsIURI> topWinURI; |
290 | 0 | rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI)); |
291 | 0 | if (NS_FAILED(rv)) { |
292 | 0 | return rv; |
293 | 0 | } |
294 | 0 | if (!topWinURI) { |
295 | 0 | LOG(("nsChannelClassifier: No window URI\n")); |
296 | 0 | } |
297 | 0 |
|
298 | 0 | // Third party checks don't work for chrome:// URIs in mochitests, so just |
299 | 0 | // default to isThirdParty = true. We check isThirdPartyWindow to expand |
300 | 0 | // the list of domains that are considered first party (e.g., if |
301 | 0 | // facebook.com includes an iframe from fatratgames.com, all subsources |
302 | 0 | // included in that iframe are considered third-party with |
303 | 0 | // isThirdPartyChannel, even if they are not third-party w.r.t. |
304 | 0 | // facebook.com), and isThirdPartyChannel to prevent top-level navigations |
305 | 0 | // from being detected as third-party. |
306 | 0 | bool isThirdPartyChannel = true; |
307 | 0 | bool isThirdPartyWindow = true; |
308 | 0 | thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow); |
309 | 0 | thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel); |
310 | 0 |
|
311 | 0 | *aResult = isThirdPartyWindow && isThirdPartyChannel; |
312 | 0 | return NS_OK; |
313 | 0 | } |
314 | | |
315 | | static void |
316 | | SetIsTrackingResourceHelper(nsIChannel* aChannel, bool aIsThirdParty) |
317 | 0 | { |
318 | 0 | MOZ_ASSERT(aChannel); |
319 | 0 |
|
320 | 0 | nsCOMPtr<nsIParentChannel> parentChannel; |
321 | 0 | NS_QueryNotificationCallbacks(aChannel, parentChannel); |
322 | 0 | if (parentChannel) { |
323 | 0 | // This channel is a parent-process proxy for a child process |
324 | 0 | // request. We should notify the child process as well. |
325 | 0 | parentChannel->NotifyTrackingResource(aIsThirdParty); |
326 | 0 | } |
327 | 0 |
|
328 | 0 | RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel); |
329 | 0 | if (httpChannel) { |
330 | 0 | httpChannel->SetIsTrackingResource(aIsThirdParty); |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | static void |
335 | | LowerPriorityHelper(nsIChannel* aChannel) |
336 | 0 | { |
337 | 0 | MOZ_ASSERT(aChannel); |
338 | 0 |
|
339 | 0 | bool isBlockingResource = false; |
340 | 0 |
|
341 | 0 | nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(aChannel)); |
342 | 0 | if (cos) { |
343 | 0 | if (nsContentUtils::IsTailingEnabled()) { |
344 | 0 | uint32_t cosFlags = 0; |
345 | 0 | cos->GetClassFlags(&cosFlags); |
346 | 0 | isBlockingResource = cosFlags & (nsIClassOfService::UrgentStart | |
347 | 0 | nsIClassOfService::Leader | |
348 | 0 | nsIClassOfService::Unblocked); |
349 | 0 |
|
350 | 0 | // Requests not allowed to be tailed are usually those with higher |
351 | 0 | // prioritization. That overweights being a tracker: don't throttle |
352 | 0 | // them when not in background. |
353 | 0 | if (!(cosFlags & nsIClassOfService::TailForbidden)) { |
354 | 0 | cos->AddClassFlags(nsIClassOfService::Throttleable); |
355 | 0 | } |
356 | 0 | } else { |
357 | 0 | // Yes, we even don't want to evaluate the isBlockingResource when tailing is off |
358 | 0 | // see bug 1395525. |
359 | 0 |
|
360 | 0 | cos->AddClassFlags(nsIClassOfService::Throttleable); |
361 | 0 | } |
362 | 0 | } |
363 | 0 |
|
364 | 0 | if (!isBlockingResource) { |
365 | 0 | nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aChannel); |
366 | 0 | if (p) { |
367 | 0 | if (LOG_ENABLED()) { |
368 | 0 | nsCOMPtr<nsIURI> uri; |
369 | 0 | aChannel->GetURI(getter_AddRefs(uri)); |
370 | 0 | nsAutoCString spec; |
371 | 0 | uri->GetAsciiSpec(spec); |
372 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
373 | 0 | LOG(("Setting PRIORITY_LOWEST for channel[%p] (%s)", |
374 | 0 | aChannel, spec.get())); |
375 | 0 | } |
376 | 0 | p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | NS_IMPL_ISUPPORTS(nsChannelClassifier, |
382 | | nsIURIClassifierCallback, |
383 | | nsIObserver) |
384 | | |
385 | | nsChannelClassifier::nsChannelClassifier(nsIChannel *aChannel) |
386 | | : mIsAllowListed(false), |
387 | | mSuspendedChannel(false), |
388 | | mChannel(aChannel), |
389 | | mTrackingProtectionEnabled(Nothing()), |
390 | | mTrackingAnnotationEnabled(Nothing()) |
391 | 0 | { |
392 | 0 | LOG_DEBUG(("nsChannelClassifier::nsChannelClassifier %p", this)); |
393 | 0 | MOZ_ASSERT(mChannel); |
394 | 0 | } |
395 | | |
396 | | nsChannelClassifier::~nsChannelClassifier() |
397 | 0 | { |
398 | 0 | LOG_DEBUG(("nsChannelClassifier::~nsChannelClassifier %p", this)); |
399 | 0 | } |
400 | | |
401 | | bool |
402 | | nsChannelClassifier::ShouldEnableTrackingProtection() |
403 | 0 | { |
404 | 0 | if (mTrackingProtectionEnabled) { |
405 | 0 | return mTrackingProtectionEnabled.value(); |
406 | 0 | } |
407 | 0 | |
408 | 0 | mTrackingProtectionEnabled = Some(false); |
409 | 0 |
|
410 | 0 | nsCOMPtr<nsILoadContext> loadContext; |
411 | 0 | NS_QueryNotificationCallbacks(mChannel, loadContext); |
412 | 0 | if (loadContext && loadContext->UseTrackingProtection()) { |
413 | 0 | Unused << ShouldEnableTrackingProtectionInternal( |
414 | 0 | mChannel, false, mTrackingProtectionEnabled.ptr()); |
415 | 0 | } |
416 | 0 |
|
417 | 0 | return mTrackingProtectionEnabled.value(); |
418 | 0 | } |
419 | | |
420 | | bool |
421 | | nsChannelClassifier::ShouldEnableTrackingAnnotation() |
422 | 0 | { |
423 | 0 | if (mTrackingAnnotationEnabled) { |
424 | 0 | return mTrackingAnnotationEnabled.value(); |
425 | 0 | } |
426 | 0 | |
427 | 0 | mTrackingAnnotationEnabled = Some(false); |
428 | 0 |
|
429 | 0 | if (!CachedPrefs::GetInstance()->IsAnnotateChannelEnabled()) { |
430 | 0 | return mTrackingAnnotationEnabled.value(); |
431 | 0 | } |
432 | 0 | |
433 | 0 | // If tracking protection is enabled, no need to do channel annotation. |
434 | 0 | if (ShouldEnableTrackingProtection()) { |
435 | 0 | return mTrackingAnnotationEnabled.value(); |
436 | 0 | } |
437 | 0 | |
438 | 0 | // To prevent calling ShouldEnableTrackingProtectionInternal() again, |
439 | 0 | // check loadContext->UseTrackingProtection() here. |
440 | 0 | // If loadContext->UseTrackingProtection() is true, here it means |
441 | 0 | // ShouldEnableTrackingProtectionInternal() has been called before in |
442 | 0 | // ShouldEnableTrackingProtection() above and the result is false. |
443 | 0 | // So, we can just return false. |
444 | 0 | nsCOMPtr<nsILoadContext> loadContext; |
445 | 0 | NS_QueryNotificationCallbacks(mChannel, loadContext); |
446 | 0 | if (loadContext && loadContext->UseTrackingProtection()) { |
447 | 0 | return mTrackingAnnotationEnabled.value(); |
448 | 0 | } |
449 | 0 | |
450 | 0 | Unused << ShouldEnableTrackingProtectionInternal( |
451 | 0 | mChannel, true, mTrackingAnnotationEnabled.ptr()); |
452 | 0 |
|
453 | 0 | return mTrackingAnnotationEnabled.value(); |
454 | 0 | } |
455 | | |
456 | | nsresult |
457 | | nsChannelClassifier::ShouldEnableTrackingProtectionInternal( |
458 | | nsIChannel *aChannel, |
459 | | bool aAnnotationsOnly, |
460 | | bool *result) |
461 | 0 | { |
462 | 0 | // Should only be called in the parent process. |
463 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
464 | 0 |
|
465 | 0 | NS_ENSURE_ARG(result); |
466 | 0 | *result = false; |
467 | 0 |
|
468 | 0 | nsresult rv; |
469 | 0 | nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv); |
470 | 0 | if (NS_FAILED(rv) || !chan) { |
471 | 0 | LOG(("nsChannelClassifier[%p]: Not an HTTP channel", this)); |
472 | 0 | return NS_OK; |
473 | 0 | } |
474 | 0 |
|
475 | 0 | nsCOMPtr<nsIURI> chanURI; |
476 | 0 | rv = aChannel->GetURI(getter_AddRefs(chanURI)); |
477 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
478 | 0 |
|
479 | 0 | // Only perform third-party checks for tracking protection |
480 | 0 | if (!aAnnotationsOnly) { |
481 | 0 | bool isThirdParty = false; |
482 | 0 | rv = IsThirdParty(aChannel, &isThirdParty); |
483 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
484 | 0 | LOG(("nsChannelClassifier[%p]: IsThirdParty() failed", this)); |
485 | 0 | return NS_OK; |
486 | 0 | } |
487 | 0 | if (!isThirdParty) { |
488 | 0 | *result = false; |
489 | 0 | if (LOG_ENABLED()) { |
490 | 0 | nsCString spec = chanURI->GetSpecOrDefault(); |
491 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
492 | 0 | LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks " |
493 | 0 | "for first party or top-level load channel[%p] with uri %s", |
494 | 0 | this, aChannel, spec.get())); |
495 | 0 | } |
496 | 0 | return NS_OK; |
497 | 0 | } |
498 | 0 | } |
499 | 0 |
|
500 | 0 | if (AddonMayLoad(aChannel, chanURI)) { |
501 | 0 | return NS_OK; |
502 | 0 | } |
503 | 0 | |
504 | 0 | nsCOMPtr<nsIIOService> ios = services::GetIOService(); |
505 | 0 | NS_ENSURE_TRUE(ios, NS_ERROR_FAILURE); |
506 | 0 |
|
507 | 0 | nsCOMPtr<nsIURI> topWinURI; |
508 | 0 | rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI)); |
509 | 0 | if (NS_FAILED(rv)) { |
510 | 0 | return rv; |
511 | 0 | } |
512 | 0 | |
513 | 0 | if (!topWinURI && CachedPrefs::GetInstance()->IsAllowListExample()) { |
514 | 0 | LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this)); |
515 | 0 | rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"), |
516 | 0 | nullptr, nullptr, getter_AddRefs(topWinURI)); |
517 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
518 | 0 | } |
519 | 0 |
|
520 | 0 | rv = AntiTrackingCommon::IsOnContentBlockingAllowList(topWinURI, |
521 | 0 | aAnnotationsOnly ? |
522 | 0 | AntiTrackingCommon::eTrackingAnnotations : |
523 | 0 | AntiTrackingCommon::eTrackingProtection, |
524 | 0 | mIsAllowListed); |
525 | 0 | if (NS_FAILED(rv)) { |
526 | 0 | return rv; // normal for some loads, no need to print a warning |
527 | 0 | } |
528 | 0 | |
529 | 0 | if (mIsAllowListed) { |
530 | 0 | *result = false; |
531 | 0 | if (LOG_ENABLED()) { |
532 | 0 | nsCString chanSpec = chanURI->GetSpecOrDefault(); |
533 | 0 | chanSpec.Truncate(std::min(chanSpec.Length(), sMaxSpecLength)); |
534 | 0 | LOG(("nsChannelClassifier[%p]: User override on channel[%p] (%s)", |
535 | 0 | this, aChannel, chanSpec.get())); |
536 | 0 | } |
537 | 0 | } else { |
538 | 0 | *result = true; |
539 | 0 | } |
540 | 0 |
|
541 | 0 | // Tracking protection will be enabled so return without updating |
542 | 0 | // the security state. If any channels are subsequently cancelled |
543 | 0 | // (page elements blocked) the state will be then updated. |
544 | 0 | if (*result) { |
545 | 0 | if (LOG_ENABLED()) { |
546 | 0 | nsCString chanSpec = chanURI->GetSpecOrDefault(); |
547 | 0 | chanSpec.Truncate(std::min(chanSpec.Length(), sMaxSpecLength)); |
548 | 0 | nsCString topWinSpec = topWinURI->GetSpecOrDefault(); |
549 | 0 | topWinSpec.Truncate(std::min(topWinSpec.Length(), sMaxSpecLength)); |
550 | 0 | LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on " |
551 | 0 | "channel[%p] with uri %s for toplevel window uri %s", this, |
552 | 0 | aChannel, chanSpec.get(), topWinSpec.get())); |
553 | 0 | } |
554 | 0 | return NS_OK; |
555 | 0 | } |
556 | 0 |
|
557 | 0 | // Tracking protection will be disabled so update the security state |
558 | 0 | // of the document and fire a secure change event. If we can't get the |
559 | 0 | // window for the channel, then the shield won't show up so we can't send |
560 | 0 | // an event to the securityUI anyway. |
561 | 0 | return NotifyTrackingProtectionDisabled(aChannel); |
562 | 0 | } |
563 | | |
564 | | bool |
565 | | nsChannelClassifier::AddonMayLoad(nsIChannel *aChannel, nsIURI *aUri) |
566 | 0 | { |
567 | 0 | nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo(); |
568 | 0 | if (!channelLoadInfo) |
569 | 0 | return false; |
570 | 0 | |
571 | 0 | // loadingPrincipal is used here to ensure we are loading into an |
572 | 0 | // addon principal. This allows an addon, with explicit permission, to |
573 | 0 | // call out to API endpoints that may otherwise get blocked. |
574 | 0 | nsIPrincipal* loadingPrincipal = channelLoadInfo->LoadingPrincipal(); |
575 | 0 | if (!loadingPrincipal) |
576 | 0 | return false; |
577 | 0 | |
578 | 0 | return BasePrincipal::Cast(loadingPrincipal)->AddonAllowsLoad(aUri, true); |
579 | 0 | } |
580 | | |
581 | | // static |
582 | | nsresult |
583 | | nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel) |
584 | 0 | { |
585 | 0 | // Can be called in EITHER the parent or child process. |
586 | 0 | nsCOMPtr<nsIParentChannel> parentChannel; |
587 | 0 | NS_QueryNotificationCallbacks(aChannel, parentChannel); |
588 | 0 | if (parentChannel) { |
589 | 0 | // This channel is a parent-process proxy for a child process request. |
590 | 0 | // Tell the child process channel to do this instead. |
591 | 0 | parentChannel->NotifyTrackingProtectionDisabled(); |
592 | 0 | return NS_OK; |
593 | 0 | } |
594 | 0 | |
595 | 0 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = |
596 | 0 | services::GetThirdPartyUtil(); |
597 | 0 | if (NS_WARN_IF(!thirdPartyUtil)) { |
598 | 0 | return NS_ERROR_FAILURE; |
599 | 0 | } |
600 | 0 | |
601 | 0 | nsresult rv; |
602 | 0 | nsCOMPtr<mozIDOMWindowProxy> win; |
603 | 0 | rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win)); |
604 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
605 | 0 |
|
606 | 0 | auto* pwin = nsPIDOMWindowOuter::From(win); |
607 | 0 | nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell(); |
608 | 0 | if (!docShell) { |
609 | 0 | return NS_OK; |
610 | 0 | } |
611 | 0 | nsCOMPtr<nsIDocument> doc = docShell->GetDocument(); |
612 | 0 | NS_ENSURE_TRUE(doc, NS_OK); |
613 | 0 |
|
614 | 0 | // Notify nsIWebProgressListeners of this security event. |
615 | 0 | // Can be used to change the UI state. |
616 | 0 | nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv); |
617 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
618 | 0 | uint32_t state = 0; |
619 | 0 | nsCOMPtr<nsISecureBrowserUI> securityUI; |
620 | 0 | docShell->GetSecurityUI(getter_AddRefs(securityUI)); |
621 | 0 | if (!securityUI) { |
622 | 0 | return NS_OK; |
623 | 0 | } |
624 | 0 | doc->SetHasTrackingContentLoaded(true); |
625 | 0 | securityUI->GetState(&state); |
626 | 0 | state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT; |
627 | 0 | eventSink->OnSecurityChange(nullptr, state); |
628 | 0 |
|
629 | 0 | return NS_OK; |
630 | 0 | } |
631 | | |
632 | | void |
633 | | nsChannelClassifier::Start() |
634 | 0 | { |
635 | 0 | nsresult rv = StartInternal(); |
636 | 0 | if (NS_FAILED(rv)) { |
637 | 0 | // If we aren't getting a callback for any reason, assume a good verdict and |
638 | 0 | // make sure we resume the channel if necessary. |
639 | 0 | OnClassifyComplete(NS_OK, NS_LITERAL_CSTRING(""),NS_LITERAL_CSTRING(""), |
640 | 0 | NS_LITERAL_CSTRING("")); |
641 | 0 | } |
642 | 0 | } |
643 | | |
644 | | nsresult |
645 | | nsChannelClassifier::StartInternal() |
646 | 0 | { |
647 | 0 | // Should only be called in the parent process. |
648 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
649 | 0 |
|
650 | 0 | // Don't bother to run the classifier on a load that has already failed. |
651 | 0 | // (this might happen after a redirect) |
652 | 0 | nsresult status; |
653 | 0 | mChannel->GetStatus(&status); |
654 | 0 | if (NS_FAILED(status)) |
655 | 0 | return status; |
656 | 0 | |
657 | 0 | // Don't bother to run the classifier on a cached load that was |
658 | 0 | // previously classified as good. |
659 | 0 | if (HasBeenClassified(mChannel)) { |
660 | 0 | return NS_ERROR_UNEXPECTED; |
661 | 0 | } |
662 | 0 | |
663 | 0 | nsCOMPtr<nsIURI> uri; |
664 | 0 | nsresult rv = mChannel->GetURI(getter_AddRefs(uri)); |
665 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
666 | 0 |
|
667 | 0 | // Don't bother checking certain types of URIs. |
668 | 0 | bool isAbout = false; |
669 | 0 | rv = uri->SchemeIs("about", &isAbout); |
670 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
671 | 0 | if (isAbout) return NS_ERROR_UNEXPECTED; |
672 | 0 | |
673 | 0 | bool hasFlags; |
674 | 0 | rv = NS_URIChainHasFlags(uri, |
675 | 0 | nsIProtocolHandler::URI_DANGEROUS_TO_LOAD, |
676 | 0 | &hasFlags); |
677 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
678 | 0 | if (hasFlags) return NS_ERROR_UNEXPECTED; |
679 | 0 | |
680 | 0 | rv = NS_URIChainHasFlags(uri, |
681 | 0 | nsIProtocolHandler::URI_IS_LOCAL_FILE, |
682 | 0 | &hasFlags); |
683 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
684 | 0 | if (hasFlags) return NS_ERROR_UNEXPECTED; |
685 | 0 | |
686 | 0 | rv = NS_URIChainHasFlags(uri, |
687 | 0 | nsIProtocolHandler::URI_IS_UI_RESOURCE, |
688 | 0 | &hasFlags); |
689 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
690 | 0 | if (hasFlags) return NS_ERROR_UNEXPECTED; |
691 | 0 | |
692 | 0 | rv = NS_URIChainHasFlags(uri, |
693 | 0 | nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, |
694 | 0 | &hasFlags); |
695 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
696 | 0 | if (hasFlags) return NS_ERROR_UNEXPECTED; |
697 | 0 | |
698 | 0 | nsCString skipHostnames = CachedPrefs::GetInstance()->GetSkipHostnames(); |
699 | 0 | if (!skipHostnames.IsEmpty()) { |
700 | 0 | LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s", |
701 | 0 | this, skipHostnames.get())); |
702 | 0 | if (IsHostnameWhitelisted(uri, skipHostnames)) { |
703 | 0 | return NS_ERROR_UNEXPECTED; |
704 | 0 | } |
705 | 0 | } |
706 | 0 | |
707 | 0 | nsCOMPtr<nsIURIClassifier> uriClassifier = |
708 | 0 | do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); |
709 | 0 | if (rv == NS_ERROR_FACTORY_NOT_REGISTERED || |
710 | 0 | rv == NS_ERROR_NOT_AVAILABLE) { |
711 | 0 | // no URI classifier, ignore this failure. |
712 | 0 | return NS_ERROR_NOT_AVAILABLE; |
713 | 0 | } |
714 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
715 | 0 |
|
716 | 0 | nsCOMPtr<nsIScriptSecurityManager> securityManager = |
717 | 0 | do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); |
718 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
719 | 0 |
|
720 | 0 | nsCOMPtr<nsIPrincipal> principal; |
721 | 0 | rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal)); |
722 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
723 | 0 |
|
724 | 0 | bool expectCallback; |
725 | 0 | if (LOG_ENABLED()) { |
726 | 0 | nsCOMPtr<nsIURI> principalURI; |
727 | 0 | principal->GetURI(getter_AddRefs(principalURI)); |
728 | 0 | nsCString spec = principalURI->GetSpecOrDefault(); |
729 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
730 | 0 | LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel[%p]", |
731 | 0 | this, spec.get(), mChannel.get())); |
732 | 0 | } |
733 | 0 | // The classify is running in parent process, no need to give a valid event |
734 | 0 | // target |
735 | 0 | rv = uriClassifier->Classify(principal, nullptr, |
736 | 0 | false, |
737 | 0 | this, &expectCallback); |
738 | 0 | if (NS_FAILED(rv)) { |
739 | 0 | return rv; |
740 | 0 | } |
741 | 0 | |
742 | 0 | if (expectCallback) { |
743 | 0 | // Suspend the channel, it will be resumed when we get the classifier |
744 | 0 | // callback. |
745 | 0 | rv = mChannel->Suspend(); |
746 | 0 | if (NS_FAILED(rv)) { |
747 | 0 | // Some channels (including nsJSChannel) fail on Suspend. This |
748 | 0 | // shouldn't be fatal, but will prevent malware from being |
749 | 0 | // blocked on these channels. |
750 | 0 | LOG_WARN(("nsChannelClassifier[%p]: Couldn't suspend channel", this)); |
751 | 0 | return rv; |
752 | 0 | } |
753 | 0 |
|
754 | 0 | mSuspendedChannel = true; |
755 | 0 | LOG_DEBUG(("nsChannelClassifier[%p]: suspended channel %p", |
756 | 0 | this, mChannel.get())); |
757 | 0 | } else { |
758 | 0 | LOG(("nsChannelClassifier[%p]: not expecting callback", this)); |
759 | 0 | return NS_ERROR_FAILURE; |
760 | 0 | } |
761 | 0 |
|
762 | 0 | // Add an observer for shutdown |
763 | 0 | AddShutdownObserver(); |
764 | 0 | return NS_OK; |
765 | 0 | } |
766 | | |
767 | | bool |
768 | | nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri, |
769 | | const nsACString &aWhitelisted) |
770 | 0 | { |
771 | 0 | nsAutoCString host; |
772 | 0 | nsresult rv = aUri->GetHost(host); |
773 | 0 | if (NS_FAILED(rv) || host.IsEmpty()) { |
774 | 0 | return false; |
775 | 0 | } |
776 | 0 | ToLowerCase(host); |
777 | 0 |
|
778 | 0 | nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ','); |
779 | 0 | while (tokenizer.hasMoreTokens()) { |
780 | 0 | const nsACString& token = tokenizer.nextToken(); |
781 | 0 | if (token.Equals(host)) { |
782 | 0 | LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)", |
783 | 0 | this, host.get())); |
784 | 0 | return true; |
785 | 0 | } |
786 | 0 | } |
787 | 0 |
|
788 | 0 | return false; |
789 | 0 | } |
790 | | |
791 | | // Note in the cache entry that this URL was classified, so that future |
792 | | // cached loads don't need to be checked. |
793 | | void |
794 | | nsChannelClassifier::MarkEntryClassified(nsresult status) |
795 | 0 | { |
796 | 0 | // Should only be called in the parent process. |
797 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
798 | 0 |
|
799 | 0 | // Don't cache tracking classifications because we support allowlisting. |
800 | 0 | if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) { |
801 | 0 | return; |
802 | 0 | } |
803 | 0 | |
804 | 0 | if (LOG_ENABLED()) { |
805 | 0 | nsAutoCString errorName; |
806 | 0 | GetErrorName(status, errorName); |
807 | 0 | nsCOMPtr<nsIURI> uri; |
808 | 0 | mChannel->GetURI(getter_AddRefs(uri)); |
809 | 0 | nsAutoCString spec; |
810 | 0 | uri->GetAsciiSpec(spec); |
811 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
812 | 0 | LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s", |
813 | 0 | errorName.get(), spec.get())); |
814 | 0 | } |
815 | 0 |
|
816 | 0 | nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel); |
817 | 0 | if (!cachingChannel) { |
818 | 0 | return; |
819 | 0 | } |
820 | 0 | |
821 | 0 | nsCOMPtr<nsISupports> cacheToken; |
822 | 0 | cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); |
823 | 0 | if (!cacheToken) { |
824 | 0 | return; |
825 | 0 | } |
826 | 0 | |
827 | 0 | nsCOMPtr<nsICacheEntry> cacheEntry = |
828 | 0 | do_QueryInterface(cacheToken); |
829 | 0 | if (!cacheEntry) { |
830 | 0 | return; |
831 | 0 | } |
832 | 0 | |
833 | 0 | cacheEntry->SetMetaDataElement("necko:classified", |
834 | 0 | NS_SUCCEEDED(status) ? "1" : nullptr); |
835 | 0 | } |
836 | | |
837 | | bool |
838 | | nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel) |
839 | 0 | { |
840 | 0 | // Should only be called in the parent process. |
841 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
842 | 0 |
|
843 | 0 | nsCOMPtr<nsICachingChannel> cachingChannel = |
844 | 0 | do_QueryInterface(aChannel); |
845 | 0 | if (!cachingChannel) { |
846 | 0 | return false; |
847 | 0 | } |
848 | 0 | |
849 | 0 | // Only check the tag if we are loading from the cache without |
850 | 0 | // validation. |
851 | 0 | bool fromCache; |
852 | 0 | if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) { |
853 | 0 | return false; |
854 | 0 | } |
855 | 0 | |
856 | 0 | nsCOMPtr<nsISupports> cacheToken; |
857 | 0 | cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); |
858 | 0 | if (!cacheToken) { |
859 | 0 | return false; |
860 | 0 | } |
861 | 0 | |
862 | 0 | nsCOMPtr<nsICacheEntry> cacheEntry = |
863 | 0 | do_QueryInterface(cacheToken); |
864 | 0 | if (!cacheEntry) { |
865 | 0 | return false; |
866 | 0 | } |
867 | 0 | |
868 | 0 | nsCString tag; |
869 | 0 | cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag)); |
870 | 0 | return tag.EqualsLiteral("1"); |
871 | 0 | } |
872 | | |
873 | | // static |
874 | | nsresult |
875 | | nsChannelClassifier::SetBlockedContent(nsIChannel *channel, |
876 | | nsresult aErrorCode, |
877 | | const nsACString& aList, |
878 | | const nsACString& aProvider, |
879 | | const nsACString& aFullHash) |
880 | 0 | { |
881 | 0 | NS_ENSURE_ARG(!aList.IsEmpty()); |
882 | 0 |
|
883 | 0 | // Can be called in EITHER the parent or child process. |
884 | 0 | nsCOMPtr<nsIParentChannel> parentChannel; |
885 | 0 | NS_QueryNotificationCallbacks(channel, parentChannel); |
886 | 0 | if (parentChannel) { |
887 | 0 | // This channel is a parent-process proxy for a child process request. |
888 | 0 | // Tell the child process channel to do this instead. |
889 | 0 | parentChannel->SetClassifierMatchedInfo(aList, aProvider, aFullHash); |
890 | 0 | return NS_OK; |
891 | 0 | } |
892 | 0 | |
893 | 0 | nsresult rv; |
894 | 0 | nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel, &rv); |
895 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
896 | 0 |
|
897 | 0 | if (classifiedChannel) { |
898 | 0 | classifiedChannel->SetMatchedInfo(aList, aProvider, aFullHash); |
899 | 0 | } |
900 | 0 |
|
901 | 0 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = |
902 | 0 | services::GetThirdPartyUtil(); |
903 | 0 | if (NS_WARN_IF(!thirdPartyUtil)) { |
904 | 0 | return NS_OK; |
905 | 0 | } |
906 | 0 | |
907 | 0 | nsCOMPtr<mozIDOMWindowProxy> win; |
908 | 0 | rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win)); |
909 | 0 | NS_ENSURE_SUCCESS(rv, NS_OK); |
910 | 0 | auto* pwin = nsPIDOMWindowOuter::From(win); |
911 | 0 | nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell(); |
912 | 0 | if (!docShell) { |
913 | 0 | return NS_OK; |
914 | 0 | } |
915 | 0 | nsCOMPtr<nsIDocument> doc = docShell->GetDocument(); |
916 | 0 | NS_ENSURE_TRUE(doc, NS_OK); |
917 | 0 |
|
918 | 0 | unsigned state; |
919 | 0 | if (aErrorCode == NS_ERROR_TRACKING_URI) { |
920 | 0 | state = nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT; |
921 | 0 | } else { |
922 | 0 | state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT; |
923 | 0 | } |
924 | 0 | pwin->NotifyContentBlockingState(state, channel); |
925 | 0 |
|
926 | 0 | // Log a warning to the web console. |
927 | 0 | nsCOMPtr<nsIURI> uri; |
928 | 0 | channel->GetURI(getter_AddRefs(uri)); |
929 | 0 | NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault()); |
930 | 0 | const char16_t* params[] = { spec.get() }; |
931 | 0 | const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ? |
932 | 0 | "TrackerUriBlocked" : "UnsafeUriBlocked"; |
933 | 0 | nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI) ? |
934 | 0 | NS_LITERAL_CSTRING("Tracking Protection") : |
935 | 0 | NS_LITERAL_CSTRING("Safe Browsing"); |
936 | 0 |
|
937 | 0 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
938 | 0 | category, |
939 | 0 | doc, |
940 | 0 | nsContentUtils::eNECKO_PROPERTIES, |
941 | 0 | message, |
942 | 0 | params, ArrayLength(params)); |
943 | 0 |
|
944 | 0 | return NS_OK; |
945 | 0 | } |
946 | | |
947 | | namespace { |
948 | | |
949 | | // This class is designed to get the results of checking blacklist and whitelist. |
950 | | // |mExpect*WhitelistResult| are used to indicate that |OnClassifyComplete| is called |
951 | | // for the result of blacklist or whitelist check. |
952 | | class TrackingURICallback final : public nsIURIClassifierCallback { |
953 | | public: |
954 | | NS_DECL_THREADSAFE_ISUPPORTS |
955 | | NS_DECL_NSIURICLASSIFIERCALLBACK |
956 | | |
957 | | explicit TrackingURICallback(nsChannelClassifier* aChannelClassifier, |
958 | | std::function<void()>&& aCallback) |
959 | | : mChannelClassifier(aChannelClassifier) |
960 | | , mChannelCallback(std::move(aCallback)) |
961 | | , mExpectAnnotationWhitelistResult(false) |
962 | | , mExpectTrackingWhitelistResult(false) |
963 | 0 | { |
964 | 0 | MOZ_ASSERT(mChannelClassifier); |
965 | 0 | } |
966 | | |
967 | | private: |
968 | 0 | ~TrackingURICallback() = default; |
969 | | nsresult OnBlacklistResult(nsresult aErrorCode, bool aInTrackingTable, |
970 | | bool aInAnnotationTable); |
971 | | nsresult OnWhitelistResult(nsresult aErrorCode); |
972 | | void OnTrackerFound(nsresult aErrorCode); |
973 | | |
974 | | RefPtr<nsChannelClassifier> mChannelClassifier; |
975 | | std::function<void()> mChannelCallback; |
976 | | bool mExpectAnnotationWhitelistResult; |
977 | | bool mExpectTrackingWhitelistResult; |
978 | | |
979 | | nsCString mList; |
980 | | nsCString mProvider; |
981 | | nsCString mFullHash; |
982 | | }; |
983 | | |
984 | | NS_IMPL_ISUPPORTS(TrackingURICallback, nsIURIClassifierCallback) |
985 | | |
986 | | // This function gets called whenever we receive the results (match or |
987 | | // no match) from an asynchronous list (blacklist or whitelist) lookup. |
988 | | /*virtual*/ nsresult |
989 | | TrackingURICallback::OnClassifyComplete(nsresult aErrorCode, |
990 | | const nsACString& aLists, |
991 | | const nsACString& aProvider, |
992 | | const nsACString& aFullHash) |
993 | 0 | { |
994 | 0 | MOZ_ASSERT(aErrorCode == NS_OK); |
995 | 0 |
|
996 | 0 | const bool shouldEnableTrackingProtection = |
997 | 0 | mChannelClassifier->ShouldEnableTrackingProtection(); |
998 | 0 | const bool shouldEnableTrackingAnnotation = |
999 | 0 | mChannelClassifier->ShouldEnableTrackingAnnotation(); |
1000 | 0 | MOZ_ASSERT(shouldEnableTrackingProtection || shouldEnableTrackingAnnotation); |
1001 | 0 |
|
1002 | 0 | LOG(("TrackingURICallback[%p]:OnClassifyComplete " |
1003 | 0 | "shouldEnableTrackingProtection=%d, shouldEnableTrackingAnnnotation=%d", |
1004 | 0 | mChannelClassifier.get(), shouldEnableTrackingProtection, |
1005 | 0 | shouldEnableTrackingAnnotation)); |
1006 | 0 |
|
1007 | 0 | // Figure out whether we are receiving the results of a blacklist or |
1008 | 0 | // a whitelist lookup |
1009 | 0 |
|
1010 | 0 | if (!mExpectAnnotationWhitelistResult && !mExpectTrackingWhitelistResult) { |
1011 | 0 | // Blacklist lookup results |
1012 | 0 |
|
1013 | 0 | mList = aLists; |
1014 | 0 | mProvider = aProvider; |
1015 | 0 | mFullHash = aFullHash; |
1016 | 0 |
|
1017 | 0 | if (aLists.IsEmpty()) { |
1018 | 0 | return OnBlacklistResult(NS_OK, false, false); // not a tracker |
1019 | 0 | } |
1020 | 0 | |
1021 | 0 | // Figure out which of the blacklist(s) matched |
1022 | 0 | |
1023 | 0 | const nsCString annotationTable = |
1024 | 0 | CachedPrefs::GetInstance()->GetAnnotationBlackList(); |
1025 | 0 | const nsCString trackingTable = |
1026 | 0 | CachedPrefs::GetInstance()->GetTrackingBlackList(); |
1027 | 0 |
|
1028 | 0 | bool inTrackingTable = false; |
1029 | 0 | bool inAnnotationTable = false; |
1030 | 0 |
|
1031 | 0 |
|
1032 | 0 | nsCCharSeparatedTokenizer tokenizer(aLists, ','); |
1033 | 0 | while (tokenizer.hasMoreTokens()) { |
1034 | 0 | const nsACString& list = tokenizer.nextToken(); |
1035 | 0 | if (shouldEnableTrackingProtection && !inTrackingTable && |
1036 | 0 | (list == TABLE_TRACKING_BLACKLIST_PREF || |
1037 | 0 | FindInReadable(list, trackingTable))) { |
1038 | 0 | inTrackingTable = true; |
1039 | 0 | } |
1040 | 0 | if (shouldEnableTrackingAnnotation && !inAnnotationTable && |
1041 | 0 | (list == TABLE_ANNOTATION_BLACKLIST_PREF || |
1042 | 0 | FindInReadable(list, annotationTable))) { |
1043 | 0 | inAnnotationTable = true; |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 |
|
1047 | 0 | MOZ_ASSERT(shouldEnableTrackingProtection || !inTrackingTable); |
1048 | 0 | MOZ_ASSERT(shouldEnableTrackingAnnotation || !inAnnotationTable); |
1049 | 0 |
|
1050 | 0 | if ((shouldEnableTrackingProtection && inTrackingTable) || |
1051 | 0 | (shouldEnableTrackingAnnotation && inAnnotationTable)) { |
1052 | 0 | // Valid blacklist result, need to check the whitelist(s) next |
1053 | 0 | return OnBlacklistResult(NS_ERROR_MAYBE_TRACKING_URI, inTrackingTable, |
1054 | 0 | inAnnotationTable); |
1055 | 0 | } |
1056 | 0 | |
1057 | 0 | if (NS_WARN_IF(!inTrackingTable && !inAnnotationTable)) { |
1058 | 0 | MOZ_ASSERT(false, "The matching lists should be tracking-related."); |
1059 | 0 | } |
1060 | 0 |
|
1061 | 0 | // Nothing to annotate or block |
1062 | 0 | return OnBlacklistResult(NS_OK, inTrackingTable, inAnnotationTable); |
1063 | 0 | } |
1064 | 0 |
|
1065 | 0 | // Whitelist lookup results |
1066 | 0 |
|
1067 | 0 | MOZ_ASSERT(shouldEnableTrackingProtection || !mExpectTrackingWhitelistResult); |
1068 | 0 | MOZ_ASSERT(shouldEnableTrackingAnnotation || !mExpectAnnotationWhitelistResult); |
1069 | 0 |
|
1070 | 0 | bool isTracker = mExpectTrackingWhitelistResult; |
1071 | 0 | bool isAnnotation = mExpectAnnotationWhitelistResult; |
1072 | 0 | MOZ_ASSERT(isTracker || isAnnotation); |
1073 | 0 |
|
1074 | 0 | if (!aLists.IsEmpty()) { |
1075 | 0 | // Figure out which of the whitelist(s) matched |
1076 | 0 |
|
1077 | 0 | const nsCString annotationWhitelistTable = |
1078 | 0 | CachedPrefs::GetInstance()->GetAnnotationWhiteList(); |
1079 | 0 | const nsCString trackingWhitelistTable = |
1080 | 0 | CachedPrefs::GetInstance()->GetTrackingWhiteList(); |
1081 | 0 |
|
1082 | 0 | nsCCharSeparatedTokenizer tokenizer(aLists, ','); |
1083 | 0 | while (tokenizer.hasMoreTokens() && (isTracker || isAnnotation)) { |
1084 | 0 | const nsACString& list = tokenizer.nextToken(); |
1085 | 0 | if (isTracker && |
1086 | 0 | (list == TABLE_TRACKING_WHITELIST_PREF || |
1087 | 0 | FindInReadable(list, trackingWhitelistTable))) { |
1088 | 0 | isTracker = false; |
1089 | 0 | } |
1090 | 0 | if (isAnnotation && |
1091 | 0 | (list == TABLE_ANNOTATION_WHITELIST_PREF || |
1092 | 0 | FindInReadable(list, annotationWhitelistTable))) { |
1093 | 0 | isAnnotation = false; |
1094 | 0 | } |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 |
|
1098 | 0 | MOZ_ASSERT(shouldEnableTrackingProtection || !isTracker); |
1099 | 0 | MOZ_ASSERT(shouldEnableTrackingAnnotation || !isAnnotation); |
1100 | 0 |
|
1101 | 0 | if (!isTracker && !isAnnotation) { |
1102 | 0 | return OnWhitelistResult(NS_OK); // fully whitelisted |
1103 | 0 | } |
1104 | 0 | |
1105 | 0 | // The lookup failed to match at least one of the active whitelists |
1106 | 0 | // (tracking protection takes precedence over tracking annotations) |
1107 | 0 | return OnWhitelistResult(isTracker ? NS_ERROR_TRACKING_URI : |
1108 | 0 | NS_ERROR_TRACKING_ANNOTATION_URI); |
1109 | 0 | } |
1110 | | |
1111 | | nsresult |
1112 | | TrackingURICallback::OnBlacklistResult(nsresult aErrorCode, |
1113 | | bool aInTrackingTable, |
1114 | | bool aInAnnotationTable) |
1115 | 0 | { |
1116 | 0 | LOG_DEBUG(("TrackingURICallback[%p]::OnBlacklistResult aErrorCode=0x%" PRIx32, |
1117 | 0 | mChannelClassifier.get(), static_cast<uint32_t>(aErrorCode))); |
1118 | 0 |
|
1119 | 0 | if (NS_SUCCEEDED(aErrorCode)) { |
1120 | 0 | if (LOG_ENABLED()) { |
1121 | 0 | nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel(); |
1122 | 0 | nsCOMPtr<nsIURI> uri; |
1123 | 0 | channel->GetURI(getter_AddRefs(uri)); |
1124 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1125 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1126 | 0 | LOG(("TrackingURICallback[%p]::OnBlacklistResult uri %s not found " |
1127 | 0 | "in blacklist", mChannelClassifier.get(), spec.get())); |
1128 | 0 | } |
1129 | 0 | mChannelCallback(); |
1130 | 0 | return NS_OK; |
1131 | 0 | } |
1132 | 0 | MOZ_ASSERT(aErrorCode == NS_ERROR_MAYBE_TRACKING_URI); |
1133 | 0 |
|
1134 | 0 | if (LOG_ENABLED()) { |
1135 | 0 | nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel(); |
1136 | 0 | nsCOMPtr<nsIURI> uri; |
1137 | 0 | channel->GetURI(getter_AddRefs(uri)); |
1138 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1139 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1140 | 0 | LOG(("TrackingURICallback[%p]::OnBlacklistResult channel[%p] " |
1141 | 0 | "uri=%s, is in blacklist. Start checking whitelist.", |
1142 | 0 | mChannelClassifier.get(), channel.get(), spec.get())); |
1143 | 0 | } |
1144 | 0 |
|
1145 | 0 | nsCOMPtr<nsIURI> whitelistURI; |
1146 | 0 | nsresult rv = mChannelClassifier->CreateWhiteListURI(getter_AddRefs(whitelistURI)); |
1147 | 0 | if (NS_FAILED(rv)) { |
1148 | 0 | nsAutoCString errorName; |
1149 | 0 | GetErrorName(rv, errorName); |
1150 | 0 | NS_WARNING(nsPrintfCString("TrackingURICallback[%p]:OnBlacklistResult got " |
1151 | 0 | "an unexpected error (rv=%s) while trying to " |
1152 | 0 | "create a whitelist URI. Allowing tracker.", |
1153 | 0 | mChannelClassifier.get(), errorName.get()).get()); |
1154 | 0 | mChannelCallback(); |
1155 | 0 | return NS_OK; // let the tracker through |
1156 | 0 | } |
1157 | 0 |
|
1158 | 0 | if (!whitelistURI) { |
1159 | 0 | LOG(("TrackingURICallback[%p]:OnBlacklistResult could not create a " |
1160 | 0 | "whitelist URI. Ignoring whitelist.", mChannelClassifier.get())); |
1161 | 0 | OnTrackerFound(aInTrackingTable ? NS_ERROR_TRACKING_URI : |
1162 | 0 | NS_ERROR_TRACKING_ANNOTATION_URI); |
1163 | 0 | mChannelCallback(); |
1164 | 0 | return NS_OK; |
1165 | 0 | } |
1166 | 0 |
|
1167 | 0 | rv = mChannelClassifier->IsTrackerWhitelisted(whitelistURI, |
1168 | 0 | aInTrackingTable, |
1169 | 0 | aInAnnotationTable, |
1170 | 0 | this); |
1171 | 0 | if (NS_FAILED(rv)) { |
1172 | 0 | if (LOG_ENABLED()) { |
1173 | 0 | nsAutoCString errorName; |
1174 | 0 | GetErrorName(rv, errorName); |
1175 | 0 | LOG(("TrackingURICallback[%p]:OnBlacklistResult " |
1176 | 0 | "IsTrackerWhitelisted has failed with rv=%s.", |
1177 | 0 | mChannelClassifier.get(), errorName.get())); |
1178 | 0 | } |
1179 | 0 |
|
1180 | 0 | if (rv == NS_ERROR_TRACKING_URI || |
1181 | 0 | rv == NS_ERROR_TRACKING_ANNOTATION_URI) { |
1182 | 0 | // whitelist disabled, blocking tracker |
1183 | 0 | OnTrackerFound(rv); |
1184 | 0 | } else { |
1185 | 0 | // ignore other failures and let the tracker through |
1186 | 0 | nsAutoCString errorName; |
1187 | 0 | GetErrorName(rv, errorName); |
1188 | 0 | NS_WARNING(nsPrintfCString("Unexpected error (%s) received from " |
1189 | 0 | "TrackingURICallback::IsTrackerWhitelisted()", |
1190 | 0 | errorName.get()).get()); |
1191 | 0 | } |
1192 | 0 | mChannelCallback(); |
1193 | 0 | return NS_OK; |
1194 | 0 | } |
1195 | 0 |
|
1196 | 0 | // We'll have to wait for OnWhitelistResult() to get called. |
1197 | 0 | MOZ_ASSERT(aInTrackingTable || aInAnnotationTable); |
1198 | 0 | mExpectTrackingWhitelistResult = aInTrackingTable; |
1199 | 0 | mExpectAnnotationWhitelistResult = aInAnnotationTable; |
1200 | 0 | return NS_OK; |
1201 | 0 | } |
1202 | | |
1203 | | nsresult |
1204 | | TrackingURICallback::OnWhitelistResult(nsresult aErrorCode) |
1205 | 0 | { |
1206 | 0 | LOG_DEBUG(("TrackingURICallback[%p]::OnWhitelistResult aErrorCode=0x%" PRIx32, |
1207 | 0 | mChannelClassifier.get(), static_cast<uint32_t>(aErrorCode))); |
1208 | 0 |
|
1209 | 0 | if (NS_SUCCEEDED(aErrorCode)) { |
1210 | 0 | if (LOG_ENABLED()) { |
1211 | 0 | nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel(); |
1212 | 0 | nsCOMPtr<nsIURI> uri; |
1213 | 0 | channel->GetURI(getter_AddRefs(uri)); |
1214 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1215 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1216 | 0 | LOG(("TrackingURICallback[%p]::OnWhitelistResult uri %s found " |
1217 | 0 | "in whitelist so we won't block it", mChannelClassifier.get(), |
1218 | 0 | spec.get())); |
1219 | 0 | } |
1220 | 0 | mChannelCallback(); |
1221 | 0 | return NS_OK; |
1222 | 0 | } |
1223 | 0 |
|
1224 | 0 | if (LOG_ENABLED()) { |
1225 | 0 | nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel(); |
1226 | 0 | nsCOMPtr<nsIURI> uri; |
1227 | 0 | channel->GetURI(getter_AddRefs(uri)); |
1228 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1229 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1230 | 0 | LOG(("TrackingURICallback[%p]::OnWhitelistResult " |
1231 | 0 | "channel[%p] uri=%s, should not be whitelisted", |
1232 | 0 | mChannelClassifier.get(), channel.get(), spec.get())); |
1233 | 0 | } |
1234 | 0 |
|
1235 | 0 | OnTrackerFound(aErrorCode); |
1236 | 0 | mChannelCallback(); |
1237 | 0 | return NS_OK; |
1238 | 0 | } |
1239 | | |
1240 | | void |
1241 | | TrackingURICallback::OnTrackerFound(nsresult aErrorCode) |
1242 | 0 | { |
1243 | 0 | nsCOMPtr<nsIChannel> channel = mChannelClassifier->GetChannel(); |
1244 | 0 | MOZ_ASSERT(channel); |
1245 | 0 | if (aErrorCode == NS_ERROR_TRACKING_URI && |
1246 | 0 | mChannelClassifier->ShouldEnableTrackingProtection()) { |
1247 | 0 | mChannelClassifier->SetBlockedContent(channel, aErrorCode, |
1248 | 0 | mList, mProvider, mFullHash); |
1249 | 0 | LOG(("TrackingURICallback[%p]::OnTrackerFound, cancelling channel[%p]", |
1250 | 0 | mChannelClassifier.get(), channel.get())); |
1251 | 0 | nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(channel); |
1252 | 0 | if (httpChannel) { |
1253 | 0 | Unused << httpChannel->CancelForTrackingProtection(); |
1254 | 0 | } else { |
1255 | 0 | Unused << channel->Cancel(aErrorCode); |
1256 | 0 | } |
1257 | 0 | } else { |
1258 | 0 | MOZ_ASSERT(aErrorCode == NS_ERROR_TRACKING_ANNOTATION_URI); |
1259 | 0 | MOZ_ASSERT(mChannelClassifier->ShouldEnableTrackingAnnotation()); |
1260 | 0 |
|
1261 | 0 | bool isThirdPartyWithTopLevelWinURI = false; |
1262 | 0 | nsresult rv = IsThirdParty(channel, &isThirdPartyWithTopLevelWinURI); |
1263 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1264 | 0 | LOG(("TrackingURICallback[%p]::OnTrackerFound IsThirdParty() failed", |
1265 | 0 | mChannelClassifier.get())); |
1266 | 0 | return; // we'll assume the channel is NOT third-party |
1267 | 0 | } |
1268 | 0 |
|
1269 | 0 | LOG(("TrackingURICallback[%p]::OnTrackerFound, annotating channel[%p]", |
1270 | 0 | mChannelClassifier.get(), channel.get())); |
1271 | 0 |
|
1272 | 0 | SetIsTrackingResourceHelper(channel, isThirdPartyWithTopLevelWinURI); |
1273 | 0 |
|
1274 | 0 | if (isThirdPartyWithTopLevelWinURI) { |
1275 | 0 | // Even with TP disabled, we still want to show the user that there |
1276 | 0 | // are unblocked trackers on the site, so notify the UI that we loaded |
1277 | 0 | // tracking content. UI code can treat this notification differently |
1278 | 0 | // depending on whether TP is enabled or disabled. |
1279 | 0 | mChannelClassifier->NotifyTrackingProtectionDisabled(channel); |
1280 | 0 |
|
1281 | 0 | if (CachedPrefs::GetInstance()->IsLowerNetworkPriority()) { |
1282 | 0 | LowerPriorityHelper(channel); |
1283 | 0 | } |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | } |
1287 | | |
1288 | | } // end of unnamed namespace/ |
1289 | | |
1290 | | nsresult |
1291 | | nsChannelClassifier::CreateWhiteListURI(nsIURI** aURI) const |
1292 | 0 | { |
1293 | 0 | nsresult rv; |
1294 | 0 | nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv); |
1295 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1296 | 0 | if (!chan) { |
1297 | 0 | return NS_ERROR_FAILURE; |
1298 | 0 | } |
1299 | 0 | |
1300 | 0 | nsCOMPtr<nsIURI> topWinURI; |
1301 | 0 | rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI)); |
1302 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1303 | 0 | if (!topWinURI) { |
1304 | 0 | if (LOG_ENABLED()) { |
1305 | 0 | nsresult rv; |
1306 | 0 | nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(mChannel, &rv); |
1307 | 0 | nsCOMPtr<nsIURI> uri; |
1308 | 0 | rv = httpChan->GetURI(getter_AddRefs(uri)); |
1309 | 0 | nsAutoCString spec; |
1310 | 0 | uri->GetAsciiSpec(spec); |
1311 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1312 | 0 | LOG(("nsChannelClassifier[%p]: No window URI associated with %s", |
1313 | 0 | this, spec.get())); |
1314 | 0 | } |
1315 | 0 | return NS_OK; |
1316 | 0 | } |
1317 | 0 |
|
1318 | 0 | nsCOMPtr<nsIScriptSecurityManager> securityManager = |
1319 | 0 | do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); |
1320 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1321 | 0 | nsCOMPtr<nsIPrincipal> chanPrincipal; |
1322 | 0 | rv = securityManager->GetChannelURIPrincipal(mChannel, |
1323 | 0 | getter_AddRefs(chanPrincipal)); |
1324 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1325 | 0 |
|
1326 | 0 | // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain" |
1327 | 0 | nsAutoCString pageHostname, resourceDomain; |
1328 | 0 | rv = topWinURI->GetHost(pageHostname); |
1329 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1330 | 0 | rv = chanPrincipal->GetBaseDomain(resourceDomain); |
1331 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1332 | 0 | nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") + |
1333 | 0 | pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain; |
1334 | 0 | LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist (channel=%p)", |
1335 | 0 | this, whitelistEntry.get(), mChannel.get())); |
1336 | 0 |
|
1337 | 0 | nsCOMPtr<nsIURI> whitelistURI; |
1338 | 0 | rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry); |
1339 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1340 | 0 |
|
1341 | 0 | whitelistURI.forget(aURI); |
1342 | 0 | return NS_OK; |
1343 | 0 | } |
1344 | | |
1345 | | nsresult |
1346 | | nsChannelClassifier::IsTrackerWhitelisted(nsIURI* aWhiteListURI, |
1347 | | bool aUseTrackingTable, |
1348 | | bool aUseAnnotationTable, |
1349 | | nsIURIClassifierCallback *aCallback) |
1350 | 0 | { |
1351 | 0 | MOZ_ASSERT(aUseTrackingTable || aUseAnnotationTable); |
1352 | 0 | MOZ_ASSERT(aWhiteListURI); |
1353 | 0 | if (!aCallback) { |
1354 | 0 | return NS_ERROR_INVALID_ARG; |
1355 | 0 | } |
1356 | 0 | |
1357 | 0 | nsresult rv; |
1358 | 0 | nsCOMPtr<nsIURIClassifier> uriClassifier = |
1359 | 0 | do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); |
1360 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1361 | 0 |
|
1362 | 0 | nsAutoCString whitelist; |
1363 | 0 | nsTArray<nsCString> whitelistExtraTables; |
1364 | 0 | nsTArray<nsCString> whitelistExtraEntries; |
1365 | 0 | if (aUseAnnotationTable) { |
1366 | 0 | whitelist += CachedPrefs::GetInstance()->GetAnnotationWhiteList(); |
1367 | 0 | whitelist += NS_LITERAL_CSTRING(","); |
1368 | 0 | whitelistExtraTables.AppendElement(TABLE_ANNOTATION_WHITELIST_PREF); |
1369 | 0 | whitelistExtraEntries.AppendElement(CachedPrefs::GetInstance()->GetAnnotationWhiteListExtraEntries()); |
1370 | 0 | } |
1371 | 0 | if (aUseTrackingTable) { |
1372 | 0 | whitelist += CachedPrefs::GetInstance()->GetTrackingWhiteList(); |
1373 | 0 | whitelistExtraTables.AppendElement(TABLE_TRACKING_WHITELIST_PREF); |
1374 | 0 | whitelistExtraEntries.AppendElement(CachedPrefs::GetInstance()->GetTrackingWhiteListExtraEntries()); |
1375 | 0 | } |
1376 | 0 |
|
1377 | 0 | if (whitelist.IsEmpty()) { |
1378 | 0 | LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled", |
1379 | 0 | this)); |
1380 | 0 | return aUseTrackingTable ? NS_ERROR_TRACKING_URI : |
1381 | 0 | NS_ERROR_TRACKING_ANNOTATION_URI; |
1382 | 0 | } |
1383 | 0 |
|
1384 | 0 | return uriClassifier->AsyncClassifyLocalWithTables(aWhiteListURI, whitelist, |
1385 | 0 | whitelistExtraTables, |
1386 | 0 | whitelistExtraEntries, |
1387 | 0 | aCallback); |
1388 | 0 | } |
1389 | | |
1390 | | /* static */ |
1391 | | nsresult |
1392 | | nsChannelClassifier::SendThreatHitReport(nsIChannel *aChannel, |
1393 | | const nsACString& aProvider, |
1394 | | const nsACString& aList, |
1395 | | const nsACString& aFullHash) |
1396 | 0 | { |
1397 | 0 | NS_ENSURE_ARG_POINTER(aChannel); |
1398 | 0 |
|
1399 | 0 | nsAutoCString provider(aProvider); |
1400 | 0 | nsPrintfCString reportEnablePref("browser.safebrowsing.provider.%s.dataSharing.enabled", |
1401 | 0 | provider.get()); |
1402 | 0 | if (!Preferences::GetBool(reportEnablePref.get(), false)) { |
1403 | 0 | LOG(("nsChannelClassifier::SendThreatHitReport data sharing disabled for %s", |
1404 | 0 | provider.get())); |
1405 | 0 | return NS_OK; |
1406 | 0 | } |
1407 | 0 |
|
1408 | 0 | nsCOMPtr<nsIURIClassifier> uriClassifier = |
1409 | 0 | do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID); |
1410 | 0 | if (!uriClassifier) { |
1411 | 0 | return NS_ERROR_UNEXPECTED; |
1412 | 0 | } |
1413 | 0 | |
1414 | 0 | nsresult rv = uriClassifier->SendThreatHitReport(aChannel, aProvider, aList, |
1415 | 0 | aFullHash); |
1416 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1417 | 0 |
|
1418 | 0 | return NS_OK; |
1419 | 0 | } |
1420 | | |
1421 | | NS_IMETHODIMP |
1422 | | nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode, |
1423 | | const nsACString& aList, |
1424 | | const nsACString& aProvider, |
1425 | | const nsACString& aFullHash) |
1426 | 0 | { |
1427 | 0 | // Should only be called in the parent process. |
1428 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
1429 | 0 | MOZ_ASSERT(aErrorCode != NS_ERROR_TRACKING_URI); |
1430 | 0 |
|
1431 | 0 | if (mSuspendedChannel) { |
1432 | 0 | nsAutoCString errorName; |
1433 | 0 | if (LOG_ENABLED() && NS_FAILED(aErrorCode)) { |
1434 | 0 | GetErrorName(aErrorCode, errorName); |
1435 | 0 | LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)", |
1436 | 0 | this, errorName.get())); |
1437 | 0 | } |
1438 | 0 | MarkEntryClassified(aErrorCode); |
1439 | 0 |
|
1440 | 0 | if (NS_FAILED(aErrorCode)) { |
1441 | 0 | if (LOG_ENABLED()) { |
1442 | 0 | nsCOMPtr<nsIURI> uri; |
1443 | 0 | mChannel->GetURI(getter_AddRefs(uri)); |
1444 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1445 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1446 | 0 | LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s " |
1447 | 0 | "with error code %s", this, mChannel.get(), |
1448 | 0 | spec.get(), errorName.get())); |
1449 | 0 | } |
1450 | 0 |
|
1451 | 0 | // Channel will be cancelled (page element blocked) due to Safe Browsing. |
1452 | 0 | // Do update the security state of the document and fire a security |
1453 | 0 | // change event. |
1454 | 0 | SetBlockedContent(mChannel, aErrorCode, aList, aProvider, aFullHash); |
1455 | 0 |
|
1456 | 0 | if (aErrorCode == NS_ERROR_MALWARE_URI || |
1457 | 0 | aErrorCode == NS_ERROR_PHISHING_URI || |
1458 | 0 | aErrorCode == NS_ERROR_UNWANTED_URI || |
1459 | 0 | aErrorCode == NS_ERROR_HARMFUL_URI) { |
1460 | 0 | SendThreatHitReport(mChannel, aProvider, aList, aFullHash); |
1461 | 0 | } |
1462 | 0 |
|
1463 | 0 | mChannel->Cancel(aErrorCode); |
1464 | 0 | } |
1465 | 0 | LOG_DEBUG(("nsChannelClassifier[%p]: resuming channel[%p] from " |
1466 | 0 | "OnClassifyComplete", this, mChannel.get())); |
1467 | 0 | mChannel->Resume(); |
1468 | 0 | } |
1469 | 0 |
|
1470 | 0 | mChannel = nullptr; |
1471 | 0 | RemoveShutdownObserver(); |
1472 | 0 |
|
1473 | 0 | return NS_OK; |
1474 | 0 | } |
1475 | | |
1476 | | nsresult |
1477 | | nsChannelClassifier::CheckIsTrackerWithLocalTable(std::function<void()>&& aCallback) |
1478 | 0 | { |
1479 | 0 | nsresult rv; |
1480 | 0 |
|
1481 | 0 | if (!aCallback) { |
1482 | 0 | return NS_ERROR_INVALID_ARG; |
1483 | 0 | } |
1484 | 0 | |
1485 | 0 | nsCOMPtr<nsIURIClassifier> uriClassifier = |
1486 | 0 | do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); |
1487 | 0 | if (NS_FAILED(rv)) { |
1488 | 0 | return rv; |
1489 | 0 | } |
1490 | 0 | |
1491 | 0 | const bool shouldEnableTrackingProtection = ShouldEnableTrackingProtection(); |
1492 | 0 | const bool shouldEnableTrackingAnnotation = ShouldEnableTrackingAnnotation(); |
1493 | 0 | if (!shouldEnableTrackingProtection && !shouldEnableTrackingAnnotation) { |
1494 | 0 | return NS_ERROR_FAILURE; |
1495 | 0 | } |
1496 | 0 | |
1497 | 0 | nsCOMPtr<nsIURI> uri; |
1498 | 0 | rv = mChannel->GetURI(getter_AddRefs(uri)); |
1499 | 0 | if (NS_FAILED(rv) || !uri) { |
1500 | 0 | return rv; |
1501 | 0 | } |
1502 | 0 | |
1503 | 0 | nsAutoCString blacklist; |
1504 | 0 | nsTArray<nsCString> blacklistExtraTables; |
1505 | 0 | nsTArray<nsCString> blacklistExtraEntries; |
1506 | 0 | if (shouldEnableTrackingAnnotation) { |
1507 | 0 | blacklist += CachedPrefs::GetInstance()->GetAnnotationBlackList(); |
1508 | 0 | blacklist += NS_LITERAL_CSTRING(","); |
1509 | 0 | blacklistExtraTables.AppendElement(TABLE_ANNOTATION_BLACKLIST_PREF); |
1510 | 0 | blacklistExtraEntries.AppendElement(CachedPrefs::GetInstance()->GetAnnotationBlackListExtraEntries()); |
1511 | 0 | } |
1512 | 0 | if (shouldEnableTrackingProtection) { |
1513 | 0 | blacklist += CachedPrefs::GetInstance()->GetTrackingBlackList(); |
1514 | 0 | blacklistExtraTables.AppendElement(TABLE_TRACKING_BLACKLIST_PREF); |
1515 | 0 | blacklistExtraEntries.AppendElement(CachedPrefs::GetInstance()->GetTrackingBlackListExtraEntries()); |
1516 | 0 | } |
1517 | 0 | if (blacklist.IsEmpty()) { |
1518 | 0 | LOG_WARN(("nsChannelClassifier[%p]:CheckIsTrackerWithLocalTable blacklist is empty", |
1519 | 0 | this)); |
1520 | 0 | return NS_ERROR_FAILURE; |
1521 | 0 | } |
1522 | 0 | // Note: duplicate lists will be removed in Classifier::SplitTables(). |
1523 | 0 |
|
1524 | 0 | nsCOMPtr<nsIURIClassifierCallback> callback = |
1525 | 0 | new TrackingURICallback(this, std::move(aCallback)); |
1526 | 0 |
|
1527 | 0 | if (LOG_ENABLED()) { |
1528 | 0 | nsCString spec = uri->GetSpecOrDefault(); |
1529 | 0 | spec.Truncate(std::min(spec.Length(), sMaxSpecLength)); |
1530 | 0 | LOG(("nsChannelClassifier[%p]: Checking blacklist for uri=%s\n", |
1531 | 0 | this, spec.get())); |
1532 | 0 | } |
1533 | 0 | return uriClassifier->AsyncClassifyLocalWithTables(uri, blacklist, |
1534 | 0 | blacklistExtraTables, |
1535 | 0 | blacklistExtraEntries, |
1536 | 0 | callback); |
1537 | 0 | } |
1538 | | |
1539 | | already_AddRefed<nsIChannel> |
1540 | | nsChannelClassifier::GetChannel() |
1541 | 0 | { |
1542 | 0 | nsCOMPtr<nsIChannel> channel = mChannel; |
1543 | 0 | return channel.forget(); |
1544 | 0 | } |
1545 | | |
1546 | | void |
1547 | | nsChannelClassifier::AddShutdownObserver() |
1548 | 0 | { |
1549 | 0 | nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); |
1550 | 0 | if (observerService) { |
1551 | 0 | observerService->AddObserver(this, "profile-change-net-teardown", false); |
1552 | 0 | } |
1553 | 0 | } |
1554 | | |
1555 | | void |
1556 | | nsChannelClassifier::RemoveShutdownObserver() |
1557 | 0 | { |
1558 | 0 | nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); |
1559 | 0 | if (observerService) { |
1560 | 0 | observerService->RemoveObserver(this, "profile-change-net-teardown"); |
1561 | 0 | } |
1562 | 0 | } |
1563 | | |
1564 | | /////////////////////////////////////////////////////////////////////////////// |
1565 | | // nsIObserver implementation |
1566 | | NS_IMETHODIMP |
1567 | | nsChannelClassifier::Observe(nsISupports *aSubject, const char *aTopic, |
1568 | | const char16_t *aData) |
1569 | 0 | { |
1570 | 0 | if (!strcmp(aTopic, "profile-change-net-teardown")) { |
1571 | 0 | // If we aren't getting a callback for any reason, make sure |
1572 | 0 | // we resume the channel. |
1573 | 0 |
|
1574 | 0 | if (mChannel && mSuspendedChannel) { |
1575 | 0 | mSuspendedChannel = false; |
1576 | 0 | mChannel->Cancel(NS_ERROR_ABORT); |
1577 | 0 | mChannel->Resume(); |
1578 | 0 | mChannel = nullptr; |
1579 | 0 | } |
1580 | 0 |
|
1581 | 0 | RemoveShutdownObserver(); |
1582 | 0 | } |
1583 | 0 |
|
1584 | 0 | return NS_OK; |
1585 | 0 | } |
1586 | | |
1587 | | #undef LOG_ENABLED |
1588 | | |
1589 | | } // namespace net |
1590 | | } // namespace mozilla |