Files
cromite/build/patches/Partition-blobs-by-top-frame-URL.patch

440 lines
21 KiB
Diff

From: uazo <uazo@users.noreply.github.com>
Date: Tue, 20 Sep 2022 07:20:01 +0000
Subject: Partition blobs by top frame URL
Verifies that the blob was created with the same top frame URL
or, if not defined, by the same agent cluster.
Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
---
.../renderer_host/render_frame_host_impl.cc | 2 +
.../Partition-blobs-by-top-frame-URL.inc | 1 +
storage/browser/blob/blob_url_registry.cc | 31 +++++++++-
storage/browser/blob/blob_url_registry.h | 11 +++-
storage/browser/blob/blob_url_store_impl.cc | 62 +++++++++++++++++--
storage/browser/blob/blob_url_store_impl.h | 15 ++++-
storage/browser/blob/features.cc | 1 +
.../public/mojom/blob/blob_url_store.mojom | 13 +++-
.../core/fileapi/public_url_manager.cc | 37 ++++++++++-
9 files changed, 158 insertions(+), 15 deletions(-)
create mode 100644 cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -13524,6 +13524,8 @@ void RenderFrameHostImpl::ReportBlockingCrossPartitionBlobURL(
}
bool RenderFrameHostImpl::IsFullCookieAccessAllowed() {
+ // 6313600: [Blob URL] Allow contexts with a StorageAccessHandle
+ // to bypass Blob URL partitioning | https://chromium-review.googlesource.com/c/chromium/src/+/6313600
return GetContentClient()->browser()->IsFullCookieAccessAllowed(
GetBrowserContext(), WebContents::FromRenderFrameHost(this),
GetLastCommittedURL(), GetStorageKey(), GetCookieSettingOverrides());
diff --git a/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc b/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
new file mode 100644
--- /dev/null
+++ b/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
@@ -0,0 +1 @@
+SET_CROMITE_FEATURE_ENABLED(kEnforceNoopenerOnBlobURLNavigation);
diff --git a/storage/browser/blob/blob_url_registry.cc b/storage/browser/blob/blob_url_registry.cc
--- a/storage/browser/blob/blob_url_registry.cc
+++ b/storage/browser/blob/blob_url_registry.cc
@@ -93,13 +93,18 @@ bool BlobUrlRegistry::AddUrlMapping(
mojo::PendingRemote<blink::mojom::Blob> blob,
const blink::StorageKey& storage_key,
const url::Origin& renderer_origin,
- int render_process_host_id) {
+ int render_process_host_id,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!BlobUrlUtils::UrlHasFragment(blob_url));
if (IsUrlMapped(blob_url, storage_key) ==
BlobUrlRegistry::MappingStatus::kIsMapped) {
return false;
}
+ url_to_unsafe_agent_cluster_id_[blob_url] = unsafe_agent_cluster_id;
+ if (unsafe_top_level_site)
+ url_to_unsafe_top_level_site_[blob_url] = *unsafe_top_level_site;
BlobUrlData data;
data.blob = std::move(blob);
data.storage_key = storage_key;
@@ -121,6 +126,8 @@ bool BlobUrlRegistry::RemoveUrlMapping(const GURL& blob_url,
return false;
}
url_to_data_.erase(data_it);
+ url_to_unsafe_agent_cluster_id_.erase(blob_url);
+ url_to_unsafe_top_level_site_.erase(blob_url);
return true;
}
@@ -184,6 +191,28 @@ BlobUrlRegistry::MappingStatus BlobUrlRegistry::IsUrlMapped(
return BlobUrlRegistry::MappingStatus::kNotMappedOther;
}
+std::optional<base::UnguessableToken> BlobUrlRegistry::GetUnsafeAgentClusterID(
+ const GURL& blob_url) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto it = url_to_unsafe_agent_cluster_id_.find(blob_url);
+ if (it != url_to_unsafe_agent_cluster_id_.end())
+ return it->second;
+ if (fallback_)
+ return fallback_->GetUnsafeAgentClusterID(blob_url);
+ return std::nullopt;
+}
+
+std::optional<net::SchemefulSite> BlobUrlRegistry::GetUnsafeTopLevelSite(
+ const GURL& blob_url) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto it = url_to_unsafe_top_level_site_.find(blob_url);
+ if (it != url_to_unsafe_top_level_site_.end())
+ return it->second;
+ if (fallback_)
+ return fallback_->GetUnsafeTopLevelSite(blob_url);
+ return std::nullopt;
+}
+
mojo::PendingRemote<blink::mojom::Blob> BlobUrlRegistry::GetBlobFromUrl(
const GURL& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/storage/browser/blob/blob_url_registry.h b/storage/browser/blob/blob_url_registry.h
--- a/storage/browser/blob/blob_url_registry.h
+++ b/storage/browser/blob/blob_url_registry.h
@@ -96,7 +96,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry {
mojo::PendingRemote<blink::mojom::Blob> blob,
const blink::StorageKey& storage_key,
const url::Origin& renderer_origin,
- int render_process_host_id);
+ int render_process_host_id,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site);
// Removes the given URL mapping associated with `storage_key`. Returns false
// if the URL wasn't mapped.
@@ -107,6 +109,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry {
MappingStatus IsUrlMapped(const GURL& blob_url,
const blink::StorageKey& storage_key) const;
+ std::optional<base::UnguessableToken> GetUnsafeAgentClusterID(
+ const GURL& blob_url) const;
+ std::optional<net::SchemefulSite> GetUnsafeTopLevelSite(
+ const GURL& blob_url) const;
+
// Returns the blob from the given url. Returns a null remote if the mapping
// doesn't exist.
mojo::PendingRemote<blink::mojom::Blob> GetBlobFromUrl(const GURL& url);
@@ -162,6 +169,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobUrlRegistry {
};
std::map<GURL, BlobUrlData> url_to_data_;
+ std::map<GURL, base::UnguessableToken> url_to_unsafe_agent_cluster_id_;
+ std::map<GURL, net::SchemefulSite> url_to_unsafe_top_level_site_;
std::map<base::UnguessableToken,
std::pair<GURL, mojo::PendingRemote<blink::mojom::Blob>>>
token_to_url_and_blob_;
diff --git a/storage/browser/blob/blob_url_store_impl.cc b/storage/browser/blob/blob_url_store_impl.cc
--- a/storage/browser/blob/blob_url_store_impl.cc
+++ b/storage/browser/blob/blob_url_store_impl.cc
@@ -115,9 +115,35 @@ BlobURLStoreImpl::~BlobURLStoreImpl() {
}
}
+bool BlobURLStoreImpl::IsSamePartition(
+ const GURL& blob_url,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
+ bool is_same_partition = false;
+ const std::optional<net::SchemefulSite>& top_level_site =
+ registry_->GetUnsafeTopLevelSite(blob_url);
+ const std::optional<base::UnguessableToken> agent_cluster_id =
+ registry_->GetUnsafeAgentClusterID(blob_url);
+ if (top_level_site.has_value()) {
+ is_same_partition = (top_level_site == unsafe_top_level_site);
+ } else {
+ is_same_partition = (agent_cluster_id == unsafe_agent_cluster_id);
+ }
+ // LOG(INFO) << "---BlobURLStoreImpl "
+ // << " is_same_partition=" << is_same_partition
+ // << " blob_url=" << blob_url
+ // << " top_level_site=" << (top_level_site.has_value() ? top_level_site->GetDebugString() : "<none>")
+ // << " unsafe_top_level_site=" << (unsafe_top_level_site.has_value() ? unsafe_top_level_site->GetDebugString() : "<none>")
+ // << " agent_cluster_id=" << (agent_cluster_id.has_value() ? agent_cluster_id->ToString() : "<none>")
+ // << " unsafe_agent_cluster_id=" << unsafe_agent_cluster_id.ToString();
+ return is_same_partition;
+}
+
void BlobURLStoreImpl::Register(
mojo::PendingRemote<blink::mojom::Blob> blob,
const GURL& url,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
RegisterCallback callback) {
// TODO(crbug.com/40061399): Generate blob URLs here, rather than
// validating the URLs the renderer process generated.
@@ -125,10 +151,18 @@ void BlobURLStoreImpl::Register(
std::move(callback).Run();
return;
}
+ // LOG(INFO) << "---BlobURLStoreImpl Register"
+ // << " url=" << url
+ // << " storage_key_origin=" << storage_key_.GetDebugString()
+ // << " renderer_origin_=" << renderer_origin_
+ // << " render_process_host_id_=" << render_process_host_id_
+ // << " unsafe_top_level_site=" << (unsafe_top_level_site.has_value() ? unsafe_top_level_site->GetDebugString() : "<none>")
+ // << " unsafe_agent_cluster_id=" << unsafe_agent_cluster_id.ToString();
if (registry_)
registry_->AddUrlMapping(url, std::move(blob), storage_key_,
- renderer_origin_, render_process_host_id_);
+ renderer_origin_, render_process_host_id_,
+ unsafe_agent_cluster_id, unsafe_top_level_site);
urls_.insert(url);
std::move(callback).Run();
}
@@ -150,7 +184,7 @@ bool BlobURLStoreImpl::ShouldPartitionBlobUrlAccess(
features::kBlockCrossPartitionBlobUrlFetching) &&
!partitioning_disabled_by_policy_;
- const bool should_bypass_partitioning =
+ const bool should_bypass_partitioning = ((false)) &&
has_storage_access_handle &&
mapping_status ==
BlobUrlRegistry::MappingStatus::
@@ -160,7 +194,9 @@ bool BlobURLStoreImpl::ShouldPartitionBlobUrlAccess(
void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
const GURL& url,
- mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
if (!registry_) {
BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
return;
@@ -183,6 +219,7 @@ void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
if (IsBlobUrlAccessCrossPartitionSameOrigin(mapping_status)) {
if (ShouldPartitionBlobUrlAccess(has_storage_access_handle,
mapping_status)) {
+ // LOG(INFO) << "---ResolveAsURLLoaderFactory blocked" << url;
partitioning_blob_url_closure_.Run(
url, blink::mojom::PartitioningBlobURLInfo::
kBlockedCrossPartitionFetching);
@@ -194,6 +231,13 @@ void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
}
}
+ if (!IsSamePartition(url, unsafe_agent_cluster_id, unsafe_top_level_site)) {
+ // LOG(INFO) << "---ResolveAsURLLoaderFactory blocked by IsSamePartition" << url;
+ BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
+ //std::move(callback).Run(std::nullopt, std::nullopt);
+ return;
+ }
+ // LOG(INFO) << "---ResolveAsURLLoaderFactory allowed " << url;
BlobURLLoaderFactory::Create(registry_->GetBlobFromUrl(url), url,
std::move(receiver));
}
@@ -201,7 +245,9 @@ void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
void BlobURLStoreImpl::ResolveAsBlobURLToken(
const GURL& url,
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
- bool is_top_level_navigation) {
+ bool is_top_level_navigation,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
// This function is known to be heap allocation heavy and performance
// critical. Extra memory safety checks can introduce regression
// (https://crbug.com/414710225) and these are disabled here.
@@ -218,6 +264,7 @@ void BlobURLStoreImpl::ResolveAsBlobURLToken(
registry_->IsUrlMapped(BlobUrlUtils::ClearUrlFragment(url),
storage_key_);
if (IsBlobUrlAccessCrossPartitionSameOrigin(mapping_status)) {
+ // LOG(INFO) << "---ResolveAsBlobURLToken blocked" << url;
if (ShouldPartitionBlobUrlAccess(has_storage_access_handle,
mapping_status)) {
partitioning_blob_url_closure_.Run(
@@ -228,12 +275,17 @@ void BlobURLStoreImpl::ResolveAsBlobURLToken(
partitioning_blob_url_closure_.Run(url, std::nullopt);
}
}
+ if (!IsSamePartition(url, unsafe_agent_cluster_id, unsafe_top_level_site)) {
+ // LOG(INFO) << "---ResolveAsBlobURLToken blocked by IsSamePartition" << url;
+ //std::move(callback).Run(std::nullopt);
+ return;
+ }
mojo::PendingRemote<blink::mojom::Blob> blob = registry_->GetBlobFromUrl(url);
if (!blob) {
return;
}
-
+ // LOG(INFO) << "---ResolveAsBlobURLToken allowed " << url;
new BlobURLTokenImpl(registry_, url, std::move(blob), std::move(token));
}
diff --git a/storage/browser/blob/blob_url_store_impl.h b/storage/browser/blob/blob_url_store_impl.h
--- a/storage/browser/blob/blob_url_store_impl.h
+++ b/storage/browser/blob/blob_url_store_impl.h
@@ -55,16 +55,22 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl
void Register(
mojo::PendingRemote<blink::mojom::Blob> blob,
const GURL& url,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
RegisterCallback callback) override;
void Revoke(const GURL& url) override;
void ResolveAsURLLoaderFactory(
const GURL& url,
- mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
+ mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site)
override;
void ResolveAsBlobURLToken(
const GURL& url,
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
- bool is_top_level_navigation) override;
+ bool is_top_level_navigation,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) override;
private:
// Checks if the passed in url is a valid blob url for this blob url store.
@@ -77,6 +83,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl
bool has_storage_access_handle,
BlobUrlRegistry::MappingStatus mapping_status);
+ bool IsSamePartition(
+ const GURL& blob_url,
+ const base::UnguessableToken& unsafe_agent_cluster_id,
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site);
+
const blink::StorageKey storage_key_;
// The origin used by the worker/document associated with this BlobURLStore on
// the renderer side. This will almost always be the same as `storage_key_`'s
diff --git a/storage/browser/blob/features.cc b/storage/browser/blob/features.cc
--- a/storage/browser/blob/features.cc
+++ b/storage/browser/blob/features.cc
@@ -9,6 +9,7 @@ namespace features {
// Please keep features in alphabetical order.
BASE_FEATURE(kBlockCrossPartitionBlobUrlFetching,
base::FEATURE_ENABLED_BY_DEFAULT);
+SET_CROMITE_FEATURE_ENABLED(kBlockCrossPartitionBlobUrlFetching);
// Please keep features in alphabetical order.
diff --git a/third_party/blink/public/mojom/blob/blob_url_store.mojom b/third_party/blink/public/mojom/blob/blob_url_store.mojom
--- a/third_party/blink/public/mojom/blob/blob_url_store.mojom
+++ b/third_party/blink/public/mojom/blob/blob_url_store.mojom
@@ -4,6 +4,7 @@
module blink.mojom;
import "mojo/public/mojom/base/unguessable_token.mojom";
+import "services/network/public/mojom/schemeful_site.mojom";
import "services/network/public/mojom/url_loader_factory.mojom";
import "third_party/blink/public/mojom/blob/blob.mojom";
import "url/mojom/url.mojom";
@@ -19,7 +20,9 @@ interface BlobURLStore {
// new blob: URL rather than letting the caller in the renderer provide one.
[Sync] Register(
pending_remote<blink.mojom.Blob> blob,
- url.mojom.Url url) => ();
+ url.mojom.Url url,
+ mojo_base.mojom.UnguessableToken unsafe_agent_cluster_id,
+ network.mojom.SchemefulSite? unsafe_top_level_site) => ();
// Revokes a public Blob URL.
Revoke(url.mojom.Url url);
@@ -33,7 +36,9 @@ interface BlobURLStore {
// both the blob URL and all other references to the blob have been dropped.
ResolveAsURLLoaderFactory(
url.mojom.Url url,
- pending_receiver<network.mojom.URLLoaderFactory> factory);
+ pending_receiver<network.mojom.URLLoaderFactory> factory,
+ mojo_base.mojom.UnguessableToken unsafe_agent_cluster_id,
+ network.mojom.SchemefulSite? unsafe_top_level_site);
// Resolves a public Blob URL into a BlobURLToken. The BlobURLToken can be
// used by the browser process to securely look up the blob a URL used to
@@ -41,7 +46,9 @@ interface BlobURLStore {
// As long as the token is alive, the resolved blob will also be kept alive.
ResolveAsBlobURLToken(url.mojom.Url url,
pending_receiver<BlobURLToken> token,
- bool is_top_level_navigation);
+ bool is_top_level_navigation,
+ mojo_base.mojom.UnguessableToken unsafe_agent_cluster_id,
+ network.mojom.SchemefulSite? unsafe_top_level_site);
};
// A token representing a Blob URL. The browser process can use this to look up
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager.cc b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
--- a/third_party/blink/renderer/core/fileapi/public_url_manager.cc
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
@@ -61,6 +61,25 @@ static void RemoveFromNullOriginMapIfNecessary(const KURL& blob_url) {
BlobURLNullOriginMap::GetInstance()->Remove(blob_url);
}
+static std::optional<BlinkSchemefulSite> GetInsecureTopLevelSite(
+ ExecutionContext* execution_context) {
+ std::optional<BlinkSchemefulSite> top_level_site;
+ if (execution_context->IsWindow()) {
+ auto* window = To<LocalDOMWindow>(execution_context);
+ if (window->top() && window->top()->GetFrame()) {
+ top_level_site = BlinkSchemefulSite(window->top()
+ ->GetFrame()
+ ->GetSecurityContext()
+ ->GetSecurityOrigin());
+ }
+ } else if (auto* worker_global_scope =
+ DynamicTo<WorkerGlobalScope>(execution_context)) {
+ top_level_site = BlinkSchemefulSite(
+ worker_global_scope->top_level_frame_security_origin());
+ }
+ return top_level_site;
+}
+
} // namespace
PublicURLManager::PublicURLManager(ExecutionContext* execution_context)
@@ -158,7 +177,17 @@ String PublicURLManager::RegisterURL(URLRegistrable* registrable) {
mojo::PendingReceiver<mojom::blink::Blob> blob_receiver =
blob_remote.InitWithNewPipeAndPassReceiver();
- GetBlobURLStore().Register(std::move(blob_remote), url);
+ std::optional<BlinkSchemefulSite> top_level_site;
+ if (GetExecutionContext()->IsWindow()) {
+ auto* window = To<LocalDOMWindow>(GetExecutionContext());
+ if (window->top() && window->top()->GetFrame()) {
+ top_level_site = BlinkSchemefulSite(window->top()
+ ->GetFrame()
+ ->GetSecurityContext()
+ ->GetSecurityOrigin());
+ }
+ }
+ GetBlobURLStore().Register(std::move(blob_remote), url, GetExecutionContext()->GetAgentClusterID(), top_level_site);
mojo_urls_.insert(url_string);
registrable->CloneMojoBlob(std::move(blob_receiver));
@@ -208,7 +237,8 @@ void PublicURLManager::Resolve(
DCHECK(url.ProtocolIs("blob"));
- GetBlobURLStore().ResolveAsURLLoaderFactory(url, std::move(factory_receiver));
+ GetBlobURLStore().ResolveAsURLLoaderFactory(url, std::move(factory_receiver),
+ GetExecutionContext()->GetAgentClusterID(), GetInsecureTopLevelSite(GetExecutionContext()));
}
void PublicURLManager::ResolveAsBlobURLToken(
@@ -221,7 +251,8 @@ void PublicURLManager::ResolveAsBlobURLToken(
DCHECK(url.ProtocolIs("blob"));
GetBlobURLStore().ResolveAsBlobURLToken(url, std::move(token_receiver),
- is_top_level_navigation);
+ is_top_level_navigation,
+ GetExecutionContext()->GetAgentClusterID(), GetInsecureTopLevelSite(GetExecutionContext()));
}
void PublicURLManager::ContextDestroyed() {
--