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 21:49:11 +00:00
Родитель 465dc3a48b
Коммит 2a31e0feae
4 изменённых файлов: 89 добавлений и 136 удалений

Просмотреть файл

@ -2083,17 +2083,39 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
} }
nsCOMPtr<nsIPrincipal> callerPrincipal; nsCOMPtr<nsIPrincipal> callerPrincipal;
nsContentUtils::GetSecurityManager()-> nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
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 // 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 (if available), or set it to "about:blank" if no
// principal is reachable. // principal is reachable.
nsCOMPtr<nsIURI> uri; nsCOMPtr<nsIURI> uri;
if (callerPrincipal) {
callerPrincipal->GetURI(getter_AddRefs(uri)); callerPrincipal->GetURI(getter_AddRefs(uri));
}
if (!uri) { if (!uri) {
rv = NS_NewURI(getter_AddRefs(uri), rv = NS_NewURI(getter_AddRefs(uri),
NS_LITERAL_CSTRING("about:blank")); 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. // Remember the old scope in case the call to SetNewDocument changes it.
nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject)); 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); rv = window->SetNewDocument(this, nsnull, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);

Просмотреть файл

@ -193,14 +193,8 @@ nsXMLDocument::~nsXMLDocument()
mLoopingForSyncLoad = PR_FALSE; 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 // QueryInterface implementation for nsXMLDocument
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXMLDocument) NS_INTERFACE_TABLE_HEAD(nsXMLDocument)
NS_INTERFACE_TABLE_INHERITED3(nsXMLDocument, NS_INTERFACE_TABLE_INHERITED3(nsXMLDocument,
nsIInterfaceRequestor, nsIInterfaceRequestor,
nsIChannelEventSink, nsIChannelEventSink,
@ -227,8 +221,6 @@ void
nsXMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) nsXMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
{ {
nsDocument::Reset(aChannel, aLoadGroup); nsDocument::Reset(aChannel, aLoadGroup);
mScriptContext = nsnull;
} }
void void
@ -289,28 +281,20 @@ nsXMLDocument::OnChannelRedirect(nsIChannel *aOldChannel,
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
if (mScriptContext && !mCrossSiteAccessEnabled) { nsCOMPtr<nsIURI> oldURI;
nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", & rv)); rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
if (NS_FAILED(rv)) NS_ENSURE_SUCCESS(rv, rv);
return rv;
JSContext *cx = (JSContext *)mScriptContext->GetNativeContext(); nsCOMPtr<nsIURI> newURI;
if (!cx) rv = aNewChannel->GetURI(getter_AddRefs(newURI));
return NS_ERROR_UNEXPECTED; 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)) { 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; return rv;
} }
}
// XXXbz Shouldn't we look at the owner on the new channel at some point? // XXXbz Shouldn't we look at the owner on the new channel at some point?
// It's not gonna be right here, but eventually it will.... // It's not gonna be right here, but eventually it will....
@ -360,64 +344,21 @@ nsXMLDocument::SetAsync(PRBool aAsync)
return NS_OK; 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 NS_IMETHODIMP
nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn) nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn)
{ {
NS_ENSURE_ARG_POINTER(aReturn); NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = PR_FALSE; *aReturn = PR_FALSE;
nsIScriptContext *callingContext = nsnull; nsCOMPtr<nsIDocument> callingDoc =
do_QueryInterface(nsContentUtils::GetDocumentFromContext());
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);
}
}
nsIURI *baseURI = mDocumentURI; nsIURI *baseURI = mDocumentURI;
nsCAutoString charset; nsCAutoString charset;
if (callingContext) { if (callingDoc) {
nsCOMPtr<nsIDOMWindow> window = baseURI = callingDoc->GetBaseURI();
do_QueryInterface(callingContext->GetGlobalObject()); charset = callingDoc->GetDocumentCharacterSet();
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();
}
}
} }
// Create a new URI // Create a new URI
@ -427,6 +368,41 @@ nsXMLDocument::Load(const nsAString& aUrl, PRBool *aReturn)
return rv; 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 // Partial Reset, need to restore principal for security reasons and
// event listener manager so that load listeners etc. will // event listener manager so that load listeners etc. will
// remain. This should be done before the security check is done to // 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); nsCOMPtr<nsIEventListenerManager> elm(mListenerManager);
mListenerManager = nsnull; 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, // 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 // and add ourselves to it. This way any pending requests
// will be automatically aborted if the user leaves the page. // will be automatically aborted if the user leaves the page.
nsCOMPtr<nsILoadGroup> loadGroup; nsCOMPtr<nsILoadGroup> loadGroup;
GetLoadGroup(getter_AddRefs(loadGroup)); if (callingDoc) {
loadGroup = callingDoc->GetDocumentLoadGroup();
}
ResetToURI(uri, loadGroup, principal);
mListenerManager = elm;
// Create a channel
nsCOMPtr<nsIChannel> channel; nsCOMPtr<nsIChannel> channel;
// nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,

Просмотреть файл

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

Просмотреть файл

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