Remove special-casing of about:blank for security purposes; give about:blank

pages the principal of whoever is responsible for loading them, when possible.
Bug 332182, r=mrbkap, sr=jst
This commit is contained in:
bzbarsky%mit.edu 2006-08-15 17:31:16 +00:00
Родитель 417c678f9a
Коммит e9379f3679
13 изменённых файлов: 364 добавлений и 271 удалений

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

@ -270,15 +270,10 @@ nsPrincipal::Equals(nsIPrincipal *aOther, PRBool *aResult)
}
// Codebases are equal if they have the same origin.
nsIURI *origin = mDomain ? mDomain : mCodebase;
nsCOMPtr<nsIURI> otherOrigin;
aOther->GetDomain(getter_AddRefs(otherOrigin));
if (!otherOrigin) {
aOther->GetURI(getter_AddRefs(otherOrigin));
}
return nsScriptSecurityManager::GetScriptSecurityManager()
->SecurityCompareURIs(origin, otherOrigin, aResult);
*aResult =
NS_SUCCEEDED(nsScriptSecurityManager::GetScriptSecurityManager()
->CheckSameOriginPrincipal(this, aOther));
return NS_OK;
}
*aResult = PR_TRUE;
@ -288,32 +283,6 @@ nsPrincipal::Equals(nsIPrincipal *aOther, PRBool *aResult)
NS_IMETHODIMP
nsPrincipal::Subsumes(nsIPrincipal *aOther, PRBool *aResult)
{
// First, check if aOther is an about:blank principal. If it is, then we can
// subsume it.
nsCOMPtr<nsIURI> otherOrigin;
aOther->GetURI(getter_AddRefs(otherOrigin));
if (otherOrigin) {
PRBool isAbout = PR_FALSE;
if (NS_SUCCEEDED(otherOrigin->SchemeIs("about", &isAbout)) && isAbout) {
nsCAutoString str;
otherOrigin->GetSpec(str);
// Note: about:blank principals do not necessarily subsume about:blank
// principals (unless aOther == this, which is checked in the Equals call
// below).
if (str.Equals("about:blank")) {
PRBool isEqual = PR_FALSE;
if (NS_SUCCEEDED(otherOrigin->Equals(mCodebase, &isEqual)) && !isEqual) {
*aResult = PR_TRUE;
return NS_OK;
}
}
}
}
return Equals(aOther, aResult);
}

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

@ -264,21 +264,12 @@ nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
return NS_OK;
}
if (!aTargetURI)
if (!aTargetURI || !aSourceURI)
{
// return false
return NS_OK;
}
if (!aSourceURI)
{
// Throw. If we don't, we might in some cases consider a system
// principal as same-origin with an about:blank (see
// CheckSameOriginPrincipalInternal). The fact that these methods are
// asymmetric is highly unfortunate.
return NS_ERROR_NOT_AVAILABLE;
}
// If either URI is a nested URI, get the base URI
nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(aSourceURI);
@ -852,8 +843,14 @@ nsScriptSecurityManager::CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject
if (aSubject == aObject)
return NS_OK;
// These booleans are only used when !aIsCheckConnect. Default
// them to false, and change if that turns out wrong.
PRBool subjectSetDomain = PR_FALSE;
PRBool objectSetDomain = PR_FALSE;
nsCOMPtr<nsIURI> subjectURI;
nsCOMPtr<nsIURI> objectURI;
if (aIsCheckConnect)
{
// Don't use domain for CheckConnect calls, since that's called for
@ -864,12 +861,18 @@ nsScriptSecurityManager::CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject
else
{
aSubject->GetDomain(getter_AddRefs(subjectURI));
if (!subjectURI)
if (!subjectURI) {
aSubject->GetURI(getter_AddRefs(subjectURI));
} else {
subjectSetDomain = PR_TRUE;
}
aObject->GetDomain(getter_AddRefs(objectURI));
if (!objectURI)
if (!objectURI) {
aObject->GetURI(getter_AddRefs(objectURI));
} else {
objectSetDomain = PR_TRUE;
}
}
PRBool isSameOrigin = PR_FALSE;
@ -888,37 +891,8 @@ nsScriptSecurityManager::CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject
if (aIsCheckConnect)
return NS_OK;
nsCOMPtr<nsIURI> subjectDomain;
aSubject->GetDomain(getter_AddRefs(subjectDomain));
nsCOMPtr<nsIURI> objectDomain;
aObject->GetDomain(getter_AddRefs(objectDomain));
// If both or neither explicitly set their domain, allow the access
if (!subjectDomain == !objectDomain)
return NS_OK;
}
// Allow access to about:blank, except from null principals (which
// never have access to anything but themselves). If SchemeIs
// fails, just deny access -- better safe than sorry.
// XXXbz when this gets removed, also remove the asymmetry between
// aSourceURI and aTargetURI in SecurityCompareURIs.
// XXXbz once this is removed, we can probably just make
// nsPrincipal::Equals call CheckSameOriginPrincipal(), which will also
// make sure it hits the domain check above. At the same time as we remove
// this we should also be able to remove the about:blank hackery in
// nsPrincipal::Subsumes.
PRBool nullSubject = PR_FALSE;
// Subject URI could be null here....
if (subjectURI) {
rv = subjectURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &nullSubject);
}
if (NS_SUCCEEDED(rv) && !nullSubject) {
nsXPIDLCString origin;
rv = aObject->GetOrigin(getter_Copies(origin));
NS_ENSURE_SUCCESS(rv, rv);
if (nsCRT::strcasecmp(origin, "moz-safe-about:blank") == 0)
if (subjectSetDomain == objectSetDomain)
return NS_OK;
}

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

