Bug 1731982 - Part 6: 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-11-25 13:11:31 +00:00
Родитель e65d0db962
Коммит 45bfad05fe
6 изменённых файлов: 169 добавлений и 91 удалений

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

@ -3016,16 +3016,31 @@ void ForceKillAllDatabases() {
}
bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
const PrincipalInfo& aStoragePrincipalInfo) {
const PrincipalInfo& aStoragePrincipalInfo,
bool aCheckClientPrincipal) {
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
return false;
}
if (NS_WARN_IF(!StoragePrincipalHelper::
VerifyValidStoragePrincipalInfoForPrincipalInfo(
aStoragePrincipalInfo, aPrincipalInfo))) {
// Note that the client prinicpal could have a different spec than the node
// principal but they should have the same origin. It's because the client
// 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;
}
@ -3033,7 +3048,7 @@ bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
}
bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
const PrincipalInfo& aPrincipalInfo,
const Maybe<PrincipalInfo>& aPrincipalInfo,
const Maybe<nsID>& aClientId) {
AssertIsOnBackgroundThread();
@ -3042,9 +3057,13 @@ bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
return false;
}
if (NS_WARN_IF(aPrincipalInfo.isNothing())) {
return false;
}
RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
if (svc && NS_WARN_IF(!svc->HasWindow(aContentParentId, aPrincipalInfo,
aClientId.ref()))) {
if (svc && NS_WARN_IF(!svc->HasWindow(
aContentParentId, aPrincipalInfo.ref(), aClientId.ref()))) {
return false;
}
}
@ -6074,8 +6093,8 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestCommonParams& params =
mParams.get_LSRequestPreloadDatastoreParams().commonParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
params.storagePrincipalInfo()))) {
if (NS_WARN_IF(!VerifyPrincipalInfo(
params.principalInfo(), params.storagePrincipalInfo(), false))) {
return false;
}
@ -6093,14 +6112,21 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestCommonParams& commonParams = params.commonParams();
if (NS_WARN_IF(
!VerifyPrincipalInfo(commonParams.principalInfo(),
commonParams.storagePrincipalInfo()))) {
if (NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
commonParams.storagePrincipalInfo(),
false))) {
return false;
}
if (params.clientPrincipalInfo() &&
NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
params.clientPrincipalInfo().ref(),
true))) {
return false;
}
if (NS_WARN_IF(!VerifyClientId(mContentParentId,
commonParams.principalInfo(),
params.clientPrincipalInfo(),
params.clientId()))) {
return false;
}
@ -6117,12 +6143,20 @@ bool LSRequestBase::VerifyRequestParams() {
const LSRequestPrepareObserverParams& params =
mParams.get_LSRequestPrepareObserverParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
params.storagePrincipalInfo()))) {
if (NS_WARN_IF(!VerifyPrincipalInfo(
params.principalInfo(), params.storagePrincipalInfo(), 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()))) {
return false;
}
@ -7751,8 +7785,8 @@ bool LSSimpleRequestBase::VerifyRequestParams() {
const LSSimpleRequestPreloadedParams& params =
mParams.get_LSSimpleRequestPreloadedParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
params.storagePrincipalInfo()))) {
if (NS_WARN_IF(!VerifyPrincipalInfo(
params.principalInfo(), params.storagePrincipalInfo(), false))) {
return false;
}

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

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

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

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

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

@ -29,13 +29,30 @@ struct LSRequestPrepareDatastoreParams
{
LSRequestCommonParams commonParams;
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
{
PrincipalInfo principalInfo;
PrincipalInfo storagePrincipalInfo;
nsID? clientId;
PrincipalInfo? clientPrincipalInfo;
};
union LSRequestParams

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

@ -84,6 +84,78 @@ bool ChooseOriginAttributes(nsIChannel* aChannel, OriginAttributes& aAttrs,
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();
return spInfo.attrs().EqualsIgnoringPartitionKey(pInfo.attrs()) &&
spInfo.originNoSuffix() == pInfo.originNoSuffix() &&
(aIgnoreSpecForContentPrincipal || spInfo.spec() == pInfo.spec()) &&
(aIgnoreDomainForContentPrincipal ||
spInfo.domain() == pInfo.domain()) &&
spInfo.baseDomain() == pInfo.baseDomain();
}
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
// static
@ -157,73 +229,18 @@ StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
// static
bool StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPartitionedPrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
if (aStoragePrincipalInfo.type() != aPrincipalInfo.type()) {
return false;
}
return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
aPartitionedPrincipalInfo, aPrincipalInfo, false, false);
}
if (aStoragePrincipalInfo.type() ==
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
const mozilla::ipc::ContentPrincipalInfo& spInfo =
aStoragePrincipalInfo.get_ContentPrincipalInfo();
const mozilla::ipc::ContentPrincipalInfo& pInfo =
aPrincipalInfo.get_ContentPrincipalInfo();
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
bool StoragePrincipalHelper::VerifyValidClientPrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aClientPrincipalInfo,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
return VerifyValidPartitionedPrincipalInfoForPrincipalInfoInternal(
aClientPrincipalInfo, aPrincipalInfo, true, true);
}
// static

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

@ -243,10 +243,19 @@ class StoragePrincipalHelper final {
static nsresult PrepareEffectiveStoragePrincipalOriginAttributes(
nsIChannel* aChannel, OriginAttributes& aOriginAttributes);
// A helper function to verify storage principal info with the principal info.
static bool VerifyValidStoragePrincipalInfoForPrincipalInfo(
const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
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 {
// This is the first-party principal.
eRegularPrincipal,
@ -308,7 +317,7 @@ class StoragePrincipalHelper final {
// These methods return the correct originAttributes to be used for network
// state components (HSTS, network cache, image-cache, and so on).
static bool GetOriginAttributesForNetworkState(nsIChannel* aChanel,
static bool GetOriginAttributesForNetworkState(nsIChannel* aChannel,
OriginAttributes& aAttributes);
static void GetOriginAttributesForNetworkState(dom::Document* aDocument,
OriginAttributes& aAttributes);