Bug 1050049 - Implement bindToUntrustedContent attribute restriction. r=smaug
authorBobby Holley <bobbyholley@gmail.com>
Mon, 11 Aug 2014 20:06:55 -0700
changeset 198998 1358c1ad00b483a7d6815a5c516be07b2107cc4e
parent 198997 ce94fbaec83c207f00d47b22c114c21e55174bca
child 198999 9648b3e555dbcca68c72588d76d42c6615525661
push id27293
push useremorley@mozilla.com
push dateTue, 12 Aug 2014 14:29:39 +0000
treeherdermozilla-central@ee1ad12a3939 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1050049
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1050049 - Implement bindToUntrustedContent attribute restriction. r=smaug
content/base/src/nsGkAtomList.h
dom/xbl/nsXBLPrototypeBinding.cpp
dom/xbl/nsXBLPrototypeBinding.h
dom/xbl/nsXBLService.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -127,16 +127,17 @@ GK_ATOM(before, "before")
 GK_ATOM(before_end, "before_end")
 GK_ATOM(before_start, "before_start")
 GK_ATOM(below, "below")
 GK_ATOM(bgcolor, "bgcolor")
 GK_ATOM(bgsound, "bgsound")
 GK_ATOM(big, "big")
 GK_ATOM(binding, "binding")
 GK_ATOM(bindings, "bindings")
+GK_ATOM(bindToUntrustedContent, "bindToUntrustedContent")
 GK_ATOM(blankrow, "blankrow")
 GK_ATOM(block, "block")
 GK_ATOM(blockquote, "blockquote")
 GK_ATOM(blur, "blur")
 GK_ATOM(body, "body")
 GK_ATOM(boolean, "boolean")
 GK_ATOM(border, "border")
 GK_ATOM(bordercolor, "bordercolor")
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -95,16 +95,17 @@ protected:
 // Constructors/Destructors
 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
 : mImplementation(nullptr),
   mBaseBinding(nullptr),
   mInheritStyle(true),
   mCheckedBaseProto(false),
   mKeyHandlersRegistered(false),
   mChromeOnlyContent(false),
+  mBindToUntrustedContent(false),
   mResources(nullptr),
   mBaseNameSpaceID(kNameSpaceID_None)
 {
   MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
 }
 
 nsresult
 nsXBLPrototypeBinding::Init(const nsACString& aID,
@@ -208,16 +209,20 @@ nsXBLPrototypeBinding::SetBindingElement
   mBinding = aElement;
   if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle,
                             nsGkAtoms::_false, eCaseMatters))
     mInheritStyle = false;
 
   mChromeOnlyContent = mBinding->AttrValueIs(kNameSpaceID_None,
                                              nsGkAtoms::chromeOnlyContent,
                                              nsGkAtoms::_true, eCaseMatters);
+
+  mBindToUntrustedContent = mBinding->AttrValueIs(kNameSpaceID_None,
+                                                  nsGkAtoms::bindToUntrustedContent,
+                                                  nsGkAtoms::_true, eCaseMatters);
 }
 
 bool
 nsXBLPrototypeBinding::GetAllowScripts() const
 {
   return mXBLDocInfoWeak->GetScriptAccess();
 }
 
--- a/dom/xbl/nsXBLPrototypeBinding.h
+++ b/dom/xbl/nsXBLPrototypeBinding.h
@@ -252,16 +252,17 @@ public:
    */
   nsIContent* GetImmediateChild(nsIAtom* aTag);
   nsIContent* LocateInstance(nsIContent* aBoundElt,
                              nsIContent* aTemplRoot,
                              nsIContent* aCopyRoot,
                              nsIContent* aTemplChild);
 
   bool ChromeOnlyContent() { return mChromeOnlyContent; }