@ -91,8 +91,8 @@ class nsIDocumentObserver;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
{ 0x63f0c69a, 0x255a, 0x432f, \
{ 0xa7, 0xe4, 0xaf, 0x81, 0x10, 0x56, 0x28, 0xef } }
{ 0x3a5ce97e, 0x4f41, 0x42ef, \
{ 0xa7, 0xc9, 0xcc, 0x3d, 0x4e, 0x7a, 0x3d, 0x00 } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -112,6 +112,7 @@ public:
: nsINode(nsnull),
mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
mNodeInfoManager(nsnull),
mIsInitialDocumentInWindow(PR_FALSE),
mPartID(0)
{
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
@ -286,6 +287,24 @@ public:
mBidiEnabled = aBidiEnabled;
}
/**
* Ask this document whether it's the initial document in its window.
*/
PRBool IsInitialDocument() const
{
return mIsInitialDocumentInWindow;
}
/**
* Tell this document that it's the initial document in its window. See
* comments on mIsInitialDocumentInWindow for when this should be called.
*/
void SetIsInitialDocument(PRBool aIsInitialDocument)
{
mIsInitialDocumentInWindow = aIsInitialDocument;
}
/**
* Get the bidi options for this document.
* @see nsBidiUtils.h
@ -829,7 +848,13 @@ protected:
nsPropertyTable mPropertyTable;
// True if BIDI is enabled.
PRBool mBidiEnabled;
PRPackedBool mBidiEnabled;
// True if this document is the initial document for a window. This should
// basically be true only for documents that exist in newly-opened windows or
// documents created to satisfy a GetDocument() on a window when there's no
// document in it.
PRPackedBool mIsInitialDocumentInWindow;
// The bidi options for this document. What this bitfield means is
// defined in nsBidiUtils.h

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

@ -189,6 +189,13 @@ nsFrameLoader::LoadURI(nsIURI* aURI)
// load instead of just forcing the system principal. That way if we have
// something loaded already the principal used will be that of what we
// already have loaded.
// XXX bz I'd love to nix this, but the problem is chrome calling
// setAttribute() on an iframe or browser and passing in a javascript: URI.
// We probably don't want to run that with chrome privileges... Though in
// similar circumstances, if one sets window.location.href from chrome we
// _do_ run that with chrome privileges, so maybe we should do the same
// here?
loadInfo->SetInheritOwner(PR_TRUE);
// Also, in this case we don't set a referrer, just in case.

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

@ -2020,9 +2020,22 @@ 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.
if (!callerPrincipal ||
NS_FAILED(nsContentUtils::GetSecurityManager()->
CheckSameOriginPrincipal(callerPrincipal, NodePrincipal()))) {
SetIsInitialDocument(PR_FALSE);
}
rv = window->SetNewDocument(this, nsnull, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Now make sure we're not flagged as the initial document anymore, now
// that we've had stuff done to us. From now on, if anyone tries to
// document.open() us, they get a new inner window.
SetIsInitialDocument(PR_FALSE);
nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
if (oldScope && newScope != oldScope) {
nsContentUtils::ReparentContentWrappersInScope(oldScope, newScope);
@ -2340,36 +2353,6 @@ nsHTMLDocument::ScriptWriteCommon(PRBool aNewlineTerminate)
GetCurrentNativeCallContext(getter_AddRefs(ncc));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString spec;
if (mDocumentURI) {
rv = mDocumentURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!mDocumentURI || spec.EqualsLiteral("about:blank")) {
// The current document's URI and principal are empty or "about:blank".
// By writing to this document, the script acquires responsibility for the
// document for security purposes. Thus a document.write of a script tag
// ends up producing a script with the same principals as the script
// that performed the write.
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> subject;
rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
NS_ENSURE_SUCCESS(rv, rv);
if (subject) {
nsCOMPtr<nsIURI> subjectURI;
subject->GetURI(getter_AddRefs(subjectURI));
if (subjectURI) {
mDocumentURI = subjectURI;
SetPrincipal(subject);
}
}
}
if (ncc) {
// We're called from JS, concatenate the extra arguments into
// string_buffer

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

@ -3245,21 +3245,20 @@ nsDocShell::Reload(PRUint32 aReloadFlags)
rv = LoadHistoryEntry(mLSHE, loadType);
}
else {
nsCOMPtr<nsIDOMDocument> domDoc(do_GetInterface(GetAsSupports(this)));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
nsIPrincipal* principal = nsnull;
nsAutoString contentTypeHint;
nsCOMPtr<nsIDOMWindow> window(do_GetInterface((nsIDocShell*)this));
if (window) {
nsCOMPtr<nsIDOMDocument> document;
window->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMNSDocument> doc(do_QueryInterface(document));
if (doc) {
doc->GetContentType(contentTypeHint);
}
if (doc) {
principal = doc->NodePrincipal();
doc->GetContentType(contentTypeHint);
}
rv = InternalLoad(mCurrentURI,
mReferrerURI,
nsnull, // No owner
INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document
principal,
INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
nsnull, // No window target
NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
nsnull, // No post data
@ -4919,7 +4918,35 @@ nsDocShell::EnsureContentViewer()
if (mIsBeingDestroyed)
return NS_ERROR_FAILURE;
return CreateAboutBlankContentViewer();
nsIPrincipal* principal = nsnull;
nsCOMPtr<nsPIDOMWindow> piDOMWindow(do_QueryInterface(mScriptGlobal));
if (piDOMWindow) {
principal = piDOMWindow->GetOpenerScriptPrincipal();
}
if (!principal) {
principal = GetInheritedPrincipal(PR_FALSE);
}
nsresult rv = CreateAboutBlankContentViewer();
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIDOMDocument> domDoc;
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
NS_ASSERTION(doc,
"Should have doc if CreateAboutBlankContentViewer "
"succeeded!");
doc->SetIsInitialDocument(PR_TRUE);
if (principal) {
doc->SetPrincipal(principal);
}
}
return rv;
}
NS_IMETHODIMP
@ -6328,10 +6355,23 @@ nsDocShell::InternalLoad(nsIURI * aURI,
nsCOMPtr<nsISupports> owner(aOwner);
//
// Get an owner from the current document if necessary
// Get an owner from the current document if necessary. Note that we only
// do this for URIs that inherit a security context; in particular we do
// NOT do this for about:blank. This way, random about:blank loads that
// have no owner (which basically means they were done by someone from
// chrome manually messing with our nsIWebNavigation or by C++ setting
// document.location) don't get a funky principal. If callers want
// something interesting to happen with the about:blank principal in this
// case, they should pass an owner in.
//
if (!owner && (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER))
GetCurrentDocumentOwner(getter_AddRefs(owner));
{
PRBool inherits;
if (!owner && (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
inherits) {
owner = GetInheritedPrincipal(PR_TRUE);
}
}
//
// Resolve the window target before going any further...
@ -6339,6 +6379,11 @@ nsDocShell::InternalLoad(nsIURI * aURI,
// load to it...
//
if (aWindowTarget && *aWindowTarget) {
// We've already done our owner-inheriting. Mask out that bit, so we
// don't try inheriting an owner from the target window if we came up
// with a null owner above.
aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
// Locate the target DocShell.
// This may involve creating a new toplevel window - if necessary.
//
@ -6724,42 +6769,51 @@ nsDocShell::InternalLoad(nsIURI * aURI,
return rv;
}
void
nsDocShell::GetCurrentDocumentOwner(nsISupports ** aOwner)
nsIPrincipal*
nsDocShell::GetInheritedPrincipal(PRBool aConsiderCurrentDocument)
{
*aOwner = nsnull;
nsCOMPtr<nsIDocument> document;
//-- Get the current document
if (mContentViewer) {
if (aConsiderCurrentDocument && mContentViewer) {
nsCOMPtr<nsIDocumentViewer>
docViewer(do_QueryInterface(mContentViewer));
if (!docViewer)
return;
return nsnull;
docViewer->GetDocument(getter_AddRefs(document));
}
else //-- If there's no document loaded yet, look at the parent (frameset)
{
if (!document) {
nsCOMPtr<nsIDocShellTreeItem> parentItem;
GetSameTypeParent(getter_AddRefs(parentItem));
if (!parentItem)
return;
nsCOMPtr<nsIDOMWindowInternal>
parentWindow(do_GetInterface(parentItem));
if (!parentWindow)
return;
nsCOMPtr<nsIDOMDocument> parentDomDoc;
parentWindow->GetDocument(getter_AddRefs(parentDomDoc));
if (!parentDomDoc)
return;
document = do_QueryInterface(parentDomDoc);
if (parentItem) {
nsCOMPtr<nsIDOMDocument> parentDomDoc(do_GetInterface(parentItem));
document = do_QueryInterface(parentDomDoc);
}
}
if (!document) {
if (!aConsiderCurrentDocument) {
return nsnull;
}
// Make sure we end up with _something_ as the principal no matter
// what.
EnsureContentViewer(); // If this fails, we'll just get a null
// docViewer and bail.
nsCOMPtr<nsIDocumentViewer>
docViewer(do_QueryInterface(mContentViewer));
if (!docViewer)
return nsnull;
docViewer->GetDocument(getter_AddRefs(document));
}
//-- Get the document's principal
if (document) {
*aOwner = document->NodePrincipal();
return document->NodePrincipal();
}
NS_IF_ADDREF(*aOwner);
return nsnull;
}
nsresult
@ -6927,19 +6981,22 @@ nsDocShell::DoURILoad(nsIURI * aURI,
// provide their own security context.
//
// XXX: Is seems wrong that the owner is ignored - even if one is
// supplied) unless the URI is javascript or data.
// supplied) unless the URI is javascript or data or about:blank.
// XXX: If this is ever changed, check all callers for what owners they're
// passing in. In particular, see the code and comments in LoadURI
// where we get the current document principal as the owner if called
// where we fall back on inheriting the owner if called
// from chrome. That would be very wrong if this code changed
// anything but channels that can't provide their own security context!
//
// (Currently chrome URIs set the owner when they are created!
// So setting a NULL owner would be bad!)
//
PRBool inherit;
// We expect URIInheritsSecurityContext to return success for an
// about:blank URI, so don't call IsAboutBlank() if this call fails.
rv = URIInheritsSecurityContext(aURI, &inherit);
if (NS_SUCCEEDED(rv) && inherit) {
if (NS_SUCCEEDED(rv) && (inherit || IsAboutBlank(aURI))) {
channel->SetOwner(aOwner);
}
@ -8815,9 +8872,27 @@ nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
nsresult
nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
{
// Need to add explicit check for about:blank here too, in the
// future. See bug 332182.
// Note: about:blank URIs do NOT inherit the security context from the
// current document, which is what this function tests for...
return NS_URIChainHasFlags(aURI,
nsIProtocolHandler::URI_HAS_NO_SECURITY_CONTEXT,
aResult);
}
/* static */
PRBool
nsDocShell::IsAboutBlank(nsIURI* aURI)
{
NS_PRECONDITION(aURI, "Must have URI");
// GetSpec can be expensive for some URIs, so check the scheme first.
PRBool isAbout = PR_FALSE;
if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
return PR_FALSE;
}
nsCAutoString str;
aURI->GetSpec(str);
return str.EqualsLiteral("about:blank");
}

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

