LCOV - code coverage report
Current view: top level - extensions/permissions - nsContentBlocker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 58 132 43.9 %
Date: 2017-07-14 16:53:18 Functions: 8 10 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "nsContentBlocker.h"
       6             : #include "nsIContent.h"
       7             : #include "nsIURI.h"
       8             : #include "nsIServiceManager.h"
       9             : #include "nsIDocShellTreeItem.h"
      10             : #include "nsIPrefService.h"
      11             : #include "nsIPrefBranch.h"
      12             : #include "nsIDocShell.h"
      13             : #include "nsString.h"
      14             : #include "nsContentPolicyUtils.h"
      15             : #include "nsIObjectLoadingContent.h"
      16             : #include "mozilla/ArrayUtils.h"
      17             : #include "nsContentUtils.h"
      18             : 
      19             : // Possible behavior pref values
      20             : // Those map to the nsIPermissionManager values where possible
      21             : #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
      22             : #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
      23             : #define BEHAVIOR_NOFOREIGN 3
      24             : 
      25             : // From nsIContentPolicy
      26             : static const char *kTypeString[] = {
      27             :                                     "other",
      28             :                                     "script",
      29             :                                     "image",
      30             :                                     "stylesheet",
      31             :                                     "object",
      32             :                                     "document",
      33             :                                     "subdocument",
      34             :                                     "refresh",
      35             :                                     "xbl",
      36             :                                     "ping",
      37             :                                     "xmlhttprequest",
      38             :                                     "objectsubrequest",
      39             :                                     "dtd",
      40             :                                     "font",
      41             :                                     "media",
      42             :                                     "websocket",
      43             :                                     "csp_report",
      44             :                                     "xslt",
      45             :                                     "beacon",
      46             :                                     "fetch",
      47             :                                     "image",
      48             :                                     "manifest",
      49             :                                     "", // TYPE_INTERNAL_SCRIPT
      50             :                                     "", // TYPE_INTERNAL_WORKER
      51             :                                     "", // TYPE_INTERNAL_SHARED_WORKER
      52             :                                     "", // TYPE_INTERNAL_EMBED
      53             :                                     "", // TYPE_INTERNAL_OBJECT
      54             :                                     "", // TYPE_INTERNAL_FRAME
      55             :                                     "", // TYPE_INTERNAL_IFRAME
      56             :                                     "", // TYPE_INTERNAL_AUDIO
      57             :                                     "", // TYPE_INTERNAL_VIDEO
      58             :                                     "", // TYPE_INTERNAL_TRACK
      59             :                                     "", // TYPE_INTERNAL_XMLHTTPREQUEST
      60             :                                     "", // TYPE_INTERNAL_EVENTSOURCE
      61             :                                     "", // TYPE_INTERNAL_SERVICE_WORKER
      62             :                                     "", // TYPE_INTERNAL_SCRIPT_PRELOAD
      63             :                                     "", // TYPE_INTERNAL_IMAGE
      64             :                                     "", // TYPE_INTERNAL_IMAGE_PRELOAD
      65             :                                     "", // TYPE_INTERNAL_STYLESHEET
      66             :                                     "", // TYPE_INTERNAL_STYLESHEET_PRELOAD
      67             :                                     "", // TYPE_INTERNAL_IMAGE_FAVICON
      68             :                                     "", // TYPE_INTERNAL_WORKERS_IMPORT_SCRIPTS
      69             : };
      70             : 
      71             : #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
      72             : uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
      73             : 
      74          42 : NS_IMPL_ISUPPORTS(nsContentBlocker,
      75             :                   nsIContentPolicy,
      76             :                   nsIObserver,
      77             :                   nsISupportsWeakReference)
      78             : 
      79           2 : nsContentBlocker::nsContentBlocker()
      80             : {
      81           2 :   memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES);
      82           2 : }
      83             : 
      84             : nsresult
      85           2 : nsContentBlocker::Init()
      86             : {
      87             :   nsresult rv;
      88           2 :   mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
      89           2 :   NS_ENSURE_SUCCESS(rv, rv);
      90             : 
      91           4 :   nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
      92           2 :   NS_ENSURE_SUCCESS(rv, rv);
      93             : 
      94           4 :   nsCOMPtr<nsIPrefBranch> prefBranch;
      95           2 :   rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch));
      96           2 :   NS_ENSURE_SUCCESS(rv, rv);
      97             : 
      98             :   // Migrate old image blocker pref
      99           4 :   nsCOMPtr<nsIPrefBranch> oldPrefBranch;
     100           2 :   oldPrefBranch = do_QueryInterface(prefService);
     101             :   int32_t oldPref;
     102           2 :   rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref);
     103           2 :   if (NS_SUCCEEDED(rv) && oldPref) {
     104             :     int32_t newPref;
     105           0 :     switch (oldPref) {
     106             :       default:
     107           0 :         newPref = BEHAVIOR_ACCEPT;
     108           0 :         break;
     109             :       case 1:
     110           0 :         newPref = BEHAVIOR_NOFOREIGN;
     111           0 :         break;
     112             :       case 2:
     113           0 :         newPref = BEHAVIOR_REJECT;
     114           0 :         break;
     115             :     }
     116           0 :     prefBranch->SetIntPref("image", newPref);
     117           0 :     oldPrefBranch->ClearUserPref("network.image.imageBehavior");
     118             :   }
     119             : 
     120             : 
     121             :   // The branch is not a copy of the prefservice, but a new object, because
     122             :   // it is a non-default branch. Adding obeservers to it will only work if
     123             :   // we make sure that the object doesn't die. So, keep a reference to it.
     124           2 :   mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
     125           2 :   NS_ENSURE_SUCCESS(rv, rv);
     126             : 
     127           2 :   rv = mPrefBranchInternal->AddObserver("", this, true);
     128           2 :   PrefChanged(prefBranch, nullptr);
     129             : 
     130           2 :   return rv;
     131             : }
     132             : 
     133             : #undef  LIMIT
     134             : #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
     135             : 
     136             : void
     137           2 : nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch,
     138             :                               const char    *aPref)
     139             : {
     140             :   int32_t val;
     141             : 
     142             : #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
     143             : 
     144          86 :   for(uint32_t i = 0; i < NUMBER_OF_TYPES; ++i) {
     145         212 :     if (*kTypeString[i] &&
     146         128 :         PREF_CHANGED(kTypeString[i]) &&
     147          44 :         NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val)))
     148           4 :       mBehaviorPref[i] = LIMIT(val, 1, 3, 1);
     149             :   }
     150             : 
     151           2 : }
     152             : 
     153             : // nsIContentPolicy Implementation
     154             : NS_IMETHODIMP
     155          23 : nsContentBlocker::ShouldLoad(uint32_t          aContentType,
     156             :                              nsIURI           *aContentLocation,
     157             :                              nsIURI           *aRequestingLocation,
     158             :                              nsISupports      *aRequestingContext,
     159             :                              const nsACString &aMimeGuess,
     160             :                              nsISupports      *aExtra,
     161             :                              nsIPrincipal     *aRequestPrincipal,
     162             :                              int16_t          *aDecision)
     163             : {
     164          23 :   MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
     165             :              "We should only see external content policy types here.");
     166             : 
     167          23 :   *aDecision = nsIContentPolicy::ACCEPT;
     168             :   nsresult rv;
     169             : 
     170             :   // Ony support NUMBER_OF_TYPES content types. that all there is at the
     171             :   // moment, but you never know...
     172          23 :   if (aContentType > NUMBER_OF_TYPES)
     173           0 :     return NS_OK;
     174             : 
     175             :   // we can't do anything without this
     176          23 :   if (!aContentLocation)
     177           0 :     return NS_OK;
     178             : 
     179             :   // The final type of an object tag may mutate before it reaches
     180             :   // shouldProcess, so we cannot make any sane blocking decisions here
     181          23 :   if (aContentType == nsIContentPolicy::TYPE_OBJECT)
     182           0 :     return NS_OK;
     183             : 
     184             :   // we only want to check http, https, ftp
     185             :   // for chrome:// and resources and others, no need to check.
     186          46 :   nsAutoCString scheme;
     187          23 :   aContentLocation->GetScheme(scheme);
     188          69 :   if (!scheme.LowerCaseEqualsLiteral("ftp") &&
     189          39 :       !scheme.LowerCaseEqualsLiteral("http") &&
     190          16 :       !scheme.LowerCaseEqualsLiteral("https"))
     191          16 :     return NS_OK;
     192             : 
     193             :   bool shouldLoad, fromPrefs;
     194           7 :   rv = TestPermission(aContentLocation, aRequestingLocation, aContentType,
     195           7 :                       &shouldLoad, &fromPrefs);
     196           7 :   NS_ENSURE_SUCCESS(rv, rv);
     197           7 :   if (!shouldLoad) {
     198           0 :     if (fromPrefs) {
     199           0 :       *aDecision = nsIContentPolicy::REJECT_TYPE;
     200             :     } else {
     201           0 :       *aDecision = nsIContentPolicy::REJECT_SERVER;
     202             :     }
     203             :   }
     204             : 
     205           7 :   return NS_OK;
     206             : }
     207             : 
     208             : NS_IMETHODIMP
     209           0 : nsContentBlocker::ShouldProcess(uint32_t          aContentType,
     210             :                                 nsIURI           *aContentLocation,
     211             :                                 nsIURI           *aRequestingLocation,
     212             :                                 nsISupports      *aRequestingContext,
     213             :                                 const nsACString &aMimeGuess,
     214             :                                 nsISupports      *aExtra,
     215             :                                 nsIPrincipal     *aRequestPrincipal,
     216             :                                 int16_t          *aDecision)
     217             : {
     218           0 :   MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
     219             :              "We should only see external content policy types here.");
     220             : 
     221             :   // For loads where aRequestingContext is chrome, we should just
     222             :   // accept.  Those are most likely toplevel loads in windows, and
     223             :   // chrome generally knows what it's doing anyway.
     224             :   nsCOMPtr<nsIDocShellTreeItem> item =
     225           0 :     do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext));
     226             : 
     227           0 :   if (item && item->ItemType() == nsIDocShellTreeItem::typeChrome) {
     228           0 :     *aDecision = nsIContentPolicy::ACCEPT;
     229           0 :     return NS_OK;
     230             :   }
     231             : 
     232             :   // For objects, we only check policy in shouldProcess, as the final type isn't
     233             :   // determined until the channel is open -- We don't want to block images in
     234             :   // object tags because plugins are disallowed.
     235             :   // NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is
     236             :   // intentional, as aContentLocation may be null for plugins that load by type
     237             :   // (e.g. java)
     238           0 :   if (aContentType == nsIContentPolicy::TYPE_OBJECT) {
     239           0 :     *aDecision = nsIContentPolicy::ACCEPT;
     240             : 
     241             :     bool shouldLoad, fromPrefs;
     242           0 :     nsresult rv = TestPermission(aContentLocation, aRequestingLocation,
     243           0 :                                  aContentType, &shouldLoad, &fromPrefs);
     244           0 :     NS_ENSURE_SUCCESS(rv, rv);
     245           0 :     if (!shouldLoad) {
     246           0 :       if (fromPrefs) {
     247           0 :         *aDecision = nsIContentPolicy::REJECT_TYPE;
     248             :       } else {
     249           0 :         *aDecision = nsIContentPolicy::REJECT_SERVER;
     250             :       }
     251             :     }
     252           0 :     return NS_OK;
     253             :   }
     254             : 
     255             :   // This isn't a load from chrome or an object tag - Just do a ShouldLoad()
     256             :   // check -- we want the same answer here
     257             :   return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
     258             :                     aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
     259           0 :                     aDecision);
     260             : }
     261             : 
     262             : nsresult
     263           7 : nsContentBlocker::TestPermission(nsIURI *aCurrentURI,
     264             :                                  nsIURI *aFirstURI,
     265             :                                  int32_t aContentType,
     266             :                                  bool *aPermission,
     267             :                                  bool *aFromPrefs)
     268             : {
     269           7 :   *aFromPrefs = false;
     270             :   nsresult rv;
     271             : 
     272           7 :   if (!*kTypeString[aContentType - 1]) {
     273             :     // Disallow internal content policy types, they should not be used here.
     274           0 :     *aPermission = false;
     275           0 :     return NS_OK;
     276             :   }
     277             : 
     278             :   // This default will also get used if there is an unknown value in the
     279             :   // permission list, or if the permission manager returns unknown values.
     280           7 :   *aPermission = true;
     281             : 
     282             :   // check the permission list first; if we find an entry, it overrides
     283             :   // default prefs.
     284             :   // Don't forget the aContentType ranges from 1..8, while the
     285             :   // array is indexed 0..7
     286             :   // All permissions tested by this method are preload permissions, so don't
     287             :   // bother actually checking with the permission manager unless we have a
     288             :   // preload permission.
     289           7 :   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
     290           7 :   if (mPermissionManager->GetHasPreloadPermissions()) {
     291           0 :     rv = mPermissionManager->TestPermission(aCurrentURI,
     292           0 :                                             kTypeString[aContentType - 1],
     293           0 :                                             &permission);
     294           0 :     NS_ENSURE_SUCCESS(rv, rv);
     295             :   }
     296             : 
     297             :   // If there is nothing on the list, use the default.
     298           7 :   if (!permission) {
     299           7 :     permission = mBehaviorPref[aContentType - 1];
     300           7 :     *aFromPrefs = true;
     301             :   }
     302             : 
     303             :   // Use the fact that the nsIPermissionManager values map to
     304             :   // the BEHAVIOR_* values above.
     305           7 :   switch (permission) {
     306             :   case BEHAVIOR_ACCEPT:
     307           7 :     *aPermission = true;
     308          14 :     break;
     309             :   case BEHAVIOR_REJECT:
     310           0 :     *aPermission = false;
     311           0 :     break;
     312             : 
     313             :   case BEHAVIOR_NOFOREIGN:
     314             :     // Third party checking
     315             : 
     316             :     // Need a requesting uri for third party checks to work.
     317           0 :     if (!aFirstURI)
     318           0 :       return NS_OK;
     319             : 
     320           0 :     bool trustedSource = false;
     321           0 :     rv = aFirstURI->SchemeIs("chrome", &trustedSource);
     322           0 :     NS_ENSURE_SUCCESS(rv,rv);
     323           0 :     if (!trustedSource) {
     324           0 :       rv = aFirstURI->SchemeIs("resource", &trustedSource);
     325           0 :       NS_ENSURE_SUCCESS(rv,rv);
     326             :     }
     327           0 :     if (trustedSource)
     328           0 :       return NS_OK;
     329             : 
     330             :     // compare tails of names checking to see if they have a common domain
     331             :     // we do this by comparing the tails of both names where each tail
     332             :     // includes at least one dot
     333             : 
     334             :     // A more generic method somewhere would be nice
     335             : 
     336           0 :     nsAutoCString currentHost;
     337           0 :     rv = aCurrentURI->GetAsciiHost(currentHost);
     338           0 :     NS_ENSURE_SUCCESS(rv, rv);
     339             : 
     340             :     // Search for two dots, starting at the end.
     341             :     // If there are no two dots found, ++dot will turn to zero,
     342             :     // that will return the entire string.
     343           0 :     int32_t dot = currentHost.RFindChar('.');
     344           0 :     dot = currentHost.RFindChar('.', dot-1);
     345           0 :     ++dot;
     346             : 
     347             :     // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
     348             :     // This will break on co.uk
     349             :     const nsACString& tail =
     350           0 :       Substring(currentHost, dot, currentHost.Length() - dot);
     351             : 
     352           0 :     nsAutoCString firstHost;
     353           0 :     rv = aFirstURI->GetAsciiHost(firstHost);
     354           0 :     NS_ENSURE_SUCCESS(rv, rv);
     355             : 
     356             :     // If the tail is longer then the whole firstHost, it will never match
     357           0 :     if (firstHost.Length() < tail.Length()) {
     358           0 :       *aPermission = false;
     359           0 :       return NS_OK;
     360             :     }
     361             : 
     362             :     // Get the last part of the firstUri with the same length as |tail|
     363             :     const nsACString& firstTail =
     364           0 :       Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length());
     365             : 
     366             :     // Check that both tails are the same, and that just before the tail in
     367             :     // |firstUri| there is a dot. That means both url are in the same domain
     368           0 :     if ((firstHost.Length() > tail.Length() &&
     369           0 :          firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') ||
     370           0 :         !tail.Equals(firstTail)) {
     371           0 :       *aPermission = false;
     372             :     }
     373           0 :     break;
     374             :   }
     375             : 
     376           7 :   return NS_OK;
     377             : }
     378             : 
     379             : NS_IMETHODIMP
     380           0 : nsContentBlocker::Observe(nsISupports     *aSubject,
     381             :                           const char      *aTopic,
     382             :                           const char16_t *aData)
     383             : {
     384           0 :   NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
     385             :                "unexpected topic - we only deal with pref changes!");
     386             : 
     387           0 :   if (mPrefBranchInternal)
     388           0 :     PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get());
     389           0 :   return NS_OK;
     390             : }

Generated by: LCOV version 1.13