Skip to content

Commit

Permalink
Bug 766282 - Implement allow-popups directive for iframe sandbox. r=s…
Browse files Browse the repository at this point in the history
…maug
  • Loading branch information
bobowen committed Mar 21, 2013
1 parent ed39892 commit c506577
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 82 deletions.
3 changes: 3 additions & 0 deletions content/base/src/nsContentUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ nsContentUtils::ParseSandboxAttributeToFlags(const nsAString& aSandboxAttrValue)
// If there's a sandbox attribute at all (and there is if this is being
// called), start off by setting all the restriction flags.
uint32_t out = SANDBOXED_NAVIGATION |
SANDBOXED_AUXILIARY_NAVIGATION |
SANDBOXED_TOPLEVEL_NAVIGATION |
SANDBOXED_PLUGINS |
SANDBOXED_ORIGIN |
Expand Down Expand Up @@ -954,6 +955,8 @@ nsContentUtils::ParseSandboxAttributeToFlags(const nsAString& aSandboxAttrValue)
out &= ~SANDBOXED_TOPLEVEL_NAVIGATION;
} else if (token.LowerCaseEqualsLiteral("allow-pointer-lock")) {
out &= ~SANDBOXED_POINTER_LOCK;
} else if (token.LowerCaseEqualsLiteral("allow-popups")) {
out &= ~SANDBOXED_AUXILIARY_NAVIGATION;
}
}
}
Expand Down
4 changes: 0 additions & 4 deletions content/base/src/nsGkAtomList.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,8 @@ GK_ATOM(align, "align")
GK_ATOM(alink, "alink")
GK_ATOM(all, "all")
GK_ATOM(allowevents, "allowevents")
GK_ATOM(allowforms, "allow-forms")
GK_ATOM(allownegativeassertions, "allownegativeassertions")
GK_ATOM(allowfullscreen, "allowfullscreen")
GK_ATOM(allowsameorigin, "allow-same-origin")
GK_ATOM(allowscripts, "allow-scripts")
GK_ATOM(allowtopnavigation, "allow-top-navigation")
GK_ATOM(allowuntrusted, "allowuntrusted")
GK_ATOM(alt, "alt")
GK_ATOM(alternate, "alternate")
Expand Down
14 changes: 12 additions & 2 deletions content/base/src/nsSandboxFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

/**
* This flag prevents content from navigating browsing contexts other than
* the sandboxed browsing context itself (or browsing contexts further
* nested inside it), and the top-level browsing context.
* itself, browsing contexts nested inside it, the top-level browsing context
* and browsing contexts that it has opened.
* As it is always on for sandboxed browsing contexts, it is used implicitly
* within the code by checking that the overall flags are non-zero.
* It is only uesd directly when the sandbox flags are initially set up.
*/
const unsigned long SANDBOXED_NAVIGATION = 0x1;

Expand Down Expand Up @@ -65,4 +68,11 @@ const unsigned long SANDBOXED_POINTER_LOCK = 0x80;
* This flag blocks the document from changing document.domain.
*/
const unsigned long SANDBOXED_DOMAIN = 0x100;

