Bug 994320 - Implement permitsAncestry CSP in C++. r=ckerschb,grobinson

This commit is contained in:
Sid Stamm 2014-05-20 10:43:17 -07:00
parent c5f52f7cbf
commit 101e9f2062
4 changed files with 145 additions and 11 deletions

View File

@ -26,6 +26,7 @@
#include "nsIPrincipal.h"
#include "nsIPropertyBag2.h"
#include "nsIScriptError.h"
#include "nsIWebNavigation.h"
#include "nsIWritablePropertyBag2.h"
#include "nsString.h"
#include "prlog.h"
@ -439,11 +440,118 @@ nsCSPContext::SetRequestContext(nsIURI* aSelfURI,
return NS_OK;
}
/**
* Based on the given docshell, determines if this CSP context allows the
* ancestry.
*
* In order to determine the URI of the parent document (one causing the load
* of this protected document), this function obtains the docShellTreeItem,
* then walks up the hierarchy until it finds a privileged (chrome) tree item.
* Getting the parent's URI looks like this in pseudocode:
*
* nsIDocShell->QI(nsIInterfaceRequestor)
* ->GI(nsIDocShellTreeItem)
* ->QI(nsIInterfaceRequestor)
* ->GI(nsIWebNavigation)
* ->GetCurrentURI();
*
* aDocShell is the docShell for the protected document.
*/
NS_IMETHODIMP
nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
{
// For now, we allows permitsAncestry, this will be fixed in Bug 994320
nsresult rv;
// Can't check ancestry without a docShell.
if (aDocShell == nullptr) {
return NS_ERROR_FAILURE;
}
*outPermitsAncestry = true;
// extract the ancestry as an array
nsCOMArray<nsIURI> ancestorsArray;
nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(aDocShell));
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_GetInterface(ir));
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
nsCOMPtr<nsIWebNavigation> webNav;
nsCOMPtr<nsIURI> currentURI;
nsCOMPtr<nsIURI> uriClone;
// iterate through each docShell parent item
while (NS_SUCCEEDED(treeItem->GetParent(getter_AddRefs(parentTreeItem))) &&
parentTreeItem != nullptr) {
ir = do_QueryInterface(parentTreeItem);
NS_ASSERTION(ir, "Could not QI docShellTreeItem to nsIInterfaceRequestor");
webNav = do_GetInterface(ir);
NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
rv = webNav->GetCurrentURI(getter_AddRefs(currentURI));
NS_ENSURE_SUCCESS(rv, rv);
if (currentURI) {
// stop when reaching chrome
bool isChrome = false;
rv = currentURI->SchemeIs("chrome", &isChrome);
NS_ENSURE_SUCCESS(rv, rv);
if (isChrome) { break; }
// delete the userpass from the URI.
rv = currentURI->CloneIgnoringRef(getter_AddRefs(uriClone));
NS_ENSURE_SUCCESS(rv, rv);
rv = uriClone->SetUserPass(EmptyCString());
NS_ENSURE_SUCCESS(rv, rv);
#ifdef PR_LOGGING
{
nsAutoCString spec;
uriClone->GetSpec(spec);
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, found ancestor: %s", spec.get()));
}
#endif
ancestorsArray.AppendElement(uriClone);
}
// next ancestor
treeItem = parentTreeItem;
}
nsAutoString violatedDirective;
// Now that we've got the ancestry chain in ancestorsArray, time to check
// them against any CSP.
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
// TODO(sid) the mapping from frame-ancestors context to TYPE_DOCUMENT is
// forced. while this works for now, we will implement something in
// bug 999656.
#ifdef PR_LOGGING
{
nsAutoCString spec;
ancestorsArray[a]->GetSpec(spec);
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s", spec.get()));
}
#endif
if (!mPolicies[i]->permits(nsIContentPolicy::TYPE_DOCUMENT,
ancestorsArray[a],
EmptyString(), // no nonce
violatedDirective)) {
// Policy is violated
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
NS_ENSURE_TRUE(observerService, NS_ERROR_NOT_AVAILABLE);
observerService->NotifyObservers(ancestorsArray[a],
CSP_VIOLATION_TOPIC,
violatedDirective.get());
// TODO(sid) generate violation reports and remove NotifyObservers
// call. (in bug 994322)
// TODO(sid) implement logic for report-only (in bug 994322)
*outPermitsAncestry = false;
}
}
}
return NS_OK;
}