@ -231,7 +231,18 @@ protected:
void SetupReferrerFromChannel(nsIChannel * aChannel);
NS_IMETHOD GetEldestPresContext(nsPresContext** aPresContext);
void GetCurrentDocumentOwner(nsISupports ** aOwner);
// Get the principal that we'll set on the channel if we're inheriting. If
// aConsiderCurrentDocument is true, we try to use the current document if
// at all possible. If that fails, we fall back on the parent document.
// If that fails too, we force creation of a content viewer and use the
// resulting principal. If aConsiderCurrentDocument is false, we just look
// at the parent.
nsIPrincipal* GetInheritedPrincipal(PRBool aConsiderCurrentDocument);
// Actually open a channel and perform a URI load. Note: whatever owner is
// passed to this function will be set on the channel. Callers who wish to
// not have an owner on the channel should just pass null.
virtual nsresult DoURILoad(nsIURI * aURI,
nsIURI * aReferrer,
PRBool aSendReferrer,
@ -462,6 +473,9 @@ protected:
// Check whether aURI should inherit our security context
static nsresult URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult);
// Check whether aURI is about:blank
static PRBool IsAboutBlank(nsIURI* aURI);
protected:
// Override the parent setter from nsDocLoader

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

@ -72,9 +72,8 @@ class nsPresContext;
struct nsTimeout;
#define NS_PIDOMWINDOW_IID \
{ /* 4602B87E-879E-49D1-96FE-C87FDD915348} */ \
0x4602b87e, 0x879e, 0x49d1, \
{ 0x96, 0xfe, 0xc8, 0x7f, 0xdd, 0x91, 0x53, 0x48 } }
{ 0xbebce53b, 0xa4ec, 0x49e5, \
{ 0x82, 0x8e, 0x23, 0x08, 0x61, 0x2b, 0x41, 0x9b } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -245,7 +244,14 @@ public:
return win->mIsHandlingResizeEvent;
}
// Tell this window who opened it. This only has an effect if there is
// either no document currently in the window or if the document is the
// original document this window came with (an about:blank document either
// preloaded into it when it was created, or created by
// CreateAboutBlankContentViewer()).
virtual void SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal) = 0;
// Ask this window who opened it.
virtual nsIPrincipal* GetOpenerScriptPrincipal() = 0;
virtual PopupControlState PushPopupControlState(PopupControlState aState,
PRBool aForce) const = 0;

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