/**
* This flag prevents content from creating new auxiliary browsing contexts,
* e.g. using the target attribute, the window.open() method, or the
* showModalDialog() method.
*/
const unsigned long SANDBOXED_AUXILIARY_NAVIGATION = 0x200;
#endif
168 changes: 104 additions & 64 deletions docshell/base/nsDocShell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3190,62 +3190,11 @@ nsDocShell::FindItemWithName(const PRUnichar * aName,
// DoFindItemWithName only returns active items and we don't check if
// the item is active for the special cases.
if (foundItem) {

// If our document is sandboxed, we need to do some extra checks.
uint32_t sandboxFlags = 0;

nsCOMPtr<nsIDocument> doc = do_GetInterface(aOriginalRequestor);

if (doc) {
sandboxFlags = doc->GetSandboxFlags();
}

if (sandboxFlags) {
nsCOMPtr<nsIDocShellTreeItem> root;
GetSameTypeRootTreeItem(getter_AddRefs(root));

// Is the found item not a top level browsing context and not ourself ?
nsCOMPtr<nsIDocShellTreeItem> selfAsItem = static_cast<nsIDocShellTreeItem *>(this);
if (foundItem != root && foundItem != selfAsItem) {
// Are we an ancestor of the foundItem ?
bool isAncestor = false;

nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
foundItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
while (parentAsItem) {
if (parentAsItem == selfAsItem) {
isAncestor = true;
break;
}
nsCOMPtr<nsIDocShellTreeItem> tmp = parentAsItem;
tmp->GetSameTypeParent(getter_AddRefs(parentAsItem));
}

if (!isAncestor) {
// No, we are not an ancestor and our document is
// sandboxed, we can't allow this.
foundItem = nullptr;
}
} else {
// Top level browsing context - is it an ancestor of ours ?
nsCOMPtr<nsIDocShellTreeItem> tmp;
GetSameTypeParent(getter_AddRefs(tmp));

while (tmp) {
if (tmp && tmp == foundItem) {
// This is an ancestor, and we are sandboxed.
// Unless allow-top-navigation is set, we can't allow this.
if (sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) {
foundItem = nullptr;
}
break;
}
tmp->GetParent(getter_AddRefs(tmp));
}
}
if (IsSandboxedFrom(foundItem, aOriginalRequestor)) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
} else {
foundItem.swap(*_retval);
}

foundItem.swap(*_retval);
}
return NS_OK;
}
Expand Down Expand Up @@ -3318,6 +3267,70 @@ nsDocShell::DoFindItemWithName(const PRUnichar* aName,
return NS_OK;
}

/* static */
bool
nsDocShell::IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
nsIDocShellTreeItem* aAccessingItem)
{
// aAccessingItem cannot be sandboxed from itself.
if (SameCOMIdentity(aTargetItem, aAccessingItem)) {
return false;
}

uint32_t sandboxFlags = 0;

nsCOMPtr<nsIDocument> doc = do_GetInterface(aAccessingItem);
if (doc) {
sandboxFlags = doc->GetSandboxFlags();
}

// If no flags, aAccessingItem is not sandboxed at all.
if (!sandboxFlags) {
return false;
}

// If aTargetItem has an ancestor, it is not top level.
nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
aTargetItem->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
if (ancestorOfTarget) {
do {
// aAccessingItem is not sandboxed if it is an ancestor of target.
if (SameCOMIdentity(aAccessingItem, ancestorOfTarget)) {
return false;
}
nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
tempTreeItem.swap(ancestorOfTarget);
} while (ancestorOfTarget);

// Otherwise, aAccessingItem is sandboxed from aTargetItem.
return true;
}

// aTargetItem is top level, is aAccessingItem the "one permitted sandboxed
// navigator", i.e. did aAccessingItem open aTargetItem?
nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(aTargetItem);
nsCOMPtr<nsIDocShell> permittedNavigator;
targetDocShell->
GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator));
if (SameCOMIdentity(aAccessingItem, permittedNavigator)) {
return false;
}

// If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, aAccessingItem is
// not sandboxed from its top.
if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
if (SameCOMIdentity(aTargetItem, rootTreeItem)) {
return false;
}
}

// Otherwise, aAccessingItem is sandboxed from aTargetItem.
return true;
}

