From 7446e864229efae1f1aeba883f8d222ce6fcb83a Mon Sep 17 00:00:00 2001 From: "mstoltz%netscape.com" Date: Tue, 19 Feb 2002 01:09:45 +0000 Subject: [PATCH] Bug 105050 - return null window.opener to scripts if opener is a mail window. Bug 32571 - Prompt user before allowing scripts to close windows if opener is null. both r=heikki, sr=jst. --- caps/idl/nsIScriptSecurityManager.idl | 6 ++ caps/src/nsScriptSecurityManager.cpp | 26 ++++++- dom/resources/locale/dom.properties | 5 +- dom/src/base/Makefile.in | 1 + dom/src/base/makefile.win | 1 + dom/src/base/nsGlobalWindow.cpp | 107 ++++++++++++++++++++++++-- dom/src/base/nsGlobalWindow.h | 1 + modules/libpref/src/init/all.js | 3 + 8 files changed, 140 insertions(+), 10 deletions(-) diff --git a/caps/idl/nsIScriptSecurityManager.idl b/caps/idl/nsIScriptSecurityManager.idl index 521281c3095..a102b40eee8 100644 --- a/caps/idl/nsIScriptSecurityManager.idl +++ b/caps/idl/nsIScriptSecurityManager.idl @@ -198,6 +198,12 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager [noscript] nsIPrincipal getObjectPrincipal(in JSContextPtr cx, in JSObjectPtr obj); + /** + * Returns true if the principal of the currently running script is the + * system principal, false otherwise. + */ + boolean subjectPrincipalIsSystem(); + /** * Forget all currently stored security policies and reread from prefs. * This must be called after any capability.policy prefs have changed. diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 6999e305258..39e64c60422 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -1240,6 +1240,31 @@ nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result) return NS_OK; } +NS_IMETHODIMP +nsScriptSecurityManager::SubjectPrincipalIsSystem(PRBool* aIsSystem) +{ + NS_ENSURE_ARG_POINTER(aIsSystem); + *aIsSystem = PR_FALSE; + + if (!mSystemPrincipal) + return NS_OK; + + nsCOMPtr subject; + nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject)); + if (NS_FAILED(rv)) + return rv; + + if(!subject) + { + // No subject principal means no JS is running; + // this is the equivalent of system principal code + *aIsSystem = PR_TRUE; + return NS_OK; + } + + return mSystemPrincipal->Equals(subject, aIsSystem); +} + NS_IMETHODIMP nsScriptSecurityManager::GetCertificatePrincipal(const char* aCertID, nsIPrincipal **result) @@ -2677,7 +2702,6 @@ nsScriptSecurityManager::InitPrefs() PRUint32 prefCount; char** prefNames; - //-- Set a callback for policy changes // Registering the security manager as an observer to the // profile-after-change topic. We will build up the policy table // after the initial profile loads and after profile switches. diff --git a/dom/resources/locale/dom.properties b/dom/resources/locale/dom.properties index 637a00c9e77..7fb07bd9207 100644 --- a/dom/resources/locale/dom.properties +++ b/dom/resources/locale/dom.properties @@ -1 +1,4 @@ -JSURLLoadBlockedWarning=Attempt to load a javascript: URL from one host\nin a window displaying content from another host\nwas blocked by the security manager. +JSURLLoadBlockedWarning = Attempt to load a javascript: URL from one host\nin a window displaying content from another host\nwas blocked by the security manager. +ConfirmWindowCloseDialogTitle = Close Window? +ConfirmWindowCloseDialogText = A script wants to close the current window. Do you wish to allow this? + diff --git a/dom/src/base/Makefile.in b/dom/src/base/Makefile.in index e48e27148f8..4f2cf5e2f30 100644 --- a/dom/src/base/Makefile.in +++ b/dom/src/base/Makefile.in @@ -60,6 +60,7 @@ REQUIRES = xpcom \ xmlextras \ find \ appshell \ + intl \ $(NULL) CPPSRCS = \ diff --git a/dom/src/base/makefile.win b/dom/src/base/makefile.win index a2e8aafb7d0..2703d5f6801 100644 --- a/dom/src/base/makefile.win +++ b/dom/src/base/makefile.win @@ -57,6 +57,7 @@ REQUIRES = xpcom \ htmlparser \ chardet \ transformiix \ + intl \ xmlextras \ find \ appshell \ diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 477f7f11eab..71b3674e5bf 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -124,6 +124,7 @@ #include "nsISupportsPrimitives.h" #include "nsDOMClassInfo.h" #include "nsIJSNativeInitializer.h" +#include "nsIStringBundle.h" #include "plbase64.h" @@ -155,7 +156,7 @@ static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); // For window.find() static const char *sWindowWatcherContractID = "@mozilla.org/embedcomp/window-watcher;1"; static const char *sJSStackContractID = "@mozilla.org/js/xpc/ContextStack;1"; - +static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID); static const char * const kCryptoContractID = NS_CRYPTO_CONTRACTID; static const char * const kPkcs11ContractID = NS_PKCS11_CONTRACTID; @@ -1156,7 +1157,28 @@ GlobalWindowImpl::GetControllers(nsIControllers** aResult) NS_IMETHODIMP GlobalWindowImpl::GetOpener(nsIDOMWindowInternal** aOpener) { - *aOpener = mOpener; + *aOpener = nsnull; + // We don't want to reveal the opener if the opener is a mail window, + // because opener can be used to spoof the contents of a message (bug 105050). + // So, we look in the opener's root docshell to see if it's a mail window. + nsCOMPtr openerSGO(do_QueryInterface(mOpener)); + if (openerSGO) { + nsCOMPtr openerDocShell; + openerSGO->GetDocShell(getter_AddRefs(openerDocShell)); + nsCOMPtr docShellAsItem(do_QueryInterface(openerDocShell)); + if (docShellAsItem) { + nsCOMPtr openerRootItem; + docShellAsItem->GetRootTreeItem(getter_AddRefs(openerRootItem)); + nsCOMPtr openerRootDocShell(do_QueryInterface(openerRootItem)); + if (openerRootDocShell) { + PRUint32 appType; + nsresult rv = openerRootDocShell->GetAppType(&appType); + if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) { + *aOpener = mOpener; + } + } + } + } NS_IF_ADDREF(*aOpener); return NS_OK; } @@ -2438,6 +2460,46 @@ GlobalWindowImpl::GetFrames(nsIDOMWindow** aFrames) return NS_OK; } +#define DOM_PROPERTIES_URL "chrome://communicator/locale/dom/dom.properties" + +nsresult +GlobalWindowImpl::ConfirmClose(PRBool* aConfirmed) +{ + nsresult rv; + // Get the localized strings for the dialog + nsCOMPtr bundleService( + do_GetService(kStringBundleServiceCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + //XXX: What to do on error? + nsCOMPtr bundle; + rv = bundleService->CreateBundle(NS_LITERAL_CSTRING(DOM_PROPERTIES_URL).get(), + getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXPIDLString confirmTitle; + rv = bundle->GetStringFromName(NS_LITERAL_STRING("ConfirmWindowCloseDialogTitle").get(), + getter_Copies(confirmTitle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXPIDLString confirmText; + rv = bundle->GetStringFromName(NS_LITERAL_STRING("ConfirmWindowCloseDialogText").get(), + getter_Copies(confirmText)); + NS_ENSURE_SUCCESS(rv, rv); + + // Show the dialog + nsCOMPtr prompter; + rv = GetPrompter(getter_AddRefs(prompter)); + NS_ENSURE_SUCCESS(rv, rv); + + return prompter->Confirm(confirmTitle, + confirmText, aConfirmed); +} + +#define DENY_SCRIPT_CLOSE 0 +#define CONFIRM_SCRIPT_CLOSE 1 +#define ALLOW_SCRIPT_CLOSE 2 + NS_IMETHODIMP GlobalWindowImpl::Close() { @@ -2447,15 +2509,44 @@ GlobalWindowImpl::Close() if (parent != NS_STATIC_CAST(nsIDOMWindow *, this)) { // window.close() is called on a frame in a frameset, such calls // are ignored. - return NS_OK; } - // Note: the basic security check, rejecting windows not opened through JS, - // has been removed. This was approved long ago by ...you're going to call me - // on this, aren't you... well it was. And anyway, a better means is coming. - // In the new world of application-level interfaces being written in JS, this - // security check was causing problems. + // If this window was not opened by script (!mOpener), find out + // whether it's OK for a script to close it. + if (!mOpener) { + nsresult rv; + nsCOMPtr secMan = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // System scripts can close all windows + PRBool InSystemCode = PR_FALSE; + rv = secMan->SubjectPrincipalIsSystem(&InSystemCode); + if (NS_FAILED(rv) || !InSystemCode) { + // Check the pref "dom.allow_scripts_to_close_windows" + nsCOMPtr pref(do_GetService(kPrefServiceCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 closePref; + rv = pref->GetIntPref("dom.allow_scripts_to_close_windows", &closePref); + if (NS_FAILED(rv)) + closePref = CONFIRM_SCRIPT_CLOSE; + PRBool confirmOK = PR_FALSE; + if (closePref == CONFIRM_SCRIPT_CLOSE) { + // Ask the user whether it's OK to close the window + rv = ConfirmClose(&confirmOK); + if (NS_FAILED(rv)) + return rv; + } else { + confirmOK = (closePref == ALLOW_SCRIPT_CLOSE); + } + if (!confirmOK) { + // User clicked Cancel, so abort the close + return NS_OK; + } + } + } nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); diff --git a/dom/src/base/nsGlobalWindow.h b/dom/src/base/nsGlobalWindow.h index 23340e4cae6..16bc46b1483 100644 --- a/dom/src/base/nsGlobalWindow.h +++ b/dom/src/base/nsGlobalWindow.h @@ -214,6 +214,7 @@ protected: const nsAReadableString& aOptions, PRBool aDialog, jsval *argv, PRUint32 argc, nsISupports *aExtraArgument, nsIDOMWindow **aReturn); + nsresult ConfirmClose(PRBool* aConfirmed); static void CloseWindow(nsISupports* aWindow); // Timeout Functions diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index b6df67f5cff..f03d74b6d93 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -334,6 +334,9 @@ pref("capability.principal.codebase.foo.granted", "UniversalFoo"); ////////////////////////////////////////////////////////// pref("dom.disable_open_during_load", false); +// 0 = never allow scripts to close windows that weren't opened by script, +// 1 = prompt user, 2 = always allow. Default is prompt. +pref("dom.allow_scripts_to_close_windows", 1); pref("javascript.enabled", true); pref("javascript.allow.mailnews", false);