nsChannelClassifier.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsChannelClassifier.h"
  6. #include "mozIThirdPartyUtil.h"
  7. #include "nsCharSeparatedTokenizer.h"
  8. #include "nsContentUtils.h"
  9. #include "nsICacheEntry.h"
  10. #include "nsICachingChannel.h"
  11. #include "nsIChannel.h"
  12. #include "nsIDocShell.h"
  13. #include "nsIDocument.h"
  14. #include "nsIDOMDocument.h"
  15. #include "nsIHttpChannelInternal.h"
  16. #include "nsIIOService.h"
  17. #include "nsILoadContext.h"
  18. #include "nsIParentChannel.h"
  19. #include "nsIPermissionManager.h"
  20. #include "nsIPrivateBrowsingTrackingProtectionWhitelist.h"
  21. #include "nsIProtocolHandler.h"
  22. #include "nsIScriptError.h"
  23. #include "nsIScriptSecurityManager.h"
  24. #include "nsISecureBrowserUI.h"
  25. #include "nsISecurityEventSink.h"
  26. #include "nsIURL.h"
  27. #include "nsIWebProgressListener.h"
  28. #include "nsNetUtil.h"
  29. #include "nsPIDOMWindow.h"
  30. #include "nsXULAppAPI.h"
  31. #include "mozilla/ErrorNames.h"
  32. #include "mozilla/Logging.h"
  33. #include "mozilla/Preferences.h"
  34. namespace mozilla {
  35. namespace net {
  36. //
  37. // MOZ_LOG=nsChannelClassifier:5
  38. //
  39. static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
  40. #undef LOG
  41. #define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
  42. #define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Debug)
  43. NS_IMPL_ISUPPORTS(nsChannelClassifier,
  44. nsIURIClassifierCallback)
  45. nsChannelClassifier::nsChannelClassifier()
  46. : mIsAllowListed(false),
  47. mSuspendedChannel(false)
  48. {
  49. }
  50. nsresult
  51. nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel,
  52. bool *result)
  53. {
  54. // Should only be called in the parent process.
  55. MOZ_ASSERT(XRE_IsParentProcess());
  56. NS_ENSURE_ARG(result);
  57. *result = false;
  58. nsCOMPtr<nsILoadContext> loadContext;
  59. NS_QueryNotificationCallbacks(aChannel, loadContext);
  60. if (!loadContext || !(loadContext->UseTrackingProtection())) {
  61. return NS_OK;
  62. }
  63. nsresult rv;
  64. nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
  65. do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
  66. NS_ENSURE_SUCCESS(rv, rv);
  67. nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(aChannel, &rv);
  68. NS_ENSURE_SUCCESS(rv, rv);
  69. nsCOMPtr<nsIURI> topWinURI;
  70. rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
  71. NS_ENSURE_SUCCESS(rv, rv);
  72. if (!topWinURI) {
  73. LOG(("nsChannelClassifier[%p]: No window URI\n", this));
  74. }
  75. nsCOMPtr<nsIURI> chanURI;
  76. rv = aChannel->GetURI(getter_AddRefs(chanURI));
  77. NS_ENSURE_SUCCESS(rv, rv);
  78. // Third party checks don't work for chrome:// URIs in mochitests, so just
  79. // default to isThirdParty = true. We check isThirdPartyWindow to expand
  80. // the list of domains that are considered first party (e.g., if
  81. // facebook.com includes an iframe from fatratgames.com, all subsources
  82. // included in that iframe are considered third-party with
  83. // isThirdPartyChannel, even if they are not third-party w.r.t.
  84. // facebook.com), and isThirdPartyChannel to prevent top-level navigations
  85. // from being detected as third-party.
  86. bool isThirdPartyChannel = true;
  87. bool isThirdPartyWindow = true;
  88. thirdPartyUtil->IsThirdPartyURI(chanURI, topWinURI, &isThirdPartyWindow);
  89. thirdPartyUtil->IsThirdPartyChannel(aChannel, nullptr, &isThirdPartyChannel);
  90. if (!isThirdPartyWindow || !isThirdPartyChannel) {
  91. *result = false;
  92. if (LOG_ENABLED()) {
  93. LOG(("nsChannelClassifier[%p]: Skipping tracking protection checks "
  94. "for first party or top-level load channel[%p] with uri %s",
  95. this, aChannel, chanURI->GetSpecOrDefault().get()));
  96. }
  97. return NS_OK;
  98. }
  99. nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
  100. NS_ENSURE_SUCCESS(rv, rv);
  101. const char ALLOWLIST_EXAMPLE_PREF[] = "channelclassifier.allowlist_example";
  102. if (!topWinURI && Preferences::GetBool(ALLOWLIST_EXAMPLE_PREF, false)) {
  103. LOG(("nsChannelClassifier[%p]: Allowlisting test domain\n", this));
  104. rv = ios->NewURI(NS_LITERAL_CSTRING("http://allowlisted.example.com"),
  105. nullptr, nullptr, getter_AddRefs(topWinURI));
  106. NS_ENSURE_SUCCESS(rv, rv);
  107. }
  108. // Take the host/port portion so we can allowlist by site. Also ignore the
  109. // scheme, since users who put sites on the allowlist probably don't expect
  110. // allowlisting to depend on scheme.
  111. nsCOMPtr<nsIURL> url = do_QueryInterface(topWinURI, &rv);
  112. if (NS_FAILED(rv)) {
  113. return rv; // normal for some loads, no need to print a warning
  114. }
  115. nsCString escaped(NS_LITERAL_CSTRING("https://"));
  116. nsAutoCString temp;
  117. rv = url->GetHostPort(temp);
  118. NS_ENSURE_SUCCESS(rv, rv);
  119. escaped.Append(temp);
  120. // Stuff the whole thing back into a URI for the permission manager.
  121. rv = ios->NewURI(escaped, nullptr, nullptr, getter_AddRefs(topWinURI));
  122. NS_ENSURE_SUCCESS(rv, rv);
  123. nsCOMPtr<nsIPermissionManager> permMgr =
  124. do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
  125. NS_ENSURE_SUCCESS(rv, rv);
  126. uint32_t permissions = nsIPermissionManager::UNKNOWN_ACTION;
  127. rv = permMgr->TestPermission(topWinURI, "trackingprotection", &permissions);
  128. NS_ENSURE_SUCCESS(rv, rv);
  129. if (permissions == nsIPermissionManager::ALLOW_ACTION) {
  130. LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] for %s", this,
  131. aChannel, escaped.get()));
  132. mIsAllowListed = true;
  133. *result = false;
  134. } else {
  135. *result = true;
  136. }
  137. // In Private Browsing Mode we also check against an in-memory list.
  138. if (NS_UsePrivateBrowsing(aChannel)) {
  139. nsCOMPtr<nsIPrivateBrowsingTrackingProtectionWhitelist> pbmtpWhitelist =
  140. do_GetService(NS_PBTRACKINGPROTECTIONWHITELIST_CONTRACTID, &rv);
  141. NS_ENSURE_SUCCESS(rv, rv);
  142. bool exists = false;
  143. rv = pbmtpWhitelist->ExistsInAllowList(topWinURI, &exists);
  144. NS_ENSURE_SUCCESS(rv, rv);
  145. if (exists) {
  146. mIsAllowListed = true;
  147. LOG(("nsChannelClassifier[%p]: Allowlisting channel[%p] in PBM for %s",
  148. this, aChannel, escaped.get()));
  149. }
  150. *result = !exists;
  151. }
  152. // Tracking protection will be enabled so return without updating
  153. // the security state. If any channels are subsequently cancelled
  154. // (page elements blocked) the state will be then updated.
  155. if (*result) {
  156. if (LOG_ENABLED()) {
  157. LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on "
  158. "channel[%p] with uri %s for toplevel window %s", this, aChannel,
  159. chanURI->GetSpecOrDefault().get(),
  160. topWinURI->GetSpecOrDefault().get()));
  161. }
  162. return NS_OK;
  163. }
  164. // Tracking protection will be disabled so update the security state
  165. // of the document and fire a secure change event. If we can't get the
  166. // window for the channel, then the shield won't show up so we can't send
  167. // an event to the securityUI anyway.
  168. return NotifyTrackingProtectionDisabled(aChannel);
  169. }
  170. // static
  171. nsresult
  172. nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
  173. {
  174. // Can be called in EITHER the parent or child process.
  175. nsCOMPtr<nsIParentChannel> parentChannel;
  176. NS_QueryNotificationCallbacks(aChannel, parentChannel);
  177. if (parentChannel) {
  178. // This channel is a parent-process proxy for a child process request.
  179. // Tell the child process channel to do this instead.
  180. parentChannel->NotifyTrackingProtectionDisabled();
  181. return NS_OK;
  182. }
  183. nsresult rv;
  184. nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
  185. do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
  186. NS_ENSURE_SUCCESS(rv, rv);
  187. nsCOMPtr<mozIDOMWindowProxy> win;
  188. rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, getter_AddRefs(win));
  189. NS_ENSURE_SUCCESS(rv, rv);
  190. auto* pwin = nsPIDOMWindowOuter::From(win);
  191. nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
  192. if (!docShell) {
  193. return NS_OK;
  194. }
  195. nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
  196. NS_ENSURE_TRUE(doc, NS_OK);
  197. // Notify nsIWebProgressListeners of this security event.
  198. // Can be used to change the UI state.
  199. nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
  200. NS_ENSURE_SUCCESS(rv, NS_OK);
  201. uint32_t state = 0;
  202. nsCOMPtr<nsISecureBrowserUI> securityUI;
  203. docShell->GetSecurityUI(getter_AddRefs(securityUI));
  204. if (!securityUI) {
  205. return NS_OK;
  206. }
  207. doc->SetHasTrackingContentLoaded(true);
  208. securityUI->GetState(&state);
  209. state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
  210. eventSink->OnSecurityChange(nullptr, state);
  211. return NS_OK;
  212. }
  213. void
  214. nsChannelClassifier::Start(nsIChannel *aChannel)
  215. {
  216. mChannel = aChannel;
  217. nsresult rv = StartInternal();
  218. if (NS_FAILED(rv)) {
  219. // If we aren't getting a callback for any reason, assume a good verdict and
  220. // make sure we resume the channel if necessary.
  221. OnClassifyComplete(NS_OK);
  222. }
  223. }
  224. nsresult
  225. nsChannelClassifier::StartInternal()
  226. {
  227. // Should only be called in the parent process.
  228. MOZ_ASSERT(XRE_IsParentProcess());
  229. // Don't bother to run the classifier on a load that has already failed.
  230. // (this might happen after a redirect)
  231. nsresult status;
  232. mChannel->GetStatus(&status);
  233. if (NS_FAILED(status))
  234. return status;
  235. // Don't bother to run the classifier on a cached load that was
  236. // previously classified as good.
  237. if (HasBeenClassified(mChannel)) {
  238. return NS_ERROR_UNEXPECTED;
  239. }
  240. nsCOMPtr<nsIURI> uri;
  241. nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
  242. NS_ENSURE_SUCCESS(rv, rv);
  243. // Don't bother checking certain types of URIs.
  244. bool hasFlags;
  245. rv = NS_URIChainHasFlags(uri,
  246. nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
  247. &hasFlags);
  248. NS_ENSURE_SUCCESS(rv, rv);
  249. if (hasFlags) return NS_ERROR_UNEXPECTED;
  250. rv = NS_URIChainHasFlags(uri,
  251. nsIProtocolHandler::URI_IS_LOCAL_FILE,
  252. &hasFlags);
  253. NS_ENSURE_SUCCESS(rv, rv);
  254. if (hasFlags) return NS_ERROR_UNEXPECTED;
  255. rv = NS_URIChainHasFlags(uri,
  256. nsIProtocolHandler::URI_IS_UI_RESOURCE,
  257. &hasFlags);
  258. NS_ENSURE_SUCCESS(rv, rv);
  259. if (hasFlags) return NS_ERROR_UNEXPECTED;
  260. rv = NS_URIChainHasFlags(uri,
  261. nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
  262. &hasFlags);
  263. NS_ENSURE_SUCCESS(rv, rv);
  264. if (hasFlags) return NS_ERROR_UNEXPECTED;
  265. // Skip whitelisted hostnames.
  266. nsAutoCString whitelisted;
  267. Preferences::GetCString("urlclassifier.skipHostnames", &whitelisted);
  268. if (!whitelisted.IsEmpty()) {
  269. ToLowerCase(whitelisted);
  270. LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
  271. this, whitelisted.get()));
  272. if (IsHostnameWhitelisted(uri, whitelisted)) {
  273. return NS_ERROR_UNEXPECTED;
  274. }
  275. }
  276. nsCOMPtr<nsIURIClassifier> uriClassifier =
  277. do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
  278. if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
  279. rv == NS_ERROR_NOT_AVAILABLE) {
  280. // no URI classifier, ignore this failure.
  281. return NS_ERROR_NOT_AVAILABLE;
  282. }
  283. NS_ENSURE_SUCCESS(rv, rv);
  284. nsCOMPtr<nsIScriptSecurityManager> securityManager =
  285. do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
  286. NS_ENSURE_SUCCESS(rv, rv);
  287. nsCOMPtr<nsIPrincipal> principal;
  288. rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
  289. NS_ENSURE_SUCCESS(rv, rv);
  290. bool expectCallback;
  291. bool trackingProtectionEnabled = false;
  292. (void)ShouldEnableTrackingProtection(mChannel, &trackingProtectionEnabled);
  293. if (LOG_ENABLED()) {
  294. nsCOMPtr<nsIURI> principalURI;
  295. principal->GetURI(getter_AddRefs(principalURI));
  296. LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel with "
  297. "uri %s", this, principalURI->GetSpecOrDefault().get(),
  298. uri->GetSpecOrDefault().get()));
  299. }
  300. rv = uriClassifier->Classify(principal, trackingProtectionEnabled, this,
  301. &expectCallback);
  302. if (NS_FAILED(rv)) {
  303. return rv;
  304. }
  305. if (expectCallback) {
  306. // Suspend the channel, it will be resumed when we get the classifier
  307. // callback.
  308. rv = mChannel->Suspend();
  309. if (NS_FAILED(rv)) {
  310. // Some channels (including nsJSChannel) fail on Suspend. This
  311. // shouldn't be fatal, but will prevent malware from being
  312. // blocked on these channels.
  313. LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
  314. return rv;
  315. }
  316. mSuspendedChannel = true;
  317. LOG(("nsChannelClassifier[%p]: suspended channel %p",
  318. this, mChannel.get()));
  319. } else {
  320. LOG(("nsChannelClassifier[%p]: not expecting callback", this));
  321. return NS_ERROR_FAILURE;
  322. }
  323. return NS_OK;
  324. }
  325. bool
  326. nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri,
  327. const nsACString &aWhitelisted)
  328. {
  329. nsAutoCString host;
  330. nsresult rv = aUri->GetHost(host);
  331. if (NS_FAILED(rv) || host.IsEmpty()) {
  332. return false;
  333. }
  334. ToLowerCase(host);
  335. nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
  336. while (tokenizer.hasMoreTokens()) {
  337. const nsCSubstring& token = tokenizer.nextToken();
  338. if (token.Equals(host)) {
  339. LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
  340. this, host.get()));
  341. return true;
  342. }
  343. }
  344. return false;
  345. }
  346. // Note in the cache entry that this URL was classified, so that future
  347. // cached loads don't need to be checked.
  348. void
  349. nsChannelClassifier::MarkEntryClassified(nsresult status)
  350. {
  351. // Should only be called in the parent process.
  352. MOZ_ASSERT(XRE_IsParentProcess());
  353. // Don't cache tracking classifications because we support allowlisting.
  354. if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) {
  355. return;
  356. }
  357. if (LOG_ENABLED()) {
  358. nsAutoCString errorName;
  359. GetErrorName(status, errorName);
  360. nsCOMPtr<nsIURI> uri;
  361. mChannel->GetURI(getter_AddRefs(uri));
  362. nsAutoCString spec;
  363. uri->GetAsciiSpec(spec);
  364. LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s",
  365. errorName.get(), spec.get()));
  366. }
  367. nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
  368. if (!cachingChannel) {
  369. return;
  370. }
  371. nsCOMPtr<nsISupports> cacheToken;
  372. cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
  373. if (!cacheToken) {
  374. return;
  375. }
  376. nsCOMPtr<nsICacheEntry> cacheEntry =
  377. do_QueryInterface(cacheToken);
  378. if (!cacheEntry) {
  379. return;
  380. }
  381. cacheEntry->SetMetaDataElement("necko:classified",
  382. NS_SUCCEEDED(status) ? "1" : nullptr);
  383. }
  384. bool
  385. nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
  386. {
  387. // Should only be called in the parent process.
  388. MOZ_ASSERT(XRE_IsParentProcess());
  389. nsCOMPtr<nsICachingChannel> cachingChannel =
  390. do_QueryInterface(aChannel);
  391. if (!cachingChannel) {
  392. return false;
  393. }
  394. // Only check the tag if we are loading from the cache without
  395. // validation.
  396. bool fromCache;
  397. if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
  398. return false;
  399. }
  400. nsCOMPtr<nsISupports> cacheToken;
  401. cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
  402. if (!cacheToken) {
  403. return false;
  404. }
  405. nsCOMPtr<nsICacheEntry> cacheEntry =
  406. do_QueryInterface(cacheToken);
  407. if (!cacheEntry) {
  408. return false;
  409. }
  410. nsXPIDLCString tag;
  411. cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
  412. return tag.EqualsLiteral("1");
  413. }
  414. //static
  415. bool
  416. nsChannelClassifier::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
  417. {
  418. nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
  419. nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
  420. if (!channelLoadInfo || !docURI) {
  421. return false;
  422. }
  423. nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
  424. if (!channelLoadingPrincipal) {
  425. // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
  426. // loads should not be blocked by Tracking Protection, so we will return
  427. // false
  428. return false;
  429. }
  430. nsCOMPtr<nsIURI> channelLoadingURI;
  431. channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
  432. if (!channelLoadingURI) {
  433. return false;
  434. }
  435. bool equals = false;
  436. nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
  437. return NS_SUCCEEDED(rv) && equals;
  438. }
  439. // static
  440. nsresult
  441. nsChannelClassifier::SetBlockedTrackingContent(nsIChannel *channel)
  442. {
  443. // Can be called in EITHER the parent or child process.
  444. nsCOMPtr<nsIParentChannel> parentChannel;
  445. NS_QueryNotificationCallbacks(channel, parentChannel);
  446. if (parentChannel) {
  447. // This channel is a parent-process proxy for a child process request. The
  448. // actual channel will be notified via the status passed to
  449. // nsIRequest::Cancel and do this for us.
  450. return NS_OK;
  451. }
  452. nsresult rv;
  453. nsCOMPtr<mozIDOMWindowProxy> win;
  454. nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
  455. do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
  456. NS_ENSURE_SUCCESS(rv, NS_OK);
  457. rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
  458. NS_ENSURE_SUCCESS(rv, NS_OK);
  459. auto* pwin = nsPIDOMWindowOuter::From(win);
  460. nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
  461. if (!docShell) {
  462. return NS_OK;
  463. }
  464. nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
  465. NS_ENSURE_TRUE(doc, NS_OK);
  466. // This event might come after the user has navigated to another page.
  467. // To prevent showing the TrackingProtection UI on the wrong page, we need to
  468. // check that the loading URI for the channel is the same as the URI currently
  469. // loaded in the document.
  470. if (!SameLoadingURI(doc, channel)) {
  471. return NS_OK;
  472. }
  473. // Notify nsIWebProgressListeners of this security event.
  474. // Can be used to change the UI state.
  475. nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
  476. NS_ENSURE_SUCCESS(rv, NS_OK);
  477. uint32_t state = 0;
  478. nsCOMPtr<nsISecureBrowserUI> securityUI;
  479. docShell->GetSecurityUI(getter_AddRefs(securityUI));
  480. if (!securityUI) {
  481. return NS_OK;
  482. }
  483. doc->SetHasTrackingContentBlocked(true);
  484. securityUI->GetState(&state);
  485. state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
  486. eventSink->OnSecurityChange(nullptr, state);
  487. // Log a warning to the web console.
  488. nsCOMPtr<nsIURI> uri;
  489. channel->GetURI(getter_AddRefs(uri));
  490. NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
  491. const char16_t* params[] = { spec.get() };
  492. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  493. NS_LITERAL_CSTRING("Tracking Protection"),
  494. doc,
  495. nsContentUtils::eNECKO_PROPERTIES,
  496. "TrackingUriBlocked",
  497. params, ArrayLength(params));
  498. return NS_OK;
  499. }
  500. nsresult
  501. nsChannelClassifier::IsTrackerWhitelisted()
  502. {
  503. nsresult rv;
  504. nsCOMPtr<nsIURIClassifier> uriClassifier =
  505. do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
  506. NS_ENSURE_SUCCESS(rv, rv);
  507. nsAutoCString tables;
  508. Preferences::GetCString("urlclassifier.trackingWhitelistTable", &tables);
  509. if (tables.IsEmpty()) {
  510. LOG(("nsChannelClassifier[%p]:IsTrackerWhitelisted whitelist disabled",
  511. this));
  512. return NS_ERROR_TRACKING_URI;
  513. }
  514. nsCOMPtr<nsIHttpChannelInternal> chan = do_QueryInterface(mChannel, &rv);
  515. NS_ENSURE_SUCCESS(rv, rv);
  516. nsCOMPtr<nsIURI> topWinURI;
  517. rv = chan->GetTopWindowURI(getter_AddRefs(topWinURI));
  518. NS_ENSURE_SUCCESS(rv, rv);
  519. if (!topWinURI) {
  520. LOG(("nsChannelClassifier[%p]: No window URI", this));
  521. return NS_ERROR_TRACKING_URI;
  522. }
  523. nsCOMPtr<nsIScriptSecurityManager> securityManager =
  524. do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
  525. NS_ENSURE_SUCCESS(rv, rv);
  526. nsCOMPtr<nsIPrincipal> chanPrincipal;
  527. rv = securityManager->GetChannelURIPrincipal(mChannel,
  528. getter_AddRefs(chanPrincipal));
  529. NS_ENSURE_SUCCESS(rv, rv);
  530. // Craft a whitelist URL like "toplevel.page/?resource=third.party.domain"
  531. nsAutoCString pageHostname, resourceDomain;
  532. rv = topWinURI->GetHost(pageHostname);
  533. NS_ENSURE_SUCCESS(rv, rv);
  534. rv = chanPrincipal->GetBaseDomain(resourceDomain);
  535. NS_ENSURE_SUCCESS(rv, rv);
  536. nsAutoCString whitelistEntry = NS_LITERAL_CSTRING("http://") +
  537. pageHostname + NS_LITERAL_CSTRING("/?resource=") + resourceDomain;
  538. LOG(("nsChannelClassifier[%p]: Looking for %s in the whitelist",
  539. this, whitelistEntry.get()));
  540. nsCOMPtr<nsIURI> whitelistURI;
  541. rv = NS_NewURI(getter_AddRefs(whitelistURI), whitelistEntry);
  542. NS_ENSURE_SUCCESS(rv, rv);
  543. // Check whether or not the tracker is in the entity whitelist
  544. nsAutoCString results;
  545. rv = uriClassifier->ClassifyLocalWithTables(whitelistURI, tables, results);
  546. NS_ENSURE_SUCCESS(rv, rv);
  547. if (!results.IsEmpty()) {
  548. return NS_OK; // found it on the whitelist, must not be blocked
  549. }
  550. LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
  551. this, whitelistEntry.get()));
  552. return NS_ERROR_TRACKING_URI;
  553. }
  554. NS_IMETHODIMP
  555. nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
  556. {
  557. // Should only be called in the parent process.
  558. MOZ_ASSERT(XRE_IsParentProcess());
  559. if (aErrorCode == NS_ERROR_TRACKING_URI &&
  560. NS_SUCCEEDED(IsTrackerWhitelisted())) {
  561. LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
  562. "in whitelist so we won't block it", this));
  563. aErrorCode = NS_OK;
  564. }
  565. if (mSuspendedChannel) {
  566. nsAutoCString errorName;
  567. if (LOG_ENABLED()) {
  568. GetErrorName(aErrorCode, errorName);
  569. LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
  570. this, errorName.get()));
  571. }
  572. MarkEntryClassified(aErrorCode);
  573. if (NS_FAILED(aErrorCode)) {
  574. if (LOG_ENABLED()) {
  575. nsCOMPtr<nsIURI> uri;
  576. mChannel->GetURI(getter_AddRefs(uri));
  577. LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
  578. "with error code %s", this, mChannel.get(),
  579. uri->GetSpecOrDefault().get(), errorName.get()));
  580. }
  581. // Channel will be cancelled (page element blocked) due to tracking.
  582. // Do update the security state of the document and fire a security
  583. // change event.
  584. if (aErrorCode == NS_ERROR_TRACKING_URI) {
  585. SetBlockedTrackingContent(mChannel);
  586. }
  587. mChannel->Cancel(aErrorCode);
  588. }
  589. LOG(("nsChannelClassifier[%p]: resuming channel %p from "
  590. "OnClassifyComplete", this, mChannel.get()));
  591. mChannel->Resume();
  592. }
  593. mChannel = nullptr;
  594. return NS_OK;
  595. }
  596. } // namespace net
  597. } // namespace mozilla