NS_IMETHODIMP
nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
{
Expand Down Expand Up @@ -5055,6 +5068,8 @@ nsDocShell::Destroy()

SetTreeOwner(nullptr);

mOnePermittedSandboxedNavigator = nullptr;

// required to break ref cycle
mSecurityUI = nullptr;

Expand Down Expand Up @@ -5403,6 +5418,31 @@ nsDocShell::GetSandboxFlags(uint32_t *aSandboxFlags)
return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
{
if (mOnePermittedSandboxedNavigator) {
NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
return NS_OK;
}

mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
NS_ASSERTION(mOnePermittedSandboxedNavigator,
"One Permitted Sandboxed Navigator must support weak references.");

return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
{
NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
nsCOMPtr<nsIDocShell> permittedNavigator =
do_QueryReferent(mOnePermittedSandboxedNavigator);
NS_IF_ADDREF(*aSandboxedNavigator = permittedNavigator);
return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
{
Expand Down Expand Up @@ -8719,8 +8759,10 @@ nsDocShell::InternalLoad(nsIURI * aURI,
if (aWindowTarget && *aWindowTarget) {
// Locate the target DocShell.
nsCOMPtr<nsIDocShellTreeItem> targetItem;
FindItemWithName(aWindowTarget, nullptr, this,
getter_AddRefs(targetItem));
if (FindItemWithName(aWindowTarget, nullptr, this,
getter_AddRefs(targetItem)) == NS_ERROR_DOM_INVALID_ACCESS_ERR) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}

targetDocShell = do_QueryInterface(targetItem);
// If the targetDocShell doesn't exist, then this is a new docShell
Expand Down Expand Up @@ -8847,19 +8889,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,

bool isNewWindow = false;
if (!targetDocShell) {
// If the docshell's document is sandboxed and was trying to
// navigate/load a frame it wasn't allowed to access, the
// FindItemWithName above will have returned null for the target
// item - we don't want to actually open a new window in this case
// though. Check if we are sandboxed and bail out here if so.
// If the docshell's document is sandboxed, only open a new window
// if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
// (i.e. if allow-popups is specified)
NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
nsIDocument* doc = mContentViewer->GetDocument();
uint32_t sandboxFlags = 0;

if (doc) {
sandboxFlags = doc->GetSandboxFlags();
if (sandboxFlags & SANDBOXED_NAVIGATION) {
return NS_ERROR_FAILURE;
if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
}

Expand Down
5 changes: 5 additions & 0 deletions docshell/base/nsDocShell.h
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ class nsDocShell : public nsDocLoader,
int32_t mLoadedTransIndex;

uint32_t mSandboxFlags;
nsWeakPtr mOnePermittedSandboxedNavigator;

// mFullscreenAllowed stores how we determine whether fullscreen is allowed
// when GetFullscreenAllowed() is called. Fullscreen is allowed in a
Expand Down Expand Up @@ -880,6 +881,10 @@ class nsDocShell : public nsDocLoader,
nsIDocShellTreeItem* aOriginalRequestor,
nsIDocShellTreeItem** _retval);

// Check whether accessing item is sandboxed from the target item.
static bool IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
nsIDocShellTreeItem* aAccessingItem);

#ifdef DEBUG
// We're counting the number of |nsDocShells| to help find leaks
static unsigned long gNumberOfDocShells;
Expand Down
9 changes: 8 additions & 1 deletion docshell/base/nsIDocShell.idl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface nsIReflowObserver;

typedef unsigned long nsLoadFlags;

[scriptable, builtinclass, uuid(5f4d82fc-3220-4f7e-9b00-626f1033318a)]
[scriptable, builtinclass, uuid(4ca172c3-67bf-4e6d-89a3-cbfb929c370d)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
Expand Down Expand Up @@ -798,6 +798,13 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
attribute unsigned long sandboxFlags;

/**
* When a new browsing context is opened by a sandboxed document, it needs to
* keep track of the browsing context that opened it, so that it can be
* navigated by it. This is the "one permitted sandboxed navigator".
*/
attribute nsIDocShell onePermittedSandboxedNavigator;

/**
* This member variable determines whether a document has Mixed Active Content that
* was initially blocked from loading, but the user has choosen to override the
Expand Down
Loading

0 comments on commit c506577

Please sign in to comment.