Bug 951457 - Create C++ CSP Parser and policy classes, part 2 - contentpolicy tiein r=sstamm, r=grobinson

This commit is contained in:
Christoph Kerschbaumer 2014-04-01 16:00:19 -07:00
parent 20d36d770e
commit af0fa6b545
3 changed files with 293 additions and 24 deletions

View File

@ -3,9 +3,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Preferences.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsChannelProperties.h"
#include "nsCOMPtr.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
@ -14,12 +11,16 @@
#include "nsCSPService.h"
#include "nsError.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIChannelEventSink.h"
#include "nsIChannelPolicy.h"
#include "nsIClassInfoImpl.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsIHttpChannel.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIPrincipal.h"
@ -31,6 +32,19 @@
using namespace mozilla;
#if defined(PR_LOGGING)
static PRLogModuleInfo *
GetCspContextLog()
{
static PRLogModuleInfo *gCspContextPRLog;
if (!gCspContextPRLog)
gCspContextPRLog = PR_NewLogModule("CSPContext");
return gCspContextPRLog;
}
#endif
#define CSPCONTEXTLOG(args) PR_LOG(GetCspContextLog(), 4, args)
/* ===== nsIContentSecurityPolicy impl ====== */
NS_IMETHODIMP
@ -42,7 +56,94 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
nsISupports* aExtra,
int16_t* outDecision)
{
#ifdef PR_LOGGING
{
nsAutoCString spec;
aContentLocation->GetSpec(spec);
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s", spec.get()));
}
#endif
nsresult rv = NS_OK;
// This ShouldLoad function is called from nsCSPService::ShouldLoad,
// which already checked a number of things, including:
// * aContentLocation is not null; we can consume this without further checks
// * scheme is not a whitelisted scheme (about: chrome:, etc).
// * CSP is enabled
// * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
// * Fast Path for Apps
// Default decision, CSP can revise it if there's a policy to enforce
*outDecision = nsIContentPolicy::ACCEPT;
// This may be a load or a preload. If it is a preload, the document will
// not have been fully parsed yet, and aRequestContext will be an
// nsIDOMHTMLDocument rather than the nsIDOMHTMLElement associated with the
// resource. As a result, we cannot extract the element's corresponding
// nonce attribute, and so we cannot correctly check the nonce on a preload.
//
// Therefore, the decision returned here for a preload may be *incorrect* as
// it cannot take the nonce into account. We will still check the load, but
// we will not cache the result or report a violation. When the "real load"
// happens subsequently, we will re-check with the additional context to
// make a final decision.
//
// We don't just return false because that would block all preloads and
// degrade performance. However, we do want to block preloads that are
// clearly blocked (their urls are not whitelisted) by CSP.
nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryInterface(aRequestContext);
bool isPreload = doc &&
(aContentType == nsIContentPolicy::TYPE_SCRIPT ||
aContentType == nsIContentPolicy::TYPE_STYLESHEET);
nsAutoString nonce;
if (!isPreload) {
nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aRequestContext);
if (htmlElement) {
rv = htmlElement->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsAutoString violatedDirective;
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
if (!mPolicies[p]->permits(aContentType,
aContentLocation,
nonce,
violatedDirective)) {
// If the policy is violated and not report-only, reject the load and
// report to the console
if (!mPolicies[p]->getReportOnlyFlag()) {
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, nsIContentPolicy::REJECT_SERVER"));
*outDecision = nsIContentPolicy::REJECT_SERVER;
}
// Do not send a report or notify observers if this is a preload - the
// decision may be wrong due to the inability to get the nonce, and will
// incorrectly fail the unit tests.
if (!isPreload) {
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
NS_ASSERTION(observerService, "CSP requires observer service.");
observerService->NotifyObservers(aContentLocation,
CSP_VIOLATION_TOPIC,
violatedDirective.get());
}
// TODO: future patches fix:
// * AsyncReportViolation, bug 994322
// * Console error reporting, bug 994322
}
}
#ifdef PR_LOGGING
{
nsAutoCString spec;
aContentLocation->GetSpec(spec);
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
}
#endif
return NS_OK;
}
@ -61,16 +162,24 @@ nsCSPContext::ShouldProcess(nsContentPolicyType aContentType,
/* ===== nsISupports implementation ========== */
NS_IMPL_ISUPPORTS(nsCSPContext,
nsIContentSecurityPolicy,
nsISerializable)
NS_IMPL_CLASSINFO(nsCSPContext,
nullptr,
nsIClassInfo::MAIN_THREAD_ONLY,
NS_CSPCONTEXT_CID)
NS_IMPL_ISUPPORTS_CI(nsCSPContext,
nsIContentSecurityPolicy,
nsISerializable)
nsCSPContext::nsCSPContext()
: mSelfURI(nullptr)
{
CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
}
nsCSPContext::~nsCSPContext()
{
CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
delete mPolicies[i];
}
@ -86,7 +195,7 @@ NS_IMETHODIMP
nsCSPContext::GetPolicy(uint32_t aIndex, nsAString& outStr)
{
if (aIndex >= mPolicies.Length()) {
return NS_ERROR_FAILURE;
return NS_ERROR_ILLEGAL_VALUE;
}
mPolicies[aIndex]->toString(outStr);
return NS_OK;
@ -115,33 +224,95 @@ nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
bool aReportOnly,
bool aSpecCompliant)
{
NS_ASSERTION(aSelfURI, "aSelfURI required for AppendPolicy");
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, aSelfURI, aReportOnly, 0);
CSPCONTEXTLOG(("nsCSPContext::AppendPolicy: %s",
NS_ConvertUTF16toUTF8(aPolicyString).get()));
if (aSelfURI) {
// aSelfURI will be disregarded since we will remove it with bug 991474
NS_WARNING("aSelfURI should be a nullptr in AppendPolicy and removed in bug 991474");
}
// Use the mSelfURI from setRequestContext, see bug 991474
NS_ASSERTION(mSelfURI, "mSelfURI required for AppendPolicy, but not set");
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, 0);
if (policy) {
mPolicies.AppendElement(policy);
}
return NS_OK;
}
// aNonceOrContent either holds the nonce-value or otherwise the content
// of the element to be hashed.
NS_IMETHODIMP
nsCSPContext::GetAllowsInlineScript(bool* outShouldReportViolations,
nsCSPContext::getAllowsInternal(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword,
const nsAString& aNonceOrContent,
bool* outShouldReportViolation,
bool* outIsAllowed) const
{
*outShouldReportViolation = false;
*outIsAllowed = true;
// Skip things that aren't hash/nonce compatible
if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) {
if (!(aContentType == nsIContentPolicy::TYPE_SCRIPT ||
aContentType == nsIContentPolicy::TYPE_STYLESHEET)) {
*outIsAllowed = false;
return NS_OK;
}
}
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
if (!mPolicies[i]->allows(aContentType,
aKeyword,
aNonceOrContent)) {
// policy is violated: must report the violation and allow the inline
// script if the policy is report-only.
*outShouldReportViolation = true;
if (!mPolicies[i]->getReportOnlyFlag()) {
*outIsAllowed = false;
}
}
}
CSPCONTEXTLOG(("nsCSPContext::getAllowsInternal, aContentType: %d, aKeyword: %s, aNonceOrContent: %s, isAllowed: %s",
aContentType,
aKeyword == CSP_HASH ? "hash" : CSP_EnumToKeyword(aKeyword),
NS_ConvertUTF16toUTF8(aNonceOrContent).get(),
*outIsAllowed ? "load" : "deny"));
return NS_OK;
}
NS_IMETHODIMP
nsCSPContext::GetAllowsInlineScript(bool* outShouldReportViolation,
bool* outAllowsInlineScript)
{
return NS_ERROR_NOT_IMPLEMENTED;
return getAllowsInternal(nsIContentPolicy::TYPE_SCRIPT,
CSP_UNSAFE_INLINE,
EmptyString(),
outShouldReportViolation,
outAllowsInlineScript);
}
NS_IMETHODIMP
nsCSPContext::GetAllowsEval(bool* outShouldReportViolations,
nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
bool* outAllowsEval)
{
return NS_ERROR_NOT_IMPLEMENTED;
return getAllowsInternal(nsIContentPolicy::TYPE_SCRIPT,
CSP_UNSAFE_EVAL,
EmptyString(),
outShouldReportViolation,
outAllowsEval);
}
NS_IMETHODIMP
nsCSPContext::GetAllowsInlineStyle(bool* outShouldReportViolations,
nsCSPContext::GetAllowsInlineStyle(bool* outShouldReportViolation,
bool* outAllowsInlineStyle)
{
return NS_ERROR_NOT_IMPLEMENTED;
return getAllowsInternal(nsIContentPolicy::TYPE_STYLESHEET,
CSP_UNSAFE_INLINE,
EmptyString(),
outShouldReportViolation,
outAllowsInlineStyle);
}
NS_IMETHODIMP
@ -150,7 +321,11 @@ nsCSPContext::GetAllowsNonce(const nsAString& aNonce,
bool* outShouldReportViolation,
bool* outAllowsNonce)
{
return NS_ERROR_NOT_IMPLEMENTED;
return getAllowsInternal(aContentType,
CSP_NONCE,
aNonce,
outShouldReportViolation,
outAllowsNonce);
}
NS_IMETHODIMP
@ -159,7 +334,11 @@ nsCSPContext::GetAllowsHash(const nsAString& aContent,
bool* outShouldReportViolation,
bool* outAllowsHash)
{
return NS_ERROR_NOT_IMPLEMENTED;
return getAllowsInternal(aContentType,
CSP_HASH,
aContent,
outShouldReportViolation,
outAllowsHash);
}
NS_IMETHODIMP
@ -179,7 +358,24 @@ nsCSPContext::SetRequestContext(nsIURI* aSelfURI,
nsIPrincipal* aDocumentPrincipal,
nsIChannel* aChannel)
{
return NS_ERROR_NOT_IMPLEMENTED;
if (!aSelfURI && !aChannel) {
CSPCONTEXTLOG(("nsCSPContext::SetRequestContext: !selfURI && !aChannel provided"));
return NS_ERROR_FAILURE;
}
mSelfURI = aSelfURI;
if (!mSelfURI) {
nsresult rv = aChannel->GetURI(getter_AddRefs(mSelfURI));
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ASSERTION(mSelfURI, "No mSelfURI in SetRequestContext, can not translate 'self' into actual URI");
// aDocumentPrincipal will be removed in bug 994872
// aReferrer will be used in the patch for bug 994322
return NS_OK;
}
NS_IMETHODIMP
@ -191,14 +387,76 @@ nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
}
/* ===== nsISerializable implementation ====== */
NS_IMETHODIMP
nsCSPContext::Read(nsIObjectInputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
nsresult rv;
nsCOMPtr<nsISupports> supports;
rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
NS_ENSURE_SUCCESS(rv, rv);
mSelfURI = do_QueryInterface(supports);
NS_ASSERTION(mSelfURI, "need a self URI to de-serialize");
uint32_t numPolicies;
rv = aStream->Read32(&numPolicies);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString policyString;
while (numPolicies > 0) {
numPolicies--;
rv = aStream->ReadString(policyString);
NS_ENSURE_SUCCESS(rv, rv);
bool reportOnly = false;
rv = aStream->ReadBoolean(&reportOnly);
NS_ENSURE_SUCCESS(rv, rv);
bool specCompliant = false;
rv = aStream->ReadBoolean(&specCompliant);
NS_ENSURE_SUCCESS(rv, rv);
// Using the new backend, we don't support non-spec-compliant policies, so
// skip any of those, will be fixed in bug 991466
if (!specCompliant) {
continue;
}
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(policyString,
mSelfURI,
reportOnly,
0);
if (policy) {
mPolicies.AppendElement(policy);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSPContext::Write(nsIObjectOutputStream* aStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
nsresult rv = NS_WriteOptionalCompoundObject(aStream,
mSelfURI,
NS_GET_IID(nsIURI),
true);
NS_ENSURE_SUCCESS(rv, rv);
// Serialize all the policies.
aStream->Write32(mPolicies.Length());
nsAutoString polStr;
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
mPolicies[p]->toString(polStr);
aStream->WriteWStringZ(polStr.get());
aStream->WriteBoolean(mPolicies[p]->getReportOnlyFlag());
// Setting specCompliant boolean for backwards compatibility (fix in bug 991466)
aStream->WriteBoolean(true);
}
return NS_OK;
}

View File

@ -8,13 +8,11 @@
#include "nsCSPUtils.h"
#include "nsIChannel.h"
#include "nsIClassInfo.h"
#include "nsIContentSecurityPolicy.h"
#include "nsISerializable.h"
#include "nsXPCOM.h"
class nsIObjectInputStream;
class nsIObjectOutputStream;
#define NS_CSPCONTEXT_CONTRACTID "@mozilla.org/cspcontext;1"
// 09d9ed1a-e5d4-4004-bfe0-27ceb923d9ac
#define NS_CSPCONTEXT_CID \
@ -32,6 +30,12 @@ class nsCSPContext : public nsIContentSecurityPolicy
virtual ~nsCSPContext();
private:
NS_IMETHODIMP getAllowsInternal(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword,
const nsAString& aNonceOrContent,
bool* outShouldReportViolations,
bool* outIsAllowed) const;
nsTArray<nsCSPPolicy*> mPolicies;
nsCOMPtr<nsIURI> mSelfURI;
};

View File

@ -715,6 +715,13 @@ nsCSPPolicy::permits(nsContentPolicyType aContentType,
}
}
// If [frame-ancestors] is not listed explicitly then default to true
// without consulting [default-src]
// TODO: currently [frame-ancestors] is mapped to TYPE_DOCUMENT (needs to be fixed)
if (aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
return true;
}
// If the above loop runs through, we haven't found a matching directive.
// Avoid relooping, just store the result of default-src while looping.
if (defaultDir) {