diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 0cea77e05bf4..2aa471d9a7d4 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -132,6 +132,7 @@ 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") diff --git a/dom/xbl/nsXBLPrototypeBinding.cpp b/dom/xbl/nsXBLPrototypeBinding.cpp index 7b7781b7bad8..f6584e05fde1 100644 --- a/dom/xbl/nsXBLPrototypeBinding.cpp +++ b/dom/xbl/nsXBLPrototypeBinding.cpp @@ -100,6 +100,7 @@ nsXBLPrototypeBinding::nsXBLPrototypeBinding() mCheckedBaseProto(false), mKeyHandlersRegistered(false), mChromeOnlyContent(false), + mBindToUntrustedContent(false), mResources(nullptr), mBaseNameSpaceID(kNameSpaceID_None) { @@ -213,6 +214,10 @@ nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement) mChromeOnlyContent = mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::chromeOnlyContent, nsGkAtoms::_true, eCaseMatters); + + mBindToUntrustedContent = mBinding->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::bindToUntrustedContent, + nsGkAtoms::_true, eCaseMatters); } bool diff --git a/dom/xbl/nsXBLPrototypeBinding.h b/dom/xbl/nsXBLPrototypeBinding.h index eb9da03d5593..02f4bd0182a2 100644 --- a/dom/xbl/nsXBLPrototypeBinding.h +++ b/dom/xbl/nsXBLPrototypeBinding.h @@ -257,6 +257,7 @@ public: nsIContent* aTemplChild); bool ChromeOnlyContent() { return mChromeOnlyContent; } + bool BindToUntrustedContent() { return mBindToUntrustedContent; } typedef nsClassHashtable InnerAttributeTable; @@ -291,6 +292,7 @@ protected: bool mCheckedBaseProto; bool mKeyHandlersRegistered; bool mChromeOnlyContent; + bool mBindToUntrustedContent; nsAutoPtr mResources; // If we have any resources, this will be non-null. diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index 94fd7d1d03bf..a3f69bf1d64b 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -644,6 +644,53 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI, 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 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, @@ -691,6 +738,21 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI, 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 altBindingURI = protoBinding->AlternateBindingURI();