gecko-dev/caps/nsJSPrincipals.cpp
Tom Ritter 117e232bdc Bug 1576254 - Add isSystemOrAddonPrincipal to JSPrincipal and nsJSPrincipals r=luke
Finally, here we add the virtual method isSystemOrAddonPrincipal to the
JSPrincipal object.

We also add it to nsJSPrincipal (where it has an easy implementation), and
to carry classes that are used by JS tests and the shell.

Differential Revision: https://phabricator.services.mozilla.com/D47477

--HG--
extra : moz-landing-system : lando
2019-10-04 17:37:36 +00:00

394 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "xpcpublic.h"
#include "nsString.h"
#include "nsIObjectOutputStream.h"
#include "nsIObjectInputStream.h"
#include "nsJSPrincipals.h"
#include "plstr.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsStringBuffer.h"
#include "mozilla/dom/StructuredCloneTags.h"
// for mozilla::dom::workerinternals::kJSPrincipalsDebugToken
#include "mozilla/dom/workerinternals/JSSettings.h"
// for mozilla::dom::worklet::kJSPrincipalsDebugToken
#include "mozilla/dom/WorkletPrincipals.h"
#include "mozilla/ipc/BackgroundUtils.h"
using namespace mozilla;
using namespace mozilla::ipc;
NS_IMETHODIMP_(MozExternalRefCountType)
nsJSPrincipals::AddRef() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(int32_t(refcount) >= 0, "illegal refcnt");
nsrefcnt count = ++refcount;
NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
return count;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsJSPrincipals::Release() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(0 != refcount, "dup release");
nsrefcnt count = --refcount;
NS_LOG_RELEASE(this, count, "nsJSPrincipals");
if (count == 0) {
delete this;
}
return count;
}
/* static */
bool nsJSPrincipals::Subsume(JSPrincipals* jsprin, JSPrincipals* other) {
bool result;
nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(
nsJSPrincipals::get(other), &result);
return NS_SUCCEEDED(rv) && result;
}
/* static */
void nsJSPrincipals::Destroy(JSPrincipals* jsprin) {
// The JS runtime can call this method during the last GC when
// nsScriptSecurityManager is destroyed. So we must not assume here that
// the security manager still exists.
nsJSPrincipals* nsjsprin = nsJSPrincipals::get(jsprin);
// We need to destroy the nsIPrincipal. We'll do this by adding
// to the refcount and calling release
#ifdef NS_BUILD_REFCNT_LOGGING
// The refcount logging considers AddRef-to-1 to indicate creation,
// so trick it into thinking it's otherwise, but balance the
// Release() we do below.
nsjsprin->refcount++;
nsjsprin->AddRef();
nsjsprin->refcount--;
#else
nsjsprin->refcount++;
#endif
nsjsprin->Release();
}
#ifdef DEBUG
// Defined here so one can do principals->dump() in the debugger
JS_PUBLIC_API void JSPrincipals::dump() {
if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
nsAutoCString str;
nsresult rv = static_cast<nsJSPrincipals*>(this)->GetScriptLocation(str);
fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this),
NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
} else if (debugToken == dom::workerinternals::kJSPrincipalsDebugToken) {
fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
} else if (debugToken == dom::WorkletPrincipals::kJSPrincipalsDebugToken) {
fprintf(stderr, "Web Worklet principal (%p)\n", this);
} else {
fprintf(stderr,
"!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
"actual=0x%x expected=0x%x\n",
this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
}
}
#endif
/* static */
bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
JSStructuredCloneReader* aReader,
JSPrincipals** aOutPrincipals) {
uint32_t tag;
uint32_t unused;
if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
return false;
}
if (!(tag == SCTAG_DOM_NULL_PRINCIPAL || tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
tag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
tag == SCTAG_DOM_WORKER_PRINCIPAL)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
}
static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
OriginAttributes& aAttrs, nsACString& aSpec,
nsACString& aOriginNoSuffix,
nsACString& aBaseDomain) {
uint32_t suffixLength, specLength;
if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
return false;
}
nsAutoCString suffix;
if (!suffix.SetLength(suffixLength, fallible)) {
return false;
}
if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
return false;
}
if (!aAttrs.PopulateFromSuffix(suffix)) {
return false;
}
if (!aSpec.SetLength(specLength, fallible)) {
return false;
}
if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
return false;
}
uint32_t originNoSuffixLength, dummy;
if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
return false;
}
MOZ_ASSERT(dummy == 0);
if (dummy != 0) {
return false;
}
if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
return false;
}
if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(),
originNoSuffixLength)) {
return false;
}
uint32_t baseDomainIsVoid, baseDomainLength;
if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
return false;
}
MOZ_ASSERT(baseDomainIsVoid == 0 || baseDomainIsVoid == 1);
if (baseDomainIsVoid) {
MOZ_ASSERT(baseDomainLength == 0);
aBaseDomain.SetIsVoid(true);
return true;
}
if (!aBaseDomain.SetLength(baseDomainLength, fallible)) {
return false;
}
if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) {
return false;
}
return true;
}
static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
PrincipalInfo& aInfo) {
if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
aInfo = SystemPrincipalInfo();
} else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
OriginAttributes attrs;
nsAutoCString spec;
nsAutoCString originNoSuffix;
nsAutoCString baseDomain;
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
return false;
}
aInfo = NullPrincipalInfo(attrs, spec);
} else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
uint32_t length, unused;
if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
return false;
}
ExpandedPrincipalInfo expanded;
for (uint32_t i = 0; i < length; i++) {
uint32_t tag;
if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
return false;
}
PrincipalInfo sub;
if (!ReadPrincipalInfo(aReader, tag, sub)) {
return false;
}
expanded.allowlist().AppendElement(sub);
}
aInfo = expanded;
} else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
OriginAttributes attrs;
nsAutoCString spec;
nsAutoCString originNoSuffix;
nsAutoCString baseDomain;
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
return false;
}
#ifdef FUZZING
if (originNoSuffix.IsEmpty()) {
return false;
}
#endif
MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
// XXX: Do we care about mDomain for structured clone?
aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
baseDomain);
} else {
#ifdef FUZZING
return false;
#else
MOZ_CRASH("unexpected principal structured clone tag");
#endif
}
return true;
}
static StaticRefPtr<nsIPrincipal> sActiveWorkerPrincipal;
nsJSPrincipals::AutoSetActiveWorkerPrincipal::AutoSetActiveWorkerPrincipal(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!sActiveWorkerPrincipal);
sActiveWorkerPrincipal = aPrincipal;
}
nsJSPrincipals::AutoSetActiveWorkerPrincipal::~AutoSetActiveWorkerPrincipal() {
sActiveWorkerPrincipal = nullptr;
}
/* static */
bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
JSStructuredCloneReader* aReader,
uint32_t aTag,
JSPrincipals** aOutPrincipals) {
MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
aTag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
aTag == SCTAG_DOM_WORKER_PRINCIPAL);
if (NS_WARN_IF(!NS_IsMainThread())) {
xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
return false;
}
if (aTag == SCTAG_DOM_WORKER_PRINCIPAL) {
// When reading principals which were written on a worker thread, we need to
// know the principal of the worker which did the write.
if (!sActiveWorkerPrincipal) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
RefPtr<nsJSPrincipals> retval = get(sActiveWorkerPrincipal);
retval.forget(aOutPrincipals);
return true;
}
PrincipalInfo info;
if (!ReadPrincipalInfo(aReader, aTag, info)) {
return false;
}
nsresult rv;
nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
*aOutPrincipals = get(prin.forget().take());
return true;
}
static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
const OriginAttributes& aAttrs,
const nsCString& aSpec,
const nsCString& aOriginNoSuffix,
const nsCString& aBaseDomain) {
nsAutoCString suffix;
aAttrs.CreateSuffix(suffix);
if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
aOriginNoSuffix.Length()))) {
return false;
}
if (aBaseDomain.IsVoid()) {
return JS_WriteUint32Pair(aWriter, 1, 0);
}
return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
}
/* static */
bool nsJSPrincipals::WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
const PrincipalInfo& aInfo) {
if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
const NullPrincipalInfo& nullInfo = aInfo;
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
::WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(),
EmptyCString(), EmptyCString());
}
if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
}
if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
const ExpandedPrincipalInfo& expanded = aInfo;
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
!JS_WriteUint32Pair(aWriter, expanded.allowlist().Length(), 0)) {
return false;
}
for (uint32_t i = 0; i < expanded.allowlist().Length(); i++) {
if (!WritePrincipalInfo(aWriter, expanded.allowlist()[i])) {
return false;
}
}
return true;
}
MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
const ContentPrincipalInfo& cInfo = aInfo;
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
::WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
cInfo.originNoSuffix(), cInfo.baseDomain());
}
bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
PrincipalInfo info;
if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
return WritePrincipalInfo(aWriter, info);
}
bool nsJSPrincipals::isSystemOrAddonPrincipal() {
return this->IsSystemPrincipal() || this->GetIsAddonOrExpandedAddonPrincipal();
}