View File

@ -636,6 +636,8 @@ CSP_DirectiveToContentType(enum CSPDirective aDir)
case CSP_MEDIA_SRC: return nsIContentPolicy::TYPE_MEDIA;
case CSP_OBJECT_SRC: return nsIContentPolicy::TYPE_OBJECT;
case CSP_FRAME_SRC: return nsIContentPolicy::TYPE_SUBDOCUMENT;
// TODO(sid): fix this mapping to be more precise (bug 999656)
case CSP_FRAME_ANCESTORS: return nsIContentPolicy::TYPE_DOCUMENT;
// Fall through to error for the following Directives:
case CSP_DEFAULT_SRC:

View File

@ -49,22 +49,24 @@ enum CSPDirective {
CSP_FONT_SRC,
CSP_CONNECT_SRC,
CSP_REPORT_URI,
CSP_FRAME_ANCESTORS,
// CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
// because we use it to calculate the size for the char* array.
CSP_LAST_DIRECTIVE_VALUE
};
static const char* CSPStrDirectives[] = {
"default-src", // CSP_DEFAULT_SRC = 0
"script-src", // CSP_SCRIPT_SRC
"object-src", // CSP_OBJECT_SRC
"style-src", // CSP_STYLE_SRC
"img-src", // CSP_IMG_SRC
"media-src", // CSP_MEDIA_SRC
"frame-src", // CSP_FRAME_SRC
"font-src", // CSP_FONT_SRC
"connect-src", // CSP_CONNECT_SRC
"report-uri", // CSP_REPORT_URI
"default-src", // CSP_DEFAULT_SRC = 0
"script-src", // CSP_SCRIPT_SRC
"object-src", // CSP_OBJECT_SRC
"style-src", // CSP_STYLE_SRC
"img-src", // CSP_IMG_SRC
"media-src", // CSP_MEDIA_SRC
"frame-src", // CSP_FRAME_SRC
"font-src", // CSP_FONT_SRC
"connect-src", // CSP_CONNECT_SRC
"report-uri", // CSP_REPORT_URI
"frame-ancestors" // CSP_FRAME_ANCESTORS
};
inline const char* CSP_EnumToDirective(enum CSPDirective aDir)

View File

@ -667,6 +667,28 @@ nsresult TestGoodGeneratedPolicies() {
"script-src http://policy-uri" },
{ "img-src 'self'; ",
"img-src http://www.selfuri.com" },
{ "frame-ancestors foo-bar.com",
"frame-ancestors http://foo-bar.com" },
{ "frame-ancestors http://a.com",
"frame-ancestors http://a.com" },
{ "frame-ancestors 'self'",
"frame-ancestors http://www.selfuri.com" },
{ "frame-ancestors http://self.com:88",
"frame-ancestors http://self.com:88" },
{ "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com",
"frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com" },
{ "frame-ancestors https://self.com:34",
"frame-ancestors https://self.com:34" },
{ "default-src 'none'; frame-ancestors 'self'",
"default-src 'none'; frame-ancestors http://www.selfuri.com" },
{ "frame-ancestors http://self:80",
"frame-ancestors http://self:80" },
{ "frame-ancestors http://self.com/bar",
"frame-ancestors http://self.com" },
{ "default-src 'self'; frame-ancestors 'self'",
"default-src http://www.selfuri.com; frame-ancestors http://www.selfuri.com" },
{ "frame-ancestors http://bar.com/foo.png",
"frame-ancestors http://bar.com" },
};
uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);