Fixing bug 397791. Prevent document principals from ever changing, and make us not use XOWs for same origin document objects. r=jonas@sickin.cc, sr=bzbarsky@mit.edu

This commit is contained in:
jst@mozilla.org 2008-01-25 13:49:11 -08:00
parent 083432e2e8
commit 275f61abc4
4 changed files with 89 additions and 136 deletions

View File

@ -2083,17 +2083,39 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
}
nsCOMPtr<nsIPrincipal> callerPrincipal;
nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
secMan->GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
if (!callerPrincipal) {
// If we're called from C++ we can't do a document.open w/o
// changing the principal of the document to something like
// about:blank (as that's the only sane thing to do when we don't
// know the origin of this call), and since we can't change the
// principals of a document for security reasons we'll have to
// refuse to go ahead with this call.
return NS_ERROR_DOM_SECURITY_ERR;
}
// We're called from script. Make sure the script is from the same
// origin, not just that the caller can access the document. This is
// needed to keep document principals from ever changing, which is
// needed because of the way we use our XOW code, and is a sane
// thing to do anyways.
PRBool equals = PR_FALSE;
if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
!equals) {
return NS_ERROR_DOM_SECURITY_ERR;
}
// The URI for the document after this call. Get it from the calling
// principal (if available), or set it to "about:blank" if no
// principal is reachable.
nsCOMPtr<nsIURI> uri;
callerPrincipal->GetURI(getter_AddRefs(uri));
if (callerPrincipal) {
callerPrincipal->GetURI(getter_AddRefs(uri));
}
if (!uri) {
rv = NS_NewURI(getter_AddRefs(uri),
NS_LITERAL_CSTRING("about:blank"));
@ -2151,15 +2173,6 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
// Remember the old scope in case the call to SetNewDocument changes it.
nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
// If callerPrincipal doesn't match our principal. make sure that
// SetNewDocument gives us a new inner window and clears our scope.
PRBool samePrincipal;
if (!callerPrincipal ||
NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &samePrincipal)) ||
!samePrincipal) {
SetIsInitialDocument(PR_FALSE);
}
rv = window->SetNewDocument(this, nsnull, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -193,14 +193,8 @@ nsXMLDocument::~nsXMLDocument()
mLoopingForSyncLoad = PR_FALSE;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLDocument, nsDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
// QueryInterface implementation for nsXMLDocument
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXMLDocument)
NS_INTERFACE_TABLE_HEAD(nsXMLDocument)
NS_INTERFACE_TABLE_INHERITED3(nsXMLDocument,
nsIInterfaceRequestor,
nsIChannelEventSink,
@ -227,8 +221,6 @@ void
nsXMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
{
nsDocument::Reset(aChannel, aLoadGroup);
mScriptContext = nsnull;
}
void
@ -240,7 +232,7 @@ nsXMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
mChannel->Cancel(NS_BINDING_ABORTED);
mChannelIsPending = nsnull;
}
nsDocument::ResetToURI(aURI, aLoadGroup, aPrincipal);
}
@ -289,27 +281,19 @@ nsXMLDocument::OnChannelRedirect(nsIChannel *aOldChannel,
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
if (mScriptContext && !mCrossSiteAccessEnabled) {
nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", & rv));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIURI> oldURI;
rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
NS_ENSURE_SUCCESS(rv, rv);
JSContext *cx = (JSContext *)mScriptContext->GetNativeContext();
if (!cx)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIURI> newURI;
rv = aNewChannel->GetURI(getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, rv);
stack->Push(cx);
rv = nsContentUtils::GetSecurityManager()->
CheckSameOriginURI(oldURI, newURI, PR_TRUE);
rv = secMan->CheckSameOrigin(nsnull, newLocation);
stack->Pop(&cx);
if (NS_FAILED(rv)) {
// The security manager set a pending exception. Since we're
// running under the event loop, we need to report it.
::JS_ReportPendingException(cx);
return rv;
}
if (NS_FAILED(rv)) {
return rv;
}
// XXXbz Shouldn't we look at the owner on the new channel at some point?
@ -360,64 +344,21 @@ nsXMLDocument::SetAsync(PRBool aAsync)
return NS_OK;
}
nsresult
nsXMLDocument::GetLoadGroup(nsILoadGroup **aLoadGroup)
{
NS_ENSURE_ARG_POINTER(aLoadGroup);
*aLoadGroup = nsnull;
if (mScriptContext) {
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(mScriptContext->GetGlobalObject());
if (window) {
nsCOMPtr<nsIDOMDocument> domdoc;
window->GetDocument(getter_AddRefs(domdoc));
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
if (doc) {
*aLoadGroup = doc->GetDocumentLoadGroup().get(); // already_AddRefed
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = PR_FALSE;
nsIScriptContext *callingContext = nsnull;
nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (stack) {
JSContext *cx;
if (NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
callingContext = nsJSUtils::GetDynamicScriptContext(cx);
}
}
nsCOMPtr<nsIDocument> callingDoc =
do_QueryInterface(nsContentUtils::GetDocumentFromContext());
nsIURI *baseURI = mDocumentURI;
nsCAutoString charset;
if (callingContext) {
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(callingContext->GetGlobalObject());
if (window) {
nsCOMPtr<nsIDOMDocument> dom_doc;
window->GetDocument(getter_AddRefs(dom_doc));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(dom_doc));
if (doc) {
baseURI = doc->GetBaseURI();
charset = doc->GetDocumentCharacterSet();
}
}
if (callingDoc) {
baseURI = callingDoc->GetBaseURI();
charset = callingDoc->GetDocumentCharacterSet();
}
// Create a new URI
@ -427,6 +368,41 @@ nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn)
return rv;
}
nsCOMPtr<nsIURI> codebase;
NodePrincipal()->GetURI(getter_AddRefs(codebase));
// Get security manager, check to see whether the current document
// is allowed to load this URI. It's important to use the current
// document's principal for this check so that we don't end up in a
// case where code with elevated privileges is calling us and
// changing the principal of this document.
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
if (codebase) {
rv = secMan->CheckSameOriginURI(codebase, uri, PR_FALSE);
if (NS_FAILED(rv)) {
return rv;
}
} else {
// We're called from chrome, check to make sure the URI we're
// about to load is also chrome.
PRBool isChrome = PR_FALSE;
if (NS_FAILED(uri->SchemeIs("chrome", &isChrome)) || !isChrome) {
return NS_ERROR_DOM_SECURITY_ERR;
}
}
rv = secMan->CheckConnect(nsnull, uri, "XMLDocument", "load");
if (NS_FAILED(rv)) {
// We need to return success here so that JS will get a proper
// exception thrown later. Native calls should always result in
// CheckConnect() succeeding, but in case JS calls C++ which calls
// this code the exception might be lost.
return NS_OK;
}
// Partial Reset, need to restore principal for security reasons and
// event listener manager so that load listeners etc. will
// remain. This should be done before the security check is done to
@ -438,48 +414,20 @@ nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn)
nsCOMPtr<nsIEventListenerManager> elm(mListenerManager);
mListenerManager = nsnull;
ResetToURI(uri, nsnull, principal);
mListenerManager = elm;
// Get security manager, check to see if we're allowed to load this URI
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
rv = secMan->CheckConnect(nsnull, uri, "XMLDocument", "load");
if (NS_FAILED(rv)) {
// We need to return success here so that JS will get a proper
// exception thrown later. Native calls should always result in
// CheckConnect() succeeding, but in case JS calls C++ which calls
// this code the exception might be lost.
return NS_OK;
}
// Store script context, if any, in case we encounter redirect
// (because we need it there)
mScriptContext = callingContext;
// Find out if UniversalBrowserRead privileges are enabled - we will
// need this in case of a redirect
PRBool crossSiteAccessEnabled;
rv = secMan->IsCapabilityEnabled("UniversalBrowserRead",
&crossSiteAccessEnabled);
if (NS_FAILED(rv)) {
return rv;
}
mCrossSiteAccessEnabled = crossSiteAccessEnabled;
// Create a channel
// When we are called from JS we can find the load group for the page,
// and add ourselves to it. This way any pending requests
// will be automatically aborted if the user leaves the page.
nsCOMPtr<nsILoadGroup> loadGroup;
GetLoadGroup(getter_AddRefs(loadGroup));
if (callingDoc) {
loadGroup = callingDoc->GetDocumentLoadGroup();
}
ResetToURI(uri, loadGroup, principal);
mListenerManager = elm;
// Create a channel
nsCOMPtr<nsIChannel> channel;
// nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,

View File

@ -95,14 +95,8 @@ public:
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsXMLDocument, nsDocument)
void SetLoadedAsData(PRBool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
protected:
virtual nsresult GetLoadGroup(nsILoadGroup **aLoadGroup);
nsCOMPtr<nsIScriptContext> mScriptContext;
// mChannelIsPending indicates whether we're currently asynchronously loading
// data from mChannel (via document.load() or normal load). It's set to true
// when we first find out about the channel (StartDocumentLoad) and set to
@ -110,7 +104,6 @@ protected:
// mChannel is also cancelled. Note that if this member is true, mChannel
// cannot be null.
PRPackedBool mChannelIsPending;
PRPackedBool mCrossSiteAccessEnabled;
PRPackedBool mLoadedAsInteractiveData;
PRPackedBool mAsync;
PRPackedBool mLoopingForSyncLoad;

View File

@ -87,7 +87,6 @@ XPC_XOW_ClassNeedsXOW(const char *name)
// TODO Make a perfect hash of these and use that?
return !strcmp(name, "Window") ||
!strcmp(name, "Location") ||
!strcmp(name, "HTMLDocument") ||
!strcmp(name, "HTMLIFrameElement") ||
!strcmp(name, "HTMLFrameElement");
}