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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerssmaug
bugs1050049
milestone34.0a1
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);
   }