Bug 1731982 - Part 7: Use the client's principal to verify client in localStorage. r=asuth

Currently, we use the script principal to verify the client to prevent
the principal forgery. After we moving to use foreign partitioned
principal for the Client, this no longer works. Instead, we can directly
use the client's principal to verify client.

Also, the patch rename StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo() to
StoragePrincipalHelper::VerifyValidPartitionedPrincipalInfoForPrincipalInfo()
and fix a problem in the function that it should ignore the PartitionKey
instead of the FirstPartyDomain.

Differential Revision: https://phabricator.services.mozilla.com/D127840
This commit is contained in:
Tim Huang 2021-10-15 20:00:04 +00:00
Родитель d96cda46f1
Коммит ebbb23509f
6 изменённых файлов: 172 добавлений и 90 удалений

Просмотреть файл

@ -3056,16 +3056,31 @@ void ForceKillAllDatabases() {
} }
bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo, bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
const PrincipalInfo& aStoragePrincipalInfo) { const PrincipalInfo& aStoragePrincipalInfo,
bool aCheckClientPrincipal) {
AssertIsOnBackgroundThread(); AssertIsOnBackgroundThread();
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) { if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
return false; return false;
} }
if (NS_WARN_IF(!StoragePrincipalHelper:: // Note that the client prinicpal could have a different spec than the node
VerifyValidStoragePrincipalInfoForPrincipalInfo( // principal but they should have the same origin. It's because the client
aStoragePrincipalInfo, aPrincipalInfo))) { // could be initialized when opening the initial about:blank document and pass
// to the newly opened window and reuse over there if the new window has the
// same origin as the initial about:blank document. But, the FilePath could be
// different. Therefore, we have to ignore comparing the Spec of the
// principals if we are verifying clinet principal here. Also, when
// document.domain is set, client principal won't get it. So, we don't compare
// domain for client princpal too.
bool result = aCheckClientPrincipal
? StoragePrincipalHelper::
VerifyValidClientPrincipalInfoForPrincipalInfo(
aStoragePrincipalInfo, aPrincipalInfo)
: StoragePrincipalHelper::
VerifyValidStoragePrincipalInfoForPrincipalInfo(
aStoragePrincipalInfo, aPrincipalInfo);
if (NS_WARN_IF(!result)) {
return false; return false;
} }
@ -3073,7 +3088,7 @@ bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
} }
bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId, bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
const PrincipalInfo& aPrincipalInfo, const Maybe<PrincipalInfo>& aPrincipalInfo,
const Maybe<nsID>& aClientId) { const Maybe<nsID>& aClientId) {
AssertIsOnBackgroundThread(); AssertIsOnBackgroundThread();
@ -3082,9 +3097,13 @@ bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
return false; return false;
} }
if (NS_WARN_IF(aPrincipalInfo.isNothing())) {
return false;
}
RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance(); RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
if (svc && NS_WARN_IF(!svc->HasWindow(aContentParentId, aPrincipalInfo, if (svc && NS_WARN_IF(!svc->HasWindow(
aClientId.ref()))) { aContentParentId, aPrincipalInfo.ref(), aClientId.ref()))) {
return false; return false;
} }
} }
@ -6114,8 +6133,8 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestCommonParams& params = const LSRequestCommonParams& params =
mParams.get_LSRequestPreloadDatastoreParams().commonParams(); mParams.get_LSRequestPreloadDatastoreParams().commonParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(), if (NS_WARN_IF(!VerifyPrincipalInfo(
params.storagePrincipalInfo()))) { params.principalInfo(), params.storagePrincipalInfo(), false))) {
return false; return false;
} }
@ -6133,14 +6152,21 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestCommonParams& commonParams = params.commonParams(); const LSRequestCommonParams& commonParams = params.commonParams();
if (NS_WARN_IF( if (NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
!VerifyPrincipalInfo(commonParams.principalInfo(), commonParams.storagePrincipalInfo(),
commonParams.storagePrincipalInfo()))) { false))) {
return false;
}
if (params.clientPrincipalInfo() &&
NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
params.clientPrincipalInfo().ref(),
true))) {
return false; return false;
} }
if (NS_WARN_IF(!VerifyClientId(mContentParentId, if (NS_WARN_IF(!VerifyClientId(mContentParentId,
commonParams.principalInfo(), params.clientPrincipalInfo(),
params.clientId()))) { params.clientId()))) {
return false; return false;
} }
@ -6157,12 +6183,20 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestPrepareObserverParams& params = const LSRequestPrepareObserverParams& params =
mParams.get_LSRequestPrepareObserverParams(); mParams.get_LSRequestPrepareObserverParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(), if (NS_WARN_IF(!VerifyPrincipalInfo(
params.storagePrincipalInfo()))) { params.principalInfo(), params.storagePrincipalInfo(), false))) {
return false; return false;
} }
if (NS_WARN_IF(!VerifyClientId(mContentParentId, params.principalInfo(), if (params.clientPrincipalInfo() &&
NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
params.clientPrincipalInfo().ref(),
true))) {
return false;
}
if (NS_WARN_IF(!VerifyClientId(mContentParentId,
params.clientPrincipalInfo(),
params.clientId()))) { params.clientId()))) {
return false; return false;
} }
@ -7782,8 +7816,8 @@ bool LSSimpleRequestBase::VerifyRequestParams() {
const LSSimpleRequestPreloadedParams& params = const LSSimpleRequestPreloadedParams& params =
mParams.get_LSSimpleRequestPreloadedParams(); mParams.get_LSSimpleRequestPreloadedParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(), if (NS_WARN_IF(!VerifyPrincipalInfo(
params.storagePrincipalInfo()))) { params.principalInfo(), params.storagePrincipalInfo(), false))) {
return false; return false;
} }