@ -305,6 +305,22 @@ static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static const char kCryptoContractID[] = NS_CRYPTO_CONTRACTID;
static const char kPkcs11ContractID[] = NS_PKCS11_CONTRACTID;
static PRBool
IsAboutBlank(nsIURI* aURI)
{
NS_PRECONDITION(aURI, "Must have URI");
// GetSpec can be expensive for some URIs, so check the scheme first.
PRBool isAbout = PR_FALSE;
if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
return PR_FALSE;
}
nsCAutoString str;
aURI->GetSpec(str);
return str.EqualsLiteral("about:blank");
}
/**
* An indirect observer object that means we don't have to implement nsIObserver
* on nsGlobalWindow, where any script could see it.
@ -779,45 +795,35 @@ PRBool
nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
{
// We reuse the inner window when:
// a. We are currently at about:blank
// a. We are currently at our original document.
// b. At least one of the following conditions are true:
// -- We are not currently a content window (i.e., we're currently a chrome
// window).
// -- The new document is the same as the old document. This means that we're
// getting called from document.open().
// -- The new URI has the same origin as the script opener uri for our current
// window.
// -- The new document has the same origin as what we have loaded right now.
if (!mDoc || !aNewDocument) {
return PR_FALSE;
}
nsIURI* curURI = mDoc->GetDocumentURI();
if (!curURI) {
return PR_FALSE;
}
PRBool isAbout;
if (NS_FAILED(curURI->SchemeIs("about", &isAbout)) || !isAbout) {
return PR_FALSE;
}
nsCAutoString uri;
curURI->GetSpec(uri);
if (!uri.EqualsLiteral("about:blank")) {
if (!mDoc->IsInitialDocument()) {
return PR_FALSE;
}
// Great, we're an about:blank document, check for one of the other
NS_ASSERTION(IsAboutBlank(mDoc->GetDocumentURI()),
"How'd this happen?");
// Great, we're the original document, check for one of the other
// conditions.
if (mDoc == aNewDocument) {
// aClearScopeHint is false.
return PR_TRUE;
}
if (mOpenerScriptPrincipal && nsContentUtils::GetSecurityManager() &&
if (nsContentUtils::GetSecurityManager() &&
NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->
CheckSameOriginPrincipal(mOpenerScriptPrincipal,
CheckSameOriginPrincipal(mDoc->NodePrincipal(),
aNewDocument->NodePrincipal()))) {
// The origin is the same.
return PR_TRUE;
@ -842,9 +848,39 @@ nsGlobalWindow::SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal)
{
FORWARD_TO_OUTER_VOID(SetOpenerScriptPrincipal, (aPrincipal));
if (mDoc) {
if (!mDoc->IsInitialDocument()) {
// We have a document already, and it's not the original one. Bail out.
// Do NOT set mOpenerScriptPrincipal in this case, just to be safe.
return;
}
#ifdef DEBUG
// We better have an about:blank document loaded at this point. Otherwise,
// something is really weird.
nsCOMPtr<nsIURI> uri;
mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
NS_ASSERTION(uri && IsAboutBlank(uri) &&
IsAboutBlank(mDoc->GetDocumentURI()),
"Unexpected original document");
#endif
// Set the opener principal on our document; given the above check, this
// is safe.
mDoc->SetPrincipal(aPrincipal);
}
mOpenerScriptPrincipal = aPrincipal;
}
nsIPrincipal*
nsGlobalWindow::GetOpenerScriptPrincipal()
{
FORWARD_TO_OUTER(GetOpenerScriptPrincipal, (), nsnull);
return mOpenerScriptPrincipal;
}
PopupControlState
PushPopupControlState(PopupControlState aState, PRBool aForce)
{
@ -1667,6 +1703,8 @@ nsGlobalWindow::SetOpenerWindow(nsIDOMWindowInternal* aOpener,
NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
"aOriginalOpener is true, but not first call to "
"SetOpenerWindow!");
NS_ASSERTION(aOpener || !aOriginalOpener,
"Shouldn't set mHadOriginalOpener if aOpener is null");
mOpener = aOpener;
if (aOriginalOpener) {
@ -3533,6 +3571,7 @@ nsGlobalWindow::Focus()
if (mDocShell) {
// Don't look for a presshell if we're a root chrome window that's got
// about:blank loaded. We don't want to focus our widget in that case.
// XXXbz should we really be checking for IsInitialDocument() instead?
PRBool lookForPresShell = PR_TRUE;
PRInt32 itemType = nsIDocShellTreeItem::typeContent;
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(mDocShell));
@ -3544,12 +3583,8 @@ nsGlobalWindow::Focus()
nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
NS_ASSERTION(doc, "Bogus doc?");
nsIURI* ourURI = doc->GetDocumentURI();
PRBool isAbout;
if (ourURI && NS_SUCCEEDED(ourURI->SchemeIs("about", &isAbout)) &&
isAbout) {
nsCAutoString spec;
ourURI->GetSpec(spec);
lookForPresShell = !spec.EqualsLiteral("about:blank");
if (ourURI) {
lookForPresShell = !IsAboutBlank(ourURI);
}
}
@ -6045,63 +6080,42 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
// success!
if (domReturn) {
// Save the principal of the calling script
// We need it to decide whether to clear the scope in SetNewDocument
NS_ASSERTION(nsContentUtils::GetSecurityManager(),
"No Security Manager Found!");
// Note that the opener script principal is not relevant for openDialog
// callers, since those already have chrome privileges. So we
// only want to do this when aDoJSFixups is true.
if (aDoJSFixups && nsContentUtils::GetSecurityManager()) {
nsCOMPtr<nsIPrincipal> principal;
nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(principal));
if (principal) {
nsCOMPtr<nsPIDOMWindow> domReturnPrivate(do_QueryInterface(domReturn));
domReturnPrivate->SetOpenerScriptPrincipal(principal);
}
}
domReturn.swap(*aReturn);
domReturn.swap(*aReturn);
}
if (NS_SUCCEEDED(rv)) {
if (aDoJSFixups) {
nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
if (!chrome_win) {
// A new non-chrome window was created from a call to
// window.open() from JavaScript, make sure there's a document in
// the new window. We do this by simply asking the new window for
// its document, this will synchronously create an empty document
// if there is no document in the window.
// XXXbz should this just use EnsureInnerWindow()?
if (aDoJSFixups) {
nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
if (!chrome_win) {
// A new non-chrome window was created from a call to
// window.open() from JavaScript, make sure there's a document in
// the new window. We do this by simply asking the new window for
// its document, this will synchronously create an empty document
// if there is no document in the window.
// XXXbz should this just use EnsureInnerWindow()?
#ifdef DEBUG_jst
{
nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn));
{
nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn));
nsIDOMDocument *temp = pidomwin->GetExtantDocument();
nsIDOMDocument *temp = pidomwin->GetExtantDocument();
NS_ASSERTION(temp, "No document in new window!!!");
}
NS_ASSERTION(temp, "No document in new window!!!");
}
#endif
nsCOMPtr<nsIDOMDocument> doc;
(*aReturn)->GetDocument(getter_AddRefs(doc));
}
nsCOMPtr<nsIDOMDocument> doc;
(*aReturn)->GetDocument(getter_AddRefs(doc));
}
}
if (checkForPopup) {
if (abuseLevel >= openControlled) {
nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow *, *aReturn);
if (!opened->IsPopupSpamWindow()) {
opened->SetPopupSpamWindow(PR_TRUE);
++gOpenPopupSpamCount;
}
if (checkForPopup) {
if (abuseLevel >= openControlled) {
nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow *, *aReturn);
if (!opened->IsPopupSpamWindow()) {
opened->SetPopupSpamWindow(PR_TRUE);
++gOpenPopupSpamCount;
}
if (abuseLevel >= openAbused)
FireAbuseEvents(PR_FALSE, PR_TRUE, aUrl, aName, aOptions);
}
if (abuseLevel >= openAbused)
FireAbuseEvents(PR_FALSE, PR_TRUE, aUrl, aName, aOptions);
}
// copy the session storage data over to the new window if

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

@ -237,6 +237,7 @@ public:
virtual NS_HIDDEN_(nsIFocusController*) GetRootFocusController();
virtual NS_HIDDEN_(void) SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal);
virtual NS_HIDDEN_(nsIPrincipal*) GetOpenerScriptPrincipal();
virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state, PRBool aForce) const;
virtual NS_HIDDEN_(void) PopPopupControlState(PopupControlState state) const;

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

@ -200,10 +200,8 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel)
if (!principal)
return NS_ERROR_FAILURE;
//-- Don't run if the script principal is different from the
// principal of the context, with two exceptions: we allow
// the script to run if the script has the system principal
// or the context is about:blank.
//-- Don't run if the script principal is different from the principal
// of the context, unless the script has the system principal.
nsCOMPtr<nsIPrincipal> objectPrincipal;
rv = securityManager->GetObjectPrincipal(
(JSContext*)scriptContext->GetNativeContext(),
@ -231,38 +229,12 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel)
}
}
else {
// No owner from channel, use the object principals.
rv = securityManager->GetObjectPrincipal(
(JSContext*)scriptContext->GetNativeContext(),
globalJSObject,
getter_AddRefs(principal));
// Paranoia check: If we don't have an owner, make sure that we're
// not giving this javascript URL the principals of some other
// random page, so if the principals aren't for about:blank, don't use
// them.
// XXX We can't just create new about:blank principals since caps
// refuses to treat two about:blank principals as equal.
if (principal) {
nsCOMPtr<nsIURI> uri;
rv = principal->GetURI(getter_AddRefs(uri));
if (!uri) {
rv = NS_ERROR_NOT_AVAILABLE;
}
if (NS_SUCCEEDED(rv)) {
nsCAutoString spec;
uri->GetSpec(spec);
if (!spec.EqualsLiteral("about:blank")) {
rv = NS_ERROR_FAILURE;
}
}
}
if (NS_FAILED(rv) || !principal) {
// If all else fails, use a null principal
principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
}
// No owner from channel, use the null principal for lack of anything
// better. Note that we do not use the object principal here because
// that would give the javascript: URL the principals of whatever page
// we might be remotely associated with, which is a good recipe for XSS
// issues.
principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
if (NS_FAILED(rv) || !principal) {
return NS_ERROR_FAILURE;

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

@ -63,6 +63,7 @@ REQUIRES = xpcom \
embed_base \
intl \
layout \
uriloader \
$(NULL)
CPPSRCS = nsPrompt.cpp \

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

@ -53,11 +53,13 @@
#include "nsIDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentLoader.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMWindow.h"
#include "nsIDOMChromeWindow.h"
#include "nsIDOMWindowInternal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScreen.h"
#include "nsIScreenManager.h"
#include "nsIScriptContext.h"
@ -793,6 +795,42 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
}
}
// Now we have to set the right opener principal on the new window. Note
// that we have to do this _before_ starting any URI loads, thanks to the
// sync nature of javascript: loads. Since this is the only place where we
// set said opener principal, we need to do it for all URIs, including
// chrome ones. So to deal with the mess that is bug 79775, just press on in
// a reasonable way even if GetSubjectPrincipal fails. In that case, just
// use a null subjectPrincipal.
nsCOMPtr<nsIPrincipal> subjectPrincipal;
if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) {
subjectPrincipal = nsnull;
}
if (windowIsNew) {
// Now set the opener principal on the new window. Note that we need to do
// this no matter whether we were opened from JS; if there is nothing on
// the JS stack, just use the principal of our parent window. In those
// cases we do _not_ set the parent window principal as the owner of the
// load--since we really don't know who the owner is, just leave it null.
nsIPrincipal* newWindowPrincipal = subjectPrincipal;
if (!newWindowPrincipal && aParent) {
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aParent));
if (sop) {
newWindowPrincipal = sop->GetPrincipal();
}
}
nsCOMPtr<nsPIDOMWindow> newWindow = do_QueryInterface(*_retval);
#ifdef DEBUG
nsCOMPtr<nsPIDOMWindow> newDebugWindow = do_GetInterface(newDocShell);
NS_ASSERTION(newWindow == newDebugWindow, "Different windows??");
#endif
if (newWindow) {
newWindow->SetOpenerScriptPrincipal(newWindowPrincipal);
}
}
if (uriToLoad) { // get the script principal and pass it to docshell
JSContextAutoPopper contextGuard;
@ -812,15 +850,8 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
if (!uriToLoadIsChrome) {
nsCOMPtr<nsIPrincipal> principal;
if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(principal))))
return NS_ERROR_FAILURE;
if (principal) {
nsCOMPtr<nsISupports> owner(do_QueryInterface(principal));
loadInfo->SetOwner(owner);
}
if (subjectPrincipal) {
loadInfo->SetOwner(subjectPrincipal);
}
// Set the new window's referrer from the calling context's document:
@ -1625,6 +1656,27 @@ nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
if (aParent) {
nsCOMPtr<nsIDOMWindowInternal> internalParent(do_QueryInterface(aParent));
piOpenedWindow->SetOpenerWindow(internalParent, aWindowIsNew); // damnit
if (aWindowIsNew) {
#ifdef DEBUG
// Assert that we're not loading things right now. If we are, when
// that load completes it will clobber whatever principals we set up
// on this new window!
nsCOMPtr<nsIDocumentLoader> docloader =
do_QueryInterface(aOpenedItem);
NS_ASSERTION(docloader, "How can we not have a docloader here?");
nsCOMPtr<nsIChannel> chan;
docloader->GetDocumentChannel(getter_AddRefs(chan));
NS_ASSERTION(!chan, "Why is there a document channel?");
#endif
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(piOpenedWindow->GetExtantDocument());
if (doc) {
doc->SetIsInitialDocument(PR_TRUE);
}
}
}
rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
}