+  bool BindToUntrustedContent() { return mBindToUntrustedContent; }
 
   typedef nsClassHashtable<nsISupportsHashKey, nsXBLAttributeEntry> InnerAttributeTable;
 
 protected:
   // Ensure that mAttributeTable has been created.
   void EnsureAttributeTable();
   // Ad an entry to the attribute table
   void AddToAttributeTable(int32_t aSourceNamespaceID, nsIAtom* aSourceTag,
@@ -286,16 +287,17 @@ protected:
   nsXBLProtoImpl* mImplementation; // Our prototype implementation (includes methods, properties, fields,
                                    // the constructor, and the destructor).
 
   nsXBLPrototypeBinding* mBaseBinding; // Weak.  The docinfo will own our base binding.
   bool mInheritStyle;
   bool mCheckedBaseProto;
   bool mKeyHandlersRegistered;
   bool mChromeOnlyContent;
+  bool mBindToUntrustedContent;
 
   nsAutoPtr<nsXBLPrototypeResources> mResources; // If we have any resources, this will be non-null.
 
   nsXBLDocumentInfo* mXBLDocInfoWeak; // A pointer back to our doc info.  Weak, since it owns us.
 
   // A table for attribute containers. Namespace IDs are used as
   // keys in the table. Containers are InnerAttributeTables.
   // This table is used to efficiently handle attribute changes.
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -639,16 +639,63 @@ nsXBLService::GetBinding(nsIContent* aBo
                          bool* aIsReady, nsXBLBinding** aResult)
 {
   // More than 6 binding URIs are rare, see bug 55070 comment 18.
   nsAutoTArray<nsIURI*, 6> uris;
   return GetBinding(aBoundElement, aURI, aPeekOnly, aOriginPrincipal, aIsReady,
                     aResult, uris);
 }
 
+static bool
+MayBindToContent(nsXBLPrototypeBinding* aProtoBinding, nsIContent* aBoundElement,
+                 nsIURI* aURI)
+{
+  // If this binding explicitly allows untrusted content, we're done.
+  if (aProtoBinding->BindToUntrustedContent()) {
+    return true;
+  }
+
+  // We let XUL content and content in XUL documents through, since XUL is
+  // restricted anyway and we want to minimize remote XUL breakage.
+  if (aBoundElement->IsXUL() || aBoundElement->OwnerDoc()->IsXUL()) {
+    return true;
+  }
+
+  // Similarly, we make an exception for anonymous content (which
+  // lives in the XBL scope), because it's already protected from content,
+  // and tends to use a lot of bindings that we wouldn't otherwise need to
+  // whitelist.
+  if (aBoundElement->IsInAnonymousSubtree()) {
+    return true;
+  }
+
+  // Allow if the bound content subsumes the binding.
+  nsCOMPtr<nsIDocument> bindingDoc = aProtoBinding->XBLDocumentInfo()->GetDocument();
+  NS_ENSURE_TRUE(bindingDoc, false);
+  if (aBoundElement->NodePrincipal()->Subsumes(bindingDoc->NodePrincipal())) {
+    return true;
+  }
+
+  // One last special case: we need to watch out for in-document data: URI
+  // bindings from remote-XUL-whitelisted domains (especially tests), because
+  // they end up with a null principal (rather than inheriting the document's
+  // principal), which causes them to fail the check above.
+  if (nsContentUtils::AllowXULXBLForPrincipal(aBoundElement->NodePrincipal())) {
+    bool isDataURI = false;
+    nsresult rv = aURI->SchemeIs("data", &isDataURI);
+    NS_ENSURE_SUCCESS(rv, false);
+    if (isDataURI) {
+      return true;
+    }
+  }
+
+  // Disallow.
+  return false;
+}
+
 nsresult
 nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
                          bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
                          bool* aIsReady, nsXBLBinding** aResult,
                          nsTArray<nsIURI*>& aDontExtendURIs)
 {
   NS_ASSERTION(aPeekOnly || aResult,
                "Must have non-null out param if not just peeking to see "
@@ -686,16 +733,31 @@ nsXBLService::GetBinding(nsIContent* aBo
     message += uriSpec;
     message += " in document ";
     message += doc;
     NS_WARNING(message.get());
 #endif
     return NS_ERROR_FAILURE;
   }
 
+  // If the binding isn't whitelisted, refuse to apply it to content that
+  // doesn't subsume it (modulo a few exceptions).
+  if (!MayBindToContent(protoBinding, aBoundElement, aURI)) {
+#ifdef DEBUG
+    nsAutoCString uriSpec;
+    aURI->GetSpec(uriSpec);
+    nsAutoCString message("Permission denied to apply binding ");
+    message += uriSpec;
+    message += " to unprivileged content. Set bindToUntrustedContent=true on "
+               "the binding to override this restriction.";
+    NS_WARNING(message.get());
+#endif
+   return NS_ERROR_FAILURE;
+  }
+
   NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
                  NS_ERROR_OUT_OF_MEMORY);
   nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
   if (altBindingURI) {
     NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
                    NS_ERROR_OUT_OF_MEMORY);
   }