From 439f2f56d312b7cb12968efd360308063b37e32e Mon Sep 17 00:00:00 2001 From: Tim Huang Date: Mon, 8 Nov 2021 14:30:55 +0000 Subject: [PATCH] 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 --- dom/localstorage/ActorsParent.cpp | 70 ++++++--- dom/localstorage/LSObject.cpp | 7 +- dom/localstorage/LSObject.h | 8 +- .../PBackgroundLSSharedTypes.ipdlh | 17 ++ .../antitracking/StoragePrincipalHelper.cpp | 147 ++++++++++-------- .../antitracking/StoragePrincipalHelper.h | 11 +- 6 files changed, 169 insertions(+), 91 deletions(-) diff --git a/dom/localstorage/ActorsParent.cpp b/dom/localstorage/ActorsParent.cpp index 0a4a36059a6b..d0dbaa1b0cb4 100644 --- a/dom/localstorage/ActorsParent.cpp +++ b/dom/localstorage/ActorsParent.cpp @@ -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& aContentParentId, - const PrincipalInfo& aPrincipalInfo, + const Maybe& aPrincipalInfo, const Maybe& aClientId) { AssertIsOnBackgroundThread(); @@ -3042,9 +3057,13 @@ bool VerifyClientId(const Maybe& aContentParentId, return false; } + if (NS_WARN_IF(aPrincipalInfo.isNothing())) { + return false; + } + RefPtr 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; } diff --git a/dom/localstorage/LSObject.cpp b/dom/localstorage/LSObject.cpp index 6c89bc177aae..9d0a9895d4e9 100644 --- a/dom/localstorage/LSObject.cpp +++ b/dom/localstorage/LSObject.cpp @@ -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 clientId = Some(clientInfo.ref().Id()); + Maybe clientPrincipalInfo = + Some(clientInfo.ref().PrincipalInfo()); + nsString documentURI; if (nsCOMPtr 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; diff --git a/dom/localstorage/LSObject.h b/dom/localstorage/LSObject.h index 451e83d43bd8..ebebc7f80383 100644 --- a/dom/localstorage/LSObject.h +++ b/dom/localstorage/LSObject.h @@ -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 mClientId; + Maybe mClientPrincipalInfo; nsCString mOrigin; nsCString mOriginKey; nsString mDocumentURI; diff --git a/dom/localstorage/PBackgroundLSSharedTypes.ipdlh b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh index e460ebaae033..5802cc821a9c 100644 --- a/dom/localstorage/PBackgroundLSSharedTypes.ipdlh +++ b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh @@ -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 diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp index d8ff06ff78c5..febe1e6fc1df 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp @@ -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 diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h index 5d9c02ad4fbc..e2b2ae768793 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.h +++ b/toolkit/components/antitracking/StoragePrincipalHelper.h @@ -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);