Просмотреть файл

@ -35,7 +35,6 @@
#include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsDebug.h" #include "nsDebug.h"
@ -363,6 +362,9 @@ nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
Maybe<nsID> clientId = Some(clientInfo.ref().Id()); Maybe<nsID> clientId = Some(clientInfo.ref().Id());
Maybe<PrincipalInfo> clientPrincipalInfo =
Some(clientInfo.ref().PrincipalInfo());
nsString documentURI; nsString documentURI;
if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) { if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
rv = doc->GetDocumentURI(documentURI); rv = doc->GetDocumentURI(documentURI);
@ -376,6 +378,7 @@ nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
object->mStoragePrincipalInfo = std::move(storagePrincipalInfo); object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
object->mPrivateBrowsingId = privateBrowsingId; object->mPrivateBrowsingId = privateBrowsingId;
object->mClientId = clientId; object->mClientId = clientId;
object->mClientPrincipalInfo = clientPrincipalInfo;
object->mOrigin = origin; object->mOrigin = origin;
object->mOriginKey = originKey; object->mOriginKey = originKey;
object->mDocumentURI = documentURI; object->mDocumentURI = documentURI;
@ -932,6 +935,7 @@ nsresult LSObject::EnsureDatabase() {
LSRequestPrepareDatastoreParams params; LSRequestPrepareDatastoreParams params;
params.commonParams() = commonParams; params.commonParams() = commonParams;
params.clientId() = mClientId; params.clientId() = mClientId;
params.clientPrincipalInfo() = mClientPrincipalInfo;
LSRequestResponse response; LSRequestResponse response;
@ -992,6 +996,7 @@ nsresult LSObject::EnsureObserver() {
params.principalInfo() = *mPrincipalInfo; params.principalInfo() = *mPrincipalInfo;
params.storagePrincipalInfo() = *mStoragePrincipalInfo; params.storagePrincipalInfo() = *mStoragePrincipalInfo;
params.clientId() = mClientId; params.clientId() = mClientId;
params.clientPrincipalInfo() = mClientPrincipalInfo;
LSRequestResponse response; LSRequestResponse response;

Просмотреть файл

@ -15,6 +15,7 @@
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
#include "mozilla/dom/Storage.h" #include "mozilla/dom/Storage.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
#include "nsID.h" #include "nsID.h"
#include "nsISupports.h" #include "nsISupports.h"
@ -31,12 +32,6 @@ namespace mozilla {
class ErrorResult; class ErrorResult;
namespace ipc {
class PrincipalInfo;
} // namespace ipc
namespace dom { namespace dom {
class LSDatabase; class LSDatabase;
@ -82,6 +77,7 @@ class LSObject final : public Storage {
uint32_t mPrivateBrowsingId; uint32_t mPrivateBrowsingId;
Maybe<nsID> mClientId; Maybe<nsID> mClientId;
Maybe<PrincipalInfo> mClientPrincipalInfo;
nsCString mOrigin; nsCString mOrigin;
nsCString mOriginKey; nsCString mOriginKey;
nsString mDocumentURI; nsString mDocumentURI;

Просмотреть файл

@ -29,13 +29,30 @@ struct LSRequestPrepareDatastoreParams
{ {
LSRequestCommonParams commonParams; LSRequestCommonParams commonParams;
nsID? clientId; nsID? clientId;
PrincipalInfo? clientPrincipalInfo;
}; };
/**
* In order to validate the principal with the client, we need to provide an
* additional principalInfo for the client. The client is using the foreign
* principal, see StoragePrincipalHelper.h for details, which is different from
* the principalInfo. So, we need to pass the principalInfo from the client So
* that we can verify it with the given client Id.
*
* Note that the storagePrincipalInfo is used to access the right cookie jar
* according to the Storage Access. This is passed in order to access the
* correct local storage. Essentially, the storage principal and the client
* principal are using the PartitionKey in their OriginAttributes. But, the
* existence of the PartitionKey between them is depending on different
* conditions. Namely, the storage principal depends on the Storage Access but
* the client principal depends on whether it's in a third party.
*/
struct LSRequestPrepareObserverParams struct LSRequestPrepareObserverParams
{ {
PrincipalInfo principalInfo; PrincipalInfo principalInfo;
PrincipalInfo storagePrincipalInfo; PrincipalInfo storagePrincipalInfo;
nsID? clientId; nsID? clientId;
PrincipalInfo? clientPrincipalInfo;
}; };
union LSRequestParams union LSRequestParams

Просмотреть файл

@ -83,6 +83,82 @@ bool ChooseOriginAttributes(nsIChannel* aChannel, OriginAttributes& aAttrs,
return true; return true;
} }
bool VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
const ipc::PrincipalInfo& aPartitionedPrincipalInfo,
const ipc::PrincipalInfo& aPrincipalInfo,
bool aIgnoreSpecForContentPrincipal,
bool aIgnoreDomainForContentPrincipal) {
if (aPartitionedPrincipalInfo.type() != aPrincipalInfo.type()) {
return false;
}
if (aPartitionedPrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
const mozilla::ipc::ContentPrincipalInfo& spInfo =
aPartitionedPrincipalInfo.get_ContentPrincipalInfo();
const mozilla::ipc::ContentPrincipalInfo& pInfo =
aPrincipalInfo.get_ContentPrincipalInfo();
if (!spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs()) ||
spInfo.originNoSuffix() != pInfo.originNoSuffix() ||
(!aIgnoreSpecForContentPrincipal && spInfo.spec() != pInfo.spec()) ||
(!aIgnoreDomainForContentPrincipal &&
spInfo.domain() != pInfo.domain()) ||
spInfo.baseDomain() != pInfo.baseDomain()) {
return false;
}
return true;
}
if (aPartitionedPrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
// Nothing to check here.
return true;
}
if (aPartitionedPrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
const mozilla::ipc::NullPrincipalInfo& spInfo =
aPartitionedPrincipalInfo.get_NullPrincipalInfo();
const mozilla::ipc::NullPrincipalInfo& pInfo =
aPrincipalInfo.get_NullPrincipalInfo();
return spInfo.spec() == pInfo.spec() &&
spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs());
}
if (aPartitionedPrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo) {
const mozilla::ipc::ExpandedPrincipalInfo& spInfo =
aPartitionedPrincipalInfo.get_ExpandedPrincipalInfo();
const mozilla::ipc::ExpandedPrincipalInfo& pInfo =
aPrincipalInfo.get_ExpandedPrincipalInfo();
if (!spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs())) {
return false;
}
if (spInfo.allowlist().Length() != pInfo.allowlist().Length()) {
return false;
}
for (uint32_t i = 0; i < spInfo.allowlist().Length(); ++i) {
if (!VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
spInfo.allowlist()[i], pInfo.allowlist()[i],
aIgnoreSpecForContentPrincipal,
aIgnoreDomainForContentPrincipal)) {
return false;
}
}
return true;
}
MOZ_CRASH("Invalid principalInfo type");
return false;
}
} // namespace } // namespace
// static // static
@ -156,73 +232,18 @@ StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
// static // static
bool StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo( bool StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo, const mozilla::ipc::PrincipalInfo& aPartitionedPrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
if (aStoragePrincipalInfo.type() != aPrincipalInfo.type()) { return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
return false; aPartitionedPrincipalInfo, aPrincipalInfo, false, false);
} }
if (aStoragePrincipalInfo.type() == // static
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) { bool StoragePrincipalHelper::VerifyValidClientPrincipalInfoForPrincipalInfo(
const mozilla::ipc::ContentPrincipalInfo& spInfo = const mozilla::ipc::PrincipalInfo& aPartitionedPrincipalInfo,
aStoragePrincipalInfo.get_ContentPrincipalInfo(); const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
const mozilla::ipc::ContentPrincipalInfo& pInfo = return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
aPrincipalInfo.get_ContentPrincipalInfo(); aPartitionedPrincipalInfo, aPrincipalInfo, true, true);
if (!spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs()) ||
spInfo.originNoSuffix() != pInfo.originNoSuffix() ||
spInfo.spec() != pInfo.spec() || spInfo.domain() != pInfo.domain() ||
spInfo.baseDomain() != pInfo.baseDomain()) {
return false;
}
return true;
}
if (aStoragePrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
// Nothing to check here.
return true;
}
if (aStoragePrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
const mozilla::ipc::NullPrincipalInfo& spInfo =
aStoragePrincipalInfo.get_NullPrincipalInfo();
const mozilla::ipc::NullPrincipalInfo& pInfo =
aPrincipalInfo.get_NullPrincipalInfo();
return spInfo.spec() == pInfo.spec() &&
spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs());
}
if (aStoragePrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo) {
const mozilla::ipc::ExpandedPrincipalInfo& spInfo =
aStoragePrincipalInfo.get_ExpandedPrincipalInfo();
const mozilla::ipc::ExpandedPrincipalInfo& pInfo =
aPrincipalInfo.get_ExpandedPrincipalInfo();
if (!spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs())) {
return false;
}
if (spInfo.allowlist().Length() != pInfo.allowlist().Length()) {
return false;
}
for (uint32_t i = 0; i < spInfo.allowlist().Length(); ++i) {
if (!VerifyValidStoragePrincipalInfoForPrincipalInfo(
spInfo.allowlist()[i], pInfo.allowlist()[i])) {
return false;
}
}
return true;
}
MOZ_CRASH("Invalid principalInfo type");
return false;
} }
// static // static

Просмотреть файл

@ -242,10 +242,19 @@ class StoragePrincipalHelper final {
static nsresult PrepareEffectiveStoragePrincipalOriginAttributes( static nsresult PrepareEffectiveStoragePrincipalOriginAttributes(
nsIChannel* aChannel, OriginAttributes& aOriginAttributes); nsIChannel* aChannel, OriginAttributes& aOriginAttributes);
// A helper function to verify storage principal info with the principal info.
static bool VerifyValidStoragePrincipalInfoForPrincipalInfo( static bool VerifyValidStoragePrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo, const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo); const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
// A helper function to verify client principal info with the principal info.
//
// Note that the client principal refers the principal of the client, which is
// supposed to be the foreign partitioned principal.
static bool VerifyValidClientPrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aClientPrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
enum PrincipalType { enum PrincipalType {
// This is the first-party principal. // This is the first-party principal.
eRegularPrincipal, eRegularPrincipal,