diff options
Diffstat (limited to 'extensions/permissions')
-rw-r--r-- | extensions/permissions/moz.build | 12 | ||||
-rw-r--r-- | extensions/permissions/nsContentBlocker.cpp | 377 | ||||
-rw-r--r-- | extensions/permissions/nsContentBlocker.h | 53 | ||||
-rw-r--r-- | extensions/permissions/nsModuleFactory.cpp | 37 |
4 files changed, 479 insertions, 0 deletions
diff --git a/extensions/permissions/moz.build b/extensions/permissions/moz.build new file mode 100644 index 000000000..401f01848 --- /dev/null +++ b/extensions/permissions/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'nsContentBlocker.cpp', + 'nsModuleFactory.cpp', +] + +FINAL_LIBRARY = 'xul' diff --git a/extensions/permissions/nsContentBlocker.cpp b/extensions/permissions/nsContentBlocker.cpp new file mode 100644 index 000000000..cc2162b70 --- /dev/null +++ b/extensions/permissions/nsContentBlocker.cpp @@ -0,0 +1,377 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsContentBlocker.h" +#include "nsIContent.h" +#include "nsIURI.h" +#include "nsIServiceManager.h" +#include "nsIDocShellTreeItem.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIDocShell.h" +#include "nsString.h" +#include "nsContentPolicyUtils.h" +#include "nsIObjectLoadingContent.h" +#include "mozilla/ArrayUtils.h" +#include "nsContentUtils.h" + +// Possible behavior pref values +// Those map to the nsIPermissionManager values where possible +#define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION +#define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION +#define BEHAVIOR_NOFOREIGN 3 + +// From nsIContentPolicy +static const char *kTypeString[] = { + "other", + "script", + "image", + "stylesheet", + "object", + "document", + "subdocument", + "refresh", + "xbl", + "ping", + "xmlhttprequest", + "objectsubrequest", + "dtd", + "font", + "media", + "websocket", + "csp_report", + "xslt", + "beacon", + "fetch", + "image", + "manifest", + "", // TYPE_INTERNAL_SCRIPT + "", // TYPE_INTERNAL_WORKER + "", // TYPE_INTERNAL_SHARED_WORKER + "", // TYPE_INTERNAL_EMBED + "", // TYPE_INTERNAL_OBJECT + "", // TYPE_INTERNAL_FRAME + "", // TYPE_INTERNAL_IFRAME + "", // TYPE_INTERNAL_AUDIO + "", // TYPE_INTERNAL_VIDEO + "", // TYPE_INTERNAL_TRACK + "", // TYPE_INTERNAL_XMLHTTPREQUEST + "", // TYPE_INTERNAL_EVENTSOURCE + "", // TYPE_INTERNAL_SERVICE_WORKER +}; + +#define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString) +uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES]; + +NS_IMPL_ISUPPORTS(nsContentBlocker, + nsIContentPolicy, + nsIObserver, + nsISupportsWeakReference) + +nsContentBlocker::nsContentBlocker() +{ + memset(mBehaviorPref, BEHAVIOR_ACCEPT, NUMBER_OF_TYPES); +} + +nsresult +nsContentBlocker::Init() +{ + nsresult rv; + mPermissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIPrefBranch> prefBranch; + rv = prefService->GetBranch("permissions.default.", getter_AddRefs(prefBranch)); + NS_ENSURE_SUCCESS(rv, rv); + + // Migrate old image blocker pref + nsCOMPtr<nsIPrefBranch> oldPrefBranch; + oldPrefBranch = do_QueryInterface(prefService); + int32_t oldPref; + rv = oldPrefBranch->GetIntPref("network.image.imageBehavior", &oldPref); + if (NS_SUCCEEDED(rv) && oldPref) { + int32_t newPref; + switch (oldPref) { + default: + newPref = BEHAVIOR_ACCEPT; + break; + case 1: + newPref = BEHAVIOR_NOFOREIGN; + break; + case 2: + newPref = BEHAVIOR_REJECT; + break; + } + prefBranch->SetIntPref("image", newPref); + oldPrefBranch->ClearUserPref("network.image.imageBehavior"); + } + + + // The branch is not a copy of the prefservice, but a new object, because + // it is a non-default branch. Adding obeservers to it will only work if + // we make sure that the object doesn't die. So, keep a reference to it. + mPrefBranchInternal = do_QueryInterface(prefBranch, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mPrefBranchInternal->AddObserver("", this, true); + PrefChanged(prefBranch, nullptr); + + return rv; +} + +#undef LIMIT +#define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default)) + +void +nsContentBlocker::PrefChanged(nsIPrefBranch *aPrefBranch, + const char *aPref) +{ + int32_t val; + +#define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P)) + + for(uint32_t i = 0; i < NUMBER_OF_TYPES; ++i) { + if (*kTypeString[i] && + PREF_CHANGED(kTypeString[i]) && + NS_SUCCEEDED(aPrefBranch->GetIntPref(kTypeString[i], &val))) + mBehaviorPref[i] = LIMIT(val, 1, 3, 1); + } + +} + +// nsIContentPolicy Implementation +NS_IMETHODIMP +nsContentBlocker::ShouldLoad(uint32_t aContentType, + nsIURI *aContentLocation, + nsIURI *aRequestingLocation, + nsISupports *aRequestingContext, + const nsACString &aMimeGuess, + nsISupports *aExtra, + nsIPrincipal *aRequestPrincipal, + int16_t *aDecision) +{ + MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType), + "We should only see external content policy types here."); + + *aDecision = nsIContentPolicy::ACCEPT; + nsresult rv; + + // Ony support NUMBER_OF_TYPES content types. that all there is at the + // moment, but you never know... + if (aContentType > NUMBER_OF_TYPES) + return NS_OK; + + // we can't do anything without this + if (!aContentLocation) + return NS_OK; + + // The final type of an object tag may mutate before it reaches + // shouldProcess, so we cannot make any sane blocking decisions here + if (aContentType == nsIContentPolicy::TYPE_OBJECT) + return NS_OK; + + // we only want to check http, https, ftp + // for chrome:// and resources and others, no need to check. + nsAutoCString scheme; + aContentLocation->GetScheme(scheme); + if (!scheme.LowerCaseEqualsLiteral("ftp") && + !scheme.LowerCaseEqualsLiteral("http") && + !scheme.LowerCaseEqualsLiteral("https")) + return NS_OK; + + bool shouldLoad, fromPrefs; + rv = TestPermission(aContentLocation, aRequestingLocation, aContentType, + &shouldLoad, &fromPrefs); + NS_ENSURE_SUCCESS(rv, rv); + if (!shouldLoad) { + if (fromPrefs) { + *aDecision = nsIContentPolicy::REJECT_TYPE; + } else { + *aDecision = nsIContentPolicy::REJECT_SERVER; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsContentBlocker::ShouldProcess(uint32_t aContentType, + nsIURI *aContentLocation, + nsIURI *aRequestingLocation, + nsISupports *aRequestingContext, + const nsACString &aMimeGuess, + nsISupports *aExtra, + nsIPrincipal *aRequestPrincipal, + int16_t *aDecision) +{ + MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType), + "We should only see external content policy types here."); + + // For loads where aRequestingContext is chrome, we should just + // accept. Those are most likely toplevel loads in windows, and + // chrome generally knows what it's doing anyway. + nsCOMPtr<nsIDocShellTreeItem> item = + do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext)); + + if (item && item->ItemType() == nsIDocShellTreeItem::typeChrome) { + *aDecision = nsIContentPolicy::ACCEPT; + return NS_OK; + } + + // For objects, we only check policy in shouldProcess, as the final type isn't + // determined until the channel is open -- We don't want to block images in + // object tags because plugins are disallowed. + // NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is + // intentional, as aContentLocation may be null for plugins that load by type + // (e.g. java) + if (aContentType == nsIContentPolicy::TYPE_OBJECT) { + *aDecision = nsIContentPolicy::ACCEPT; + + bool shouldLoad, fromPrefs; + nsresult rv = TestPermission(aContentLocation, aRequestingLocation, + aContentType, &shouldLoad, &fromPrefs); + NS_ENSURE_SUCCESS(rv, rv); + if (!shouldLoad) { + if (fromPrefs) { + *aDecision = nsIContentPolicy::REJECT_TYPE; + } else { + *aDecision = nsIContentPolicy::REJECT_SERVER; + } + } + return NS_OK; + } + + // This isn't a load from chrome or an object tag - Just do a ShouldLoad() + // check -- we want the same answer here + return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, + aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, + aDecision); +} + +nsresult +nsContentBlocker::TestPermission(nsIURI *aCurrentURI, + nsIURI *aFirstURI, + int32_t aContentType, + bool *aPermission, + bool *aFromPrefs) +{ + *aFromPrefs = false; + + if (!*kTypeString[aContentType - 1]) { + // Disallow internal content policy types, they should not be used here. + *aPermission = false; + return NS_OK; + } + + // This default will also get used if there is an unknown value in the + // permission list, or if the permission manager returns unknown values. + *aPermission = true; + + // check the permission list first; if we find an entry, it overrides + // default prefs. + // Don't forget the aContentType ranges from 1..8, while the + // array is indexed 0..7 + uint32_t permission; + nsresult rv = mPermissionManager->TestPermission(aCurrentURI, + kTypeString[aContentType - 1], + &permission); + NS_ENSURE_SUCCESS(rv, rv); + + // If there is nothing on the list, use the default. + if (!permission) { + permission = mBehaviorPref[aContentType - 1]; + *aFromPrefs = true; + } + + // Use the fact that the nsIPermissionManager values map to + // the BEHAVIOR_* values above. + switch (permission) { + case BEHAVIOR_ACCEPT: + *aPermission = true; + break; + case BEHAVIOR_REJECT: + *aPermission = false; + break; + + case BEHAVIOR_NOFOREIGN: + // Third party checking + + // Need a requesting uri for third party checks to work. + if (!aFirstURI) + return NS_OK; + + bool trustedSource = false; + rv = aFirstURI->SchemeIs("chrome", &trustedSource); + NS_ENSURE_SUCCESS(rv,rv); + if (!trustedSource) { + rv = aFirstURI->SchemeIs("resource", &trustedSource); + NS_ENSURE_SUCCESS(rv,rv); + } + if (trustedSource) + return NS_OK; + + // compare tails of names checking to see if they have a common domain + // we do this by comparing the tails of both names where each tail + // includes at least one dot + + // A more generic method somewhere would be nice + + nsAutoCString currentHost; + rv = aCurrentURI->GetAsciiHost(currentHost); + NS_ENSURE_SUCCESS(rv, rv); + + // Search for two dots, starting at the end. + // If there are no two dots found, ++dot will turn to zero, + // that will return the entire string. + int32_t dot = currentHost.RFindChar('.'); + dot = currentHost.RFindChar('.', dot-1); + ++dot; + + // Get the domain, ie the last part of the host (www.domain.com -> domain.com) + // This will break on co.uk + const nsCSubstring &tail = + Substring(currentHost, dot, currentHost.Length() - dot); + + nsAutoCString firstHost; + rv = aFirstURI->GetAsciiHost(firstHost); + NS_ENSURE_SUCCESS(rv, rv); + + // If the tail is longer then the whole firstHost, it will never match + if (firstHost.Length() < tail.Length()) { + *aPermission = false; + return NS_OK; + } + + // Get the last part of the firstUri with the same length as |tail| + const nsCSubstring &firstTail = + Substring(firstHost, firstHost.Length() - tail.Length(), tail.Length()); + + // Check that both tails are the same, and that just before the tail in + // |firstUri| there is a dot. That means both url are in the same domain + if ((firstHost.Length() > tail.Length() && + firstHost.CharAt(firstHost.Length() - tail.Length() - 1) != '.') || + !tail.Equals(firstTail)) { + *aPermission = false; + } + break; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsContentBlocker::Observe(nsISupports *aSubject, + const char *aTopic, + const char16_t *aData) +{ + NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic), + "unexpected topic - we only deal with pref changes!"); + + if (mPrefBranchInternal) + PrefChanged(mPrefBranchInternal, NS_LossyConvertUTF16toASCII(aData).get()); + return NS_OK; +} diff --git a/extensions/permissions/nsContentBlocker.h b/extensions/permissions/nsContentBlocker.h new file mode 100644 index 000000000..880300952 --- /dev/null +++ b/extensions/permissions/nsContentBlocker.h @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef nsContentBlocker_h__ +#define nsContentBlocker_h__ + +#include "nsIContentPolicy.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" +#include "nsIPermissionManager.h" +#include "nsIPrefBranch.h" +#include "mozilla/Attributes.h" + +class nsIPrefBranch; + +//////////////////////////////////////////////////////////////////////////////// + +class nsContentBlocker final : public nsIContentPolicy, + public nsIObserver, + public nsSupportsWeakReference +{ +public: + + // nsISupports + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTPOLICY + NS_DECL_NSIOBSERVER + + nsContentBlocker(); + nsresult Init(); + +private: + ~nsContentBlocker() {} + + void PrefChanged(nsIPrefBranch *, const char *); + nsresult TestPermission(nsIURI *aCurrentURI, + nsIURI *aFirstURI, + int32_t aContentType, + bool *aPermission, + bool *aFromPrefs); + + nsCOMPtr<nsIPermissionManager> mPermissionManager; + nsCOMPtr<nsIPrefBranch> mPrefBranchInternal; + static uint8_t mBehaviorPref[]; +}; + +#define NS_CONTENTBLOCKER_CID \ +{ 0x4ca6b67b, 0x5cc7, 0x4e71, \ + { 0xa9, 0x8a, 0x97, 0xaf, 0x1c, 0x13, 0x48, 0x62 } } + +#define NS_CONTENTBLOCKER_CONTRACTID "@mozilla.org/permissions/contentblocker;1" + +#endif /* nsContentBlocker_h__ */ diff --git a/extensions/permissions/nsModuleFactory.cpp b/extensions/permissions/nsModuleFactory.cpp new file mode 100644 index 000000000..a8917c463 --- /dev/null +++ b/extensions/permissions/nsModuleFactory.cpp @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ModuleUtils.h" +#include "nsIServiceManager.h" +#include "nsContentBlocker.h" +#include "nsXPIDLString.h" + +// Define the constructor function for the objects +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsContentBlocker, Init) + +NS_DEFINE_NAMED_CID(NS_CONTENTBLOCKER_CID); + +static const mozilla::Module::CIDEntry kPermissionsCIDs[] = { + { &kNS_CONTENTBLOCKER_CID, false, nullptr, nsContentBlockerConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kPermissionsContracts[] = { + { NS_CONTENTBLOCKER_CONTRACTID, &kNS_CONTENTBLOCKER_CID }, + { nullptr } +}; + +static const mozilla::Module::CategoryEntry kPermissionsCategories[] = { + { "content-policy", NS_CONTENTBLOCKER_CONTRACTID, NS_CONTENTBLOCKER_CONTRACTID }, + { nullptr } +}; + +static const mozilla::Module kPermissionsModule = { + mozilla::Module::kVersion, + kPermissionsCIDs, + kPermissionsContracts, + kPermissionsCategories +}; + +NSMODULE_DEFN(nsPermissionsModule) = &kPermissionsModule; |