extensions/permissions/nsContentBlocker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     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"
    18 // Possible behavior pref values
    19 // Those map to the nsIPermissionManager values where possible
    20 #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
    21 #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
    22 #define BEHAVIOR_NOFOREIGN 3
    24 // From nsIContentPolicy
    25 static const char *kTypeString[] = {"other",
    26                                     "script",
    27                                     "image",
    28                                     "stylesheet",
    29                                     "object",
    30                                     "document",
    31                                     "subdocument",
    32                                     "refresh",
    33                                     "xbl",
    34                                     "ping",
    35                                     "xmlhttprequest",
    36                                     "objectsubrequest",
    37                                     "dtd",
    38                                     "font",
    39                                     "media",
    40                                     "websocket",
    41                                     "csp_report",
    42                                     "xslt"};
    44 #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
    45 uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
    47 NS_IMPL_ISUPPORTS(nsContentBlocker, 
    48                   nsIContentPolicy,
    49                   nsIObserver,
    50                   nsISupportsWeakReference)
    52 nsContentBlocker::nsContentBlocker()
    53 {
    54   memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES);
    55 }
    57 nsresult
    58 nsContentBlocker::Init()
    59 {
    60   nsresult rv;
    61   mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    62   NS_ENSURE_SUCCESS(rv, rv);
    64   nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
    65   NS_ENSURE_SUCCESS(rv, rv);
    67   nsCOMPtr<nsIPrefBranch> prefBranch;
    68   rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch));
    69   NS_ENSURE_SUCCESS(rv, rv);
    71   // Migrate old image blocker pref
    72   nsCOMPtr<nsIPrefBranch> oldPrefBranch;
    73   oldPrefBranch = do_QueryInterface(prefService);
    74   int32_t oldPref;
    75   rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref);
    76   if (NS_SUCCEEDED(rv) && oldPref) {
    77     int32_t newPref;
    78     switch (oldPref) {
    79       default:
    80         newPref = BEHAVIOR_ACCEPT;
    81         break;
    82       case 1:
    83         newPref = BEHAVIOR_NOFOREIGN;
    84         break;
    85       case 2:
    86         newPref = BEHAVIOR_REJECT;
    87         break;
    88     }
    89     prefBranch->SetIntPref("image", newPref);
    90     oldPrefBranch->ClearUserPref("network.image.imageBehavior");
    91   }
    94   // The branch is not a copy of the prefservice, but a new object, because
    95   // it is a non-default branch. Adding obeservers to it will only work if
    96   // we make sure that the object doesn't die. So, keep a reference to it.
    97   mPrefBranchInternal = do_QueryInterface(prefBranch, &rv);
    98   NS_ENSURE_SUCCESS(rv, rv);
   100   rv = mPrefBranchInternal->AddObserver("", this, true);
   101   PrefChanged(prefBranch, nullptr);
   103   return rv;
   104 }
   106 #undef  LIMIT
   107 #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
   109 void
   110 nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch,
   111                               const char    *aPref)
   112 {
   113   int32_t val;
   115 #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
   117   for(uint32_t i = 0; i < NUMBER_OF_TYPES; ++i) {
   118     if (PREF_CHANGED(kTypeString[i]) &&
   119         NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val)))
   120       mBehaviorPref[i] = LIMIT(val, 1, 3, 1);
   121   }
   123 }
   125 // nsIContentPolicy Implementation
   126 NS_IMETHODIMP 
   127 nsContentBlocker::ShouldLoad(uint32_t          aContentType,
   128                              nsIURI           *aContentLocation,
   129                              nsIURI           *aRequestingLocation,
   130                              nsISupports      *aRequestingContext,
   131                              const nsACString &aMimeGuess,
   132                              nsISupports      *aExtra,
   133                              nsIPrincipal     *aRequestPrincipal,
   134                              int16_t          *aDecision)
   135 {
   136   *aDecision = nsIContentPolicy::ACCEPT;
   137   nsresult rv;
   139   // Ony support NUMBER_OF_TYPES content types. that all there is at the
   140   // moment, but you never know...
   141   if (aContentType > NUMBER_OF_TYPES)
   142     return NS_OK;
   144   // we can't do anything without this
   145   if (!aContentLocation)
   146     return NS_OK;
   148   // The final type of an object tag may mutate before it reaches
   149   // shouldProcess, so we cannot make any sane blocking decisions here
   150   if (aContentType == nsIContentPolicy::TYPE_OBJECT)
   151     return NS_OK;
   153   // we only want to check http, https, ftp
   154   // for chrome:// and resources and others, no need to check.
   155   nsAutoCString scheme;
   156   aContentLocation->GetScheme(scheme);
   157   if (!scheme.LowerCaseEqualsLiteral("ftp") &&
   158       !scheme.LowerCaseEqualsLiteral("http") &&
   159       !scheme.LowerCaseEqualsLiteral("https"))
   160     return NS_OK;
   162   bool shouldLoad, fromPrefs;
   163   rv = TestPermission(aContentLocation, aRequestingLocation, aContentType,
   164                       &shouldLoad, &fromPrefs);
   165   NS_ENSURE_SUCCESS(rv, rv);
   166   if (!shouldLoad) {
   167     if (fromPrefs) {
   168       *aDecision = nsIContentPolicy::REJECT_TYPE;
   169     } else {
   170       *aDecision = nsIContentPolicy::REJECT_SERVER;
   171     }
   172   }
   174   return NS_OK;
   175 }
   177 NS_IMETHODIMP
   178 nsContentBlocker::ShouldProcess(uint32_t          aContentType,
   179                                 nsIURI           *aContentLocation,
   180                                 nsIURI           *aRequestingLocation,
   181                                 nsISupports      *aRequestingContext,
   182                                 const nsACString &aMimeGuess,
   183                                 nsISupports      *aExtra,
   184                                 nsIPrincipal     *aRequestPrincipal,
   185                                 int16_t          *aDecision)
   186 {
   187   // For loads where aRequestingContext is chrome, we should just
   188   // accept.  Those are most likely toplevel loads in windows, and
   189   // chrome generally knows what it's doing anyway.
   190   nsCOMPtr<nsIDocShellTreeItem> item =
   191     do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext));
   193   if (item && item->ItemType() == nsIDocShellTreeItem::typeChrome) {
   194     *aDecision = nsIContentPolicy::ACCEPT;
   195     return NS_OK;
   196   }
   198   // For objects, we only check policy in shouldProcess, as the final type isn't
   199   // determined until the channel is open -- We don't want to block images in
   200   // object tags because plugins are disallowed.
   201   // NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is
   202   // intentional, as aContentLocation may be null for plugins that load by type
   203   // (e.g. java)
   204   if (aContentType == nsIContentPolicy::TYPE_OBJECT) {
   205     *aDecision = nsIContentPolicy::ACCEPT;
   207     bool shouldLoad, fromPrefs;
   208     nsresult rv = TestPermission(aContentLocation, aRequestingLocation,
   209                                  aContentType, &shouldLoad, &fromPrefs);
   210     NS_ENSURE_SUCCESS(rv, rv);
   211     if (!shouldLoad) {
   212       if (fromPrefs) {
   213         *aDecision = nsIContentPolicy::REJECT_TYPE;
   214       } else {
   215         *aDecision = nsIContentPolicy::REJECT_SERVER;
   216       }
   217     }
   218     return NS_OK;
   219   }
   221   // This isn't a load from chrome or an object tag - Just do a ShouldLoad()
   222   // check -- we want the same answer here
   223   return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
   224                     aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
   225                     aDecision);
   226 }
   228 nsresult
   229 nsContentBlocker::TestPermission(nsIURI *aCurrentURI,
   230                                  nsIURI *aFirstURI,
   231                                  int32_t aContentType,
   232                                  bool *aPermission,
   233                                  bool *aFromPrefs)
   234 {
   235   *aFromPrefs = false;
   236   // This default will also get used if there is an unknown value in the
   237   // permission list, or if the permission manager returns unknown values.
   238   *aPermission = true;
   240   // check the permission list first; if we find an entry, it overrides
   241   // default prefs.
   242   // Don't forget the aContentType ranges from 1..8, while the
   243   // array is indexed 0..7
   244   uint32_t permission;
   245   nsresult rv = mPermissionManager->TestPermission(aCurrentURI, 
   246                                                    kTypeString[aContentType - 1],
   247                                                    &permission);
   248   NS_ENSURE_SUCCESS(rv, rv);
   250   // If there is nothing on the list, use the default.
   251   if (!permission) {
   252     permission = mBehaviorPref[aContentType - 1];
   253     *aFromPrefs = true;
   254   }
   256   // Use the fact that the nsIPermissionManager values map to 
   257   // the BEHAVIOR_* values above.
   258   switch (permission) {
   259   case BEHAVIOR_ACCEPT:
   260     *aPermission = true;
   261     break;
   262   case BEHAVIOR_REJECT:
   263     *aPermission = false;
   264     break;
   266   case BEHAVIOR_NOFOREIGN:
   267     // Third party checking
   269     // Need a requesting uri for third party checks to work.
   270     if (!aFirstURI)
   271       return NS_OK;
   273     bool trustedSource = false;
   274     rv = aFirstURI->SchemeIs("chrome", &trustedSource);
   275     NS_ENSURE_SUCCESS(rv,rv);
   276     if (!trustedSource) {
   277       rv = aFirstURI->SchemeIs("resource", &trustedSource);
   278       NS_ENSURE_SUCCESS(rv,rv);
   279     }
   280     if (trustedSource)
   281       return NS_OK;
   283     // compare tails of names checking to see if they have a common domain
   284     // we do this by comparing the tails of both names where each tail 
   285     // includes at least one dot
   287     // A more generic method somewhere would be nice
   289     nsAutoCString currentHost;
   290     rv = aCurrentURI->GetAsciiHost(currentHost);
   291     NS_ENSURE_SUCCESS(rv, rv);
   293     // Search for two dots, starting at the end.
   294     // If there are no two dots found, ++dot will turn to zero,
   295     // that will return the entire string.
   296     int32_t dot = currentHost.RFindChar('.');
   297     dot = currentHost.RFindChar('.', dot-1);
   298     ++dot;
   300     // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
   301     // This will break on co.uk
   302     const nsCSubstring &tail =
   303       Substring(currentHost, dot, currentHost.Length() - dot);
   305     nsAutoCString firstHost;
   306     rv = aFirstURI->GetAsciiHost(firstHost);
   307     NS_ENSURE_SUCCESS(rv, rv);
   309     // If the tail is longer then the whole firstHost, it will never match
   310     if (firstHost.Length() < tail.Length()) {
   311       *aPermission = false;
   312       return NS_OK;
   313     }
   315     // Get the last part of the firstUri with the same length as |tail|
   316     const nsCSubstring &firstTail = 
   317       Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length());
   319     // Check that both tails are the same, and that just before the tail in
   320     // |firstUri| there is a dot. That means both url are in the same domain
   321     if ((firstHost.Length() > tail.Length() && 
   322          firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') || 
   323         !tail.Equals(firstTail)) {
   324       *aPermission = false;
   325     }
   326     break;
   327   }
   329   return NS_OK;
   330 }
   332 NS_IMETHODIMP
   333 nsContentBlocker::Observe(nsISupports     *aSubject,
   334                           const char      *aTopic,
   335                           const char16_t *aData)
   336 {
   337   NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
   338                "unexpected topic - we only deal with pref changes!");
   340   if (mPrefBranchInternal)
   341     PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get());
   342   return NS_OK;
   343 }

mercurial