diff options
author | Ben Kelly <wanderview@chromium.org> | 2021-02-02 15:45:09 +0000 |
---|---|---|
committer | Michael BrĂ¼ning <michael.bruning@qt.io> | 2021-04-20 12:57:09 +0000 |
commit | 8783292e8cb9179403dd425476a35f3b6fbae7cd (patch) | |
tree | 6ad4f01bc2cd50bb33f5a8551f0581e108bccb0b | |
parent | 4e34cdf60a697a3831bea711e0f55af76e043fab (diff) |
[Backport] CVE-2021-21209: Inappropriate implementation in storage (3/5)
Manual backport of patch originally reviewed on
https://chromium-review.googlesource.com/c/chromium/src/+/2590076:
CacheStorage: Refactor opaque padding.
This CL refactors how we generate and store opaque response padding:
* Padding values are now generated immediately in fetch().
* Padding values are associated with the Response and follow it.
* Network loaded responses get a purely random pad.
* Http cache loaded responses get a hashed padding value.
* CacheStorage now stores padding values in each entry.
* CacheStorage entries with side data for code cache have a separate,
additional padding value added.
* Many additional tests.
Bug: 1143526
Change-Id: I40b094097b64be7bab8899acad8b9baffe304d33
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Ben Kelly <wanderview@chromium.org>
Cr-Commit-Position: refs/heads/master@{#849608}
Change-Id: I56d667a2f0ad266a3cd978c842e78e501eb79c60
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
31 files changed, 562 insertions, 289 deletions
diff --git a/chromium/content/browser/appcache/appcache_backfillers.cc b/chromium/content/browser/appcache/appcache_backfillers.cc index f3509a30e05..fbe0b5de0a3 100644 --- a/chromium/content/browser/appcache/appcache_backfillers.cc +++ b/chromium/content/browser/appcache/appcache_backfillers.cc @@ -7,7 +7,7 @@ #include "content/browser/appcache/appcache_update_job.h" #include "net/http/http_request_headers.h" #include "sql/statement.h" -#include "storage/browser/quota/padding_key.h" +#include "storage/common/quota/padding_key.h" #include "url/gurl.h" namespace content { @@ -18,9 +18,7 @@ int64_t ComputeEntryPaddingSize(std::string response_url, std::string manifest_url) { if (GURL(response_url).GetOrigin() == GURL(manifest_url).GetOrigin()) return 0; - return storage::ComputeResponsePadding( - response_url, storage::GetDefaultPaddingKey(), /*has_metadata=*/false, - /*loaded_with_credentials=*/false, net::HttpRequestHeaders::kGetMethod); + return storage::ComputeRandomResponsePadding(); } // Iterates over each Cache record; execute |callable| on each iteration. diff --git a/chromium/content/browser/appcache/appcache_database.cc b/chromium/content/browser/appcache/appcache_database.cc index e2148da8719..a8e52341824 100644 --- a/chromium/content/browser/appcache/appcache_database.cc +++ b/chromium/content/browser/appcache/appcache_database.cc @@ -19,7 +19,6 @@ #include "sql/meta_table.h" #include "sql/statement.h" #include "sql/transaction.h" -#include "storage/browser/quota/padding_key.h" #include "third_party/blink/public/common/features.h" namespace content { diff --git a/chromium/content/browser/appcache/appcache_update_job.cc b/chromium/content/browser/appcache/appcache_update_job.cc index c12a5b3adb7..61dbf4f0238 100644 --- a/chromium/content/browser/appcache/appcache_update_job.cc +++ b/chromium/content/browser/appcache/appcache_update_job.cc @@ -26,7 +26,7 @@ #include "net/base/net_errors.h" #include "net/base/request_priority.h" #include "net/http/http_request_headers.h" -#include "storage/browser/quota/padding_key.h" +#include "storage/common/quota/padding_key.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/appcache/appcache.mojom.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom.h" @@ -207,10 +207,7 @@ int64_t ComputeAppCacheResponsePadding(const GURL& response_url, if (response_url.GetOrigin() == manifest_url.GetOrigin()) return 0; - return storage::ComputeResponsePadding( - response_url.spec(), storage::GetDefaultPaddingKey(), - /*has_metadata=*/false, /*loaded_with_credentials=*/false, - net::HttpRequestHeaders::kGetMethod); + return storage::ComputeRandomResponsePadding(); } } // namespace diff --git a/chromium/content/browser/cache_storage/cache_storage.proto b/chromium/content/browser/cache_storage/cache_storage.proto index 14ad0f0e34f..245cc2bde73 100644 --- a/chromium/content/browser/cache_storage/cache_storage.proto +++ b/chromium/content/browser/cache_storage/cache_storage.proto @@ -56,6 +56,8 @@ message CacheResponse { optional bool was_fetched_via_spdy = 12; optional string mime_type = 13; optional string request_method = 14; + optional int64 padding = 15; + optional int64 side_data_padding = 16; } message CacheMetadata { diff --git a/chromium/content/browser/cache_storage/cache_storage_histogram_utils.h b/chromium/content/browser/cache_storage/cache_storage_histogram_utils.h index 031ddacc804..3fd75d694b8 100644 --- a/chromium/content/browser/cache_storage/cache_storage_histogram_utils.h +++ b/chromium/content/browser/cache_storage/cache_storage_histogram_utils.h @@ -37,7 +37,8 @@ enum class ErrorStorageType { kCreateBackendDidCreateFailed = 22, kStorageGetAllMatchedEntriesBackendClosed = 23, kStorageHandleNull = 24, - kMaxValue = kStorageHandleNull, + kWriteSideDataDidWriteMetadataWrongBytes = 25, + kMaxValue = kWriteSideDataDidWriteMetadataWrongBytes, }; blink::mojom::CacheStorageError MakeErrorStorage(ErrorStorageType type); diff --git a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage.cc b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage.cc index 28a928b65f6..48acfdb7a51 100644 --- a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage.cc +++ b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage.cc @@ -46,8 +46,8 @@ #include "net/base/directory_lister.h" #include "net/base/net_errors.h" #include "storage/browser/blob/blob_storage_context.h" -#include "storage/browser/quota/padding_key.h" #include "storage/browser/quota/quota_manager_proxy.h" +#include "storage/common/quota/padding_key.h" #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" using blink::mojom::CacheStorageError; diff --git a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc index c662b8af907..5613029b80c 100644 --- a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc +++ b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc @@ -53,8 +53,8 @@ #include "net/http/http_status_code.h" #include "services/network/public/mojom/fetch_api.mojom.h" #include "storage/browser/blob/blob_storage_context.h" -#include "storage/browser/quota/padding_key.h" #include "storage/browser/quota/quota_manager_proxy.h" +#include "storage/common/quota/padding_key.h" #include "third_party/blink/public/common/cache_storage/cache_storage_utils.h" #include "third_party/blink/public/common/fetch/fetch_api_request_headers_map.h" #include "third_party/blink/public/mojom/loader/referrer.mojom.h" @@ -79,7 +79,8 @@ const size_t kMaxQueryCacheResultBytes = // // 1: Uniform random 400K. // 2: Uniform random 14,431K. -const int32_t kCachePaddingAlgorithmVersion = 2; +// 3: FetchAPIResponse.padding and separate side data padding. +const int32_t kCachePaddingAlgorithmVersion = 3; // Maximum number of recursive QueryCacheOpenNextEntry() calls we permit // before forcing an asynchronous task. @@ -374,6 +375,14 @@ void ReadMetadataDidReadMetadata(disk_cache::Entry* entry, std::move(callback).Run(std::move(metadata)); } +bool ShouldPadResourceSize(const content::proto::CacheResponse* response) { + return storage::ShouldPadResponseType( + ProtoResponseTypeToFetchResponseType(response->response_type())); +} +bool ShouldPadResourceSize(const blink::mojom::FetchAPIResponse& response) { + return storage::ShouldPadResponseType(response.response_type); +} + blink::mojom::FetchAPIRequestPtr CreateRequest( const proto::CacheMetadata& metadata, const GURL& request_url) { @@ -394,6 +403,7 @@ blink::mojom::FetchAPIRequestPtr CreateRequest( } blink::mojom::FetchAPIResponsePtr CreateResponse( + const url::Origin& origin, const proto::CacheMetadata& metadata, const std::string& cache_name) { // We no longer support Responses with only a single URL entry. This field @@ -427,6 +437,15 @@ blink::mojom::FetchAPIResponsePtr CreateResponse( if (metadata.response().has_request_method()) request_method = metadata.response().request_method(); + auto response_time = + base::Time::FromInternalValue(metadata.response().response_time()); + int64_t padding = 0; + if (metadata.response().has_padding()) { + padding = metadata.response().padding(); + } else if (ShouldPadResourceSize(&metadata.response())) { + padding = storage::ComputeRandomResponsePadding(); + } + // Note that |has_range_requested| can be safely set to false since it only // affects HTTP 206 (Partial) responses, which are blocked from cache storage. // See https://fetch.spec.whatwg.org/#main-fetch for usage of @@ -435,10 +454,9 @@ blink::mojom::FetchAPIResponsePtr CreateResponse( url_list, metadata.response().status_code(), metadata.response().status_text(), ProtoResponseTypeToFetchResponseType(metadata.response().response_type()), - network::mojom::FetchResponseSource::kCacheStorage, headers, mime_type, - request_method, nullptr /* blob */, - blink::mojom::ServiceWorkerResponseError::kUnknown, - base::Time::FromInternalValue(metadata.response().response_time()), + padding, network::mojom::FetchResponseSource::kCacheStorage, headers, + mime_type, request_method, /*blob=*/nullptr, + blink::mojom::ServiceWorkerResponseError::kUnknown, response_time, cache_name, std::vector<std::string>( metadata.response().cors_exposed_header_names().begin(), @@ -450,53 +468,27 @@ blink::mojom::FetchAPIResponsePtr CreateResponse( metadata.response().connection_info()), alpn_negotiated_protocol, metadata.response().loaded_with_credentials(), metadata.response().was_fetched_via_spdy(), - /* has_range_requested */ false); + /*has_range_requested=*/false); } - -// The size of opaque (non-cors) resource responses are padded in order -// to obfuscate their actual size. -bool ShouldPadResponseType(network::mojom::FetchResponseType response_type, - bool has_urls) { - switch (response_type) { - case network::mojom::FetchResponseType::kBasic: - case network::mojom::FetchResponseType::kCors: - case network::mojom::FetchResponseType::kDefault: - case network::mojom::FetchResponseType::kError: - return false; - case network::mojom::FetchResponseType::kOpaque: - case network::mojom::FetchResponseType::kOpaqueRedirect: - return has_urls; - } - NOTREACHED(); - return false; -} - -bool ShouldPadResourceSize(const content::proto::CacheResponse* response) { - return ShouldPadResponseType( - ProtoResponseTypeToFetchResponseType(response->response_type()), - response->url_list_size()); -} - -bool ShouldPadResourceSize(const blink::mojom::FetchAPIResponse& response) { - return ShouldPadResponseType(response.response_type, - !response.url_list.empty()); -} - -int64_t CalculateResponsePaddingInternal( +int64_t CalculateSideDataPadding( + const url::Origin& origin, const ::content::proto::CacheResponse* response, - const crypto::SymmetricKey* padding_key, int side_data_size) { DCHECK(ShouldPadResourceSize(response)); DCHECK_GE(side_data_size, 0); + + if (!side_data_size) + return 0; + // Fallback to random padding if this is for an older entry without + // a url list or request method. + if (response->url_list_size() == 0 || !response->has_request_method()) + return storage::ComputeRandomResponsePadding(); + const std::string& url = response->url_list(response->url_list_size() - 1); - bool loaded_with_credentials = response->has_loaded_with_credentials() && - response->loaded_with_credentials(); - const std::string& request_method = response->has_request_method() - ? response->request_method() - : net::HttpRequestHeaders::kGetMethod; - return storage::ComputeResponsePadding(url, padding_key, side_data_size > 0, - loaded_with_credentials, - request_method); + const base::Time response_time = + base::Time::FromInternalValue(response->response_time()); + return storage::ComputeStableResponsePadding( + origin, url, response_time, response->request_method(), side_data_size); } net::RequestPriority GetDiskCachePriority( @@ -508,12 +500,19 @@ net::RequestPriority GetDiskCachePriority( } // namespace struct LegacyCacheStorageCache::QueryCacheResult { - explicit QueryCacheResult(base::Time entry_time) : entry_time(entry_time) {} + QueryCacheResult(base::Time entry_time, + int64_t padding, + int64_t side_data_padding) + : entry_time(entry_time), + padding(padding), + side_data_padding(side_data_padding) {} blink::mojom::FetchAPIRequestPtr request; blink::mojom::FetchAPIResponsePtr response; disk_cache::ScopedEntryPtr entry; base::Time entry_time; + int64_t padding = 0; + int64_t side_data_padding = 0; }; struct LegacyCacheStorageCache::QueryCacheContext { @@ -1248,17 +1247,40 @@ void LegacyCacheStorageCache::QueryCacheDidReadMetadata( return; } + // Check for older cache entries that need to be padded, but don't + // have any padding stored in the entry. Upgrade these entries + // as we encounter them. This method will be re-entered once the + // new paddings are written back to disk. + if (ShouldPadResourceSize(&metadata->response()) && + !metadata->response().has_padding()) { + QueryCacheUpgradePadding(std::move(query_cache_context), std::move(entry), + std::move(metadata)); + return; + } + // If the entry was created before we started adding entry times, then // default to using the Response object's time for sorting purposes. int64_t entry_time = metadata->has_entry_time() ? metadata->entry_time() : metadata->response().response_time(); - query_cache_context->matches->push_back( - QueryCacheResult(base::Time::FromInternalValue(entry_time))); + // Note, older entries that don't require padding may still not have + // a padding value since we don't pay the cost to upgrade these entries. + // Treat these as a zero padding. + int64_t padding = + metadata->response().has_padding() ? metadata->response().padding() : 0; + int64_t side_data_padding = metadata->response().has_side_data_padding() + ? metadata->response().side_data_padding() + : 0; + + DCHECK(!ShouldPadResourceSize(&metadata->response()) || + (padding + side_data_padding)); + + query_cache_context->matches->push_back(QueryCacheResult( + base::Time::FromInternalValue(entry_time), padding, side_data_padding)); QueryCacheResult* match = &query_cache_context->matches->back(); match->request = CreateRequest(*metadata, GURL(entry->GetKey())); - match->response = CreateResponse(*metadata, cache_name_); + match->response = CreateResponse(origin_, *metadata, cache_name_); if (!match->response) { entry->Doom(); @@ -1320,6 +1342,49 @@ void LegacyCacheStorageCache::QueryCacheDidReadMetadata( QueryCacheOpenNextEntry(std::move(query_cache_context)); } +void LegacyCacheStorageCache::QueryCacheUpgradePadding( + std::unique_ptr<QueryCacheContext> query_cache_context, + disk_cache::ScopedEntryPtr entry, + std::unique_ptr<proto::CacheMetadata> metadata) { + DCHECK(ShouldPadResourceSize(&metadata->response())); + // This should only be called while initializing because the padding + // version change should trigger an immediate query of all resources + // to recompute padding. + DCHECK(initializing_); + auto* response = metadata->mutable_response(); + response->set_padding(storage::ComputeRandomResponsePadding()); + response->set_side_data_padding(CalculateSideDataPadding( + origin_, response, entry->GetDataSize(INDEX_SIDE_DATA))); + // Get a temporary copy of the entry and metadata pointers before moving them + // into base::BindOnce. + disk_cache::Entry* temp_entry_ptr = entry.get(); + auto* temp_metadata_ptr = metadata.get(); + WriteMetadata( + temp_entry_ptr, *temp_metadata_ptr, + base::BindOnce( + [](base::WeakPtr<LegacyCacheStorageCache> self, + std::unique_ptr<QueryCacheContext> query_cache_context, + disk_cache::ScopedEntryPtr entry, + std::unique_ptr<proto::CacheMetadata> metadata, int expected_bytes, + int rv) { + if (!self) + return; + if (expected_bytes != rv) { + entry->Doom(); + self->QueryCacheOpenNextEntry(std::move(query_cache_context)); + return; + } + // We must have a padding here in order to avoid infinite + // recursion. + DCHECK(metadata->response().has_padding()); + self->QueryCacheDidReadMetadata(std::move(query_cache_context), + std::move(entry), + std::move(metadata)); + }, + weak_ptr_factory_.GetWeakPtr(), std::move(query_cache_context), + std::move(entry), std::move(metadata))); +} + // static bool LegacyCacheStorageCache::QueryCacheResultCompare( const QueryCacheResult& lhs, @@ -1346,26 +1411,6 @@ size_t LegacyCacheStorageCache::EstimatedResponseSizeWithoutBlob( } // static -int64_t LegacyCacheStorageCache::CalculateResponsePadding( - const blink::mojom::FetchAPIResponse& response, - const crypto::SymmetricKey* padding_key, - int side_data_size) { - DCHECK_GE(side_data_size, 0); - if (!ShouldPadResourceSize(response)) - return 0; - // Going forward we should always have a request method here since its - // impossible to create a no-cors Response via the constructor. We must - // handle a missing method, however, since we may get a Response loaded - // from an old cache_storage instance without the data. - std::string request_method = response.request_method.has_value() - ? response.request_method.value() - : net::HttpRequestHeaders::kGetMethod; - return storage::ComputeResponsePadding( - response.url_list.back().spec(), padding_key, side_data_size > 0, - response.loaded_with_credentials, request_method); -} - -// static int32_t LegacyCacheStorageCache::GetResponsePaddingVersion() { return kCachePaddingAlgorithmVersion; } @@ -1608,19 +1653,13 @@ void LegacyCacheStorageCache::WriteSideDataDidReadMetaData( if (!headers || headers->response().response_time() != expected_response_time.ToInternalValue()) { WriteSideDataComplete(std::move(callback), std::move(entry), + /*padding=*/0, /*side_data_padding=*/0, CacheStorageError::kErrorNotFound); return; } // Get a temporary copy of the entry pointer before passing it in base::Bind. disk_cache::Entry* temp_entry_ptr = entry.get(); - std::unique_ptr<content::proto::CacheResponse> response( - headers->release_response()); - - int side_data_size_before_write = 0; - if (ShouldPadResourceSize(response.get())) - side_data_size_before_write = entry->GetDataSize(INDEX_SIDE_DATA); - // Create a callback that is copyable, even though it can only be called once. // BindRepeating() cannot be used directly because |callback|, |entry| and // |response| are not copyable. @@ -1628,7 +1667,7 @@ void LegacyCacheStorageCache::WriteSideDataDidReadMetaData( base::AdaptCallbackForRepeating(base::BindOnce( &LegacyCacheStorageCache::WriteSideDataDidWrite, weak_ptr_factory_.GetWeakPtr(), std::move(callback), std::move(entry), - buf_len, std::move(response), side_data_size_before_write, trace_id)); + buf_len, std::move(headers), trace_id)); DCHECK(scheduler_->IsRunningExclusiveOperation()); int rv = temp_entry_ptr->WriteData( @@ -1643,8 +1682,7 @@ void LegacyCacheStorageCache::WriteSideDataDidWrite( ErrorCallback callback, ScopedWritableEntry entry, int expected_bytes, - std::unique_ptr<::content::proto::CacheResponse> response, - int side_data_size_before_write, + std::unique_ptr<::content::proto::CacheMetadata> metadata, int64_t trace_id, int rv) { TRACE_EVENT_WITH_FLOW0("CacheStorage", @@ -1652,31 +1690,67 @@ void LegacyCacheStorageCache::WriteSideDataDidWrite( TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_IN); if (rv != expected_bytes) { WriteSideDataComplete(std::move(callback), std::move(entry), + /*padding=*/0, /*side_data_padding=*/0, CacheStorageError::kErrorStorage); return; } - if (ShouldPadResourceSize(response.get())) { - cache_padding_ -= CalculateResponsePaddingInternal( - response.get(), cache_padding_key_.get(), side_data_size_before_write); + auto* response = metadata->mutable_response(); + + if (ShouldPadResourceSize(response)) { + cache_padding_ -= response->side_data_padding(); + + response->set_side_data_padding( + CalculateSideDataPadding(origin_, response, rv)); + cache_padding_ += response->side_data_padding(); + + // Get a temporary copy of the entry pointer before passing it in + // base::Bind. + disk_cache::Entry* temp_entry_ptr = entry.get(); - cache_padding_ += CalculateResponsePaddingInternal( - response.get(), cache_padding_key_.get(), rv); + WriteMetadata( + temp_entry_ptr, *metadata, + base::BindOnce(&LegacyCacheStorageCache::WriteSideDataDidWriteMetadata, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + std::move(entry), response->padding(), + response->side_data_padding())); + return; } WriteSideDataComplete(std::move(callback), std::move(entry), + response->padding(), response->side_data_padding(), CacheStorageError::kSuccess); } +void LegacyCacheStorageCache::WriteSideDataDidWriteMetadata( + ErrorCallback callback, + ScopedWritableEntry entry, + int64_t padding, + int64_t side_data_padding, + int expected_bytes, + int rv) { + auto result = blink::mojom::CacheStorageError::kSuccess; + if (rv != expected_bytes) { + result = MakeErrorStorage( + ErrorStorageType::kWriteSideDataDidWriteMetadataWrongBytes); + } + WriteSideDataComplete(std::move(callback), std::move(entry), padding, + side_data_padding, result); +} + void LegacyCacheStorageCache::WriteSideDataComplete( ErrorCallback callback, ScopedWritableEntry entry, + int64_t padding, + int64_t side_data_padding, blink::mojom::CacheStorageError error) { if (error != CacheStorageError::kSuccess) { // If we found the entry, then we possibly wrote something and now we're // dooming the entry, causing a change in size, so update the size before // returning. if (error != CacheStorageError::kErrorNotFound) { + entry.reset(); + cache_padding_ -= (padding + side_data_padding); UpdateCacheSize(base::BindOnce(std::move(callback), error)); return; } @@ -1870,17 +1944,33 @@ void LegacyCacheStorageCache::PutDidCreateEntry( for (const auto& header : put_context->response->cors_exposed_header_names) response_metadata->add_cors_exposed_header_names(header); + DCHECK(!ShouldPadResourceSize(*put_context->response) || + put_context->response->padding); + response_metadata->set_padding(put_context->response->padding); + + int64_t side_data_padding = 0; + if (ShouldPadResourceSize(*put_context->response) && + put_context->side_data_blob) { + side_data_padding = CalculateSideDataPadding( + origin_, response_metadata, put_context->side_data_blob_size); + } + response_metadata->set_side_data_padding(side_data_padding); + // Get a temporary copy of the entry pointer before passing it in base::Bind. disk_cache::Entry* temp_entry_ptr = put_context->cache_entry.get(); WriteMetadata( temp_entry_ptr, metadata, base::BindOnce(&LegacyCacheStorageCache::PutDidWriteHeaders, - weak_ptr_factory_.GetWeakPtr(), std::move(put_context))); + weak_ptr_factory_.GetWeakPtr(), std::move(put_context), + response_metadata->padding(), + response_metadata->side_data_padding())); } void LegacyCacheStorageCache::PutDidWriteHeaders( std::unique_ptr<PutContext> put_context, + int64_t padding, + int64_t side_data_padding, int expected_bytes, int rv) { TRACE_EVENT_WITH_FLOW0("CacheStorage", @@ -1896,11 +1986,9 @@ void LegacyCacheStorageCache::PutDidWriteHeaders( return; } - if (ShouldPadResourceSize(*put_context->response)) { - cache_padding_ += CalculateResponsePadding(*put_context->response, - cache_padding_key_.get(), - 0 /* side_data_size */); - } + DCHECK(!ShouldPadResourceSize(*put_context->response) || + (padding + side_data_padding)); + cache_padding_ += padding + side_data_padding; PutWriteBlobToCache(std::move(put_context), INDEX_RESPONSE_BODY); } @@ -2084,12 +2172,9 @@ void LegacyCacheStorageCache::PaddingDidQueryCache( int64_t cache_padding = 0; if (error == CacheStorageError::kSuccess) { for (const auto& result : *query_cache_results) { - if (ShouldPadResourceSize(*result.response)) { - int32_t side_data_size = - result.entry ? result.entry->GetDataSize(INDEX_SIDE_DATA) : 0; - cache_padding += CalculateResponsePadding( - *result.response, cache_padding_key_.get(), side_data_size); - } + DCHECK(!ShouldPadResourceSize(*result.response) || + (result.padding + result.side_data_padding)); + cache_padding += result.padding + result.side_data_padding; } } @@ -2290,9 +2375,9 @@ void LegacyCacheStorageCache::DeleteDidQueryCache( for (auto& result : *query_cache_results) { disk_cache::ScopedEntryPtr entry = std::move(result.entry); if (ShouldPadResourceSize(*result.response)) { - cache_padding_ -= - CalculateResponsePadding(*result.response, cache_padding_key_.get(), - entry->GetDataSize(INDEX_SIDE_DATA)); + DCHECK(!ShouldPadResourceSize(*result.response) || + (result.padding + result.side_data_padding)); + cache_padding_ -= (result.padding + result.side_data_padding); } entry->Doom(); } diff --git a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h index 099b9c49e36..bf173d95546 100644 --- a/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h +++ b/chromium/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h @@ -47,7 +47,6 @@ struct PutContext; namespace proto { class CacheMetadata; -class CacheResponse; } // namespace proto namespace cache_storage_cache_unittest { @@ -83,10 +82,6 @@ class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache { int64_t cache_size, int64_t cache_padding, std::unique_ptr<crypto::SymmetricKey> cache_padding_key); - static int64_t CalculateResponsePadding( - const blink::mojom::FetchAPIResponse& response, - const crypto::SymmetricKey* padding_key, - int side_data_size); static int32_t GetResponsePaddingVersion(); void Match(blink::mojom::FetchAPIRequestPtr request, @@ -282,6 +277,10 @@ class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache { std::unique_ptr<QueryCacheContext> query_cache_context, disk_cache::ScopedEntryPtr entry, std::unique_ptr<proto::CacheMetadata> metadata); + void QueryCacheUpgradePadding( + std::unique_ptr<QueryCacheContext> query_cache_context, + disk_cache::ScopedEntryPtr entry, + std::unique_ptr<proto::CacheMetadata> metadata); static bool QueryCacheResultCompare(const QueryCacheResult& lhs, const QueryCacheResult& rhs); static size_t EstimatedResponseSizeWithoutBlob( @@ -362,12 +361,19 @@ class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache { ErrorCallback callback, ScopedWritableEntry entry, int expected_bytes, - std::unique_ptr<content::proto::CacheResponse> response, - int side_data_size_before_write, + std::unique_ptr<content::proto::CacheMetadata> metadata, int64_t trace_id, int rv); + void WriteSideDataDidWriteMetadata(ErrorCallback callback, + ScopedWritableEntry entry, + int64_t padding, + int64_t side_data_padding, + int expected_bytes, + int rv); void WriteSideDataComplete(ErrorCallback callback, ScopedWritableEntry entry, + int64_t padding, + int64_t side_data_padding, blink::mojom::CacheStorageError error); // Puts the request and response object in the cache. The response body (if @@ -386,6 +392,8 @@ class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache { void PutDidCreateEntry(std::unique_ptr<PutContext> put_context, disk_cache::EntryResult result); void PutDidWriteHeaders(std::unique_ptr<PutContext> put_context, + int64_t padding, + int64_t side_data_padding, int expected_bytes, int rv); void PutWriteBlobToCache(std::unique_ptr<PutContext> put_context, @@ -540,6 +548,7 @@ class CONTENT_EXPORT LegacyCacheStorageCache : public CacheStorageCache { // The actual cache size (not including padding). int64_t cache_size_; int64_t cache_padding_ = 0; + // TODO(wanderview): remove padding key management std::unique_ptr<crypto::SymmetricKey> cache_padding_key_; int64_t last_reported_size_ = 0; size_t max_query_size_bytes_; diff --git a/chromium/content/common/background_fetch/background_fetch_types.cc b/chromium/content/common/background_fetch/background_fetch_types.cc index a37fe7d14c1..08f8fb83c5c 100644 --- a/chromium/content/common/background_fetch/background_fetch_types.cc +++ b/chromium/content/common/background_fetch/background_fetch_types.cc @@ -31,8 +31,8 @@ blink::mojom::FetchAPIResponsePtr BackgroundFetchSettledFetch::CloneResponse( return nullptr; return blink::mojom::FetchAPIResponse::New( response->url_list, response->status_code, response->status_text, - response->response_type, response->response_source, response->headers, - response->mime_type, response->request_method, + response->response_type, response->padding, response->response_source, + response->headers, response->mime_type, response->request_method, CloneSerializedBlob(response->blob), response->error, response->response_time, response->cache_storage_cache_name, response->cors_exposed_header_names, diff --git a/chromium/content/common/service_worker/service_worker_loader_helpers.cc b/chromium/content/common/service_worker/service_worker_loader_helpers.cc index 0a8f32b009c..c9bd10b8b14 100644 --- a/chromium/content/common/service_worker/service_worker_loader_helpers.cc +++ b/chromium/content/common/service_worker/service_worker_loader_helpers.cc @@ -97,6 +97,7 @@ void ServiceWorkerLoaderHelpers::SaveResponseInfo( out_head->was_fallback_required_by_service_worker = false; out_head->url_list_via_service_worker = response.url_list; out_head->response_type = response.response_type; + out_head->padding = response.padding; if (response.mime_type.has_value()) { std::string charset; bool had_charset = false; diff --git a/chromium/content/renderer/loader/web_url_loader_impl.cc b/chromium/content/renderer/loader/web_url_loader_impl.cc index 1f45b65c172..56c8518504e 100644 --- a/chromium/content/renderer/loader/web_url_loader_impl.cc +++ b/chromium/content/renderer/loader/web_url_loader_impl.cc @@ -929,6 +929,7 @@ void WebURLLoaderImpl::PopulateURLResponse( response->SetWasFallbackRequiredByServiceWorker( head.was_fallback_required_by_service_worker); response->SetType(head.response_type); + response->SetPadding(head.padding); response->SetUrlListViaServiceWorker(head.url_list_via_service_worker); response->SetCacheStorageCacheName( head.service_worker_response_source == diff --git a/chromium/services/network/public/mojom/url_response_head.mojom b/chromium/services/network/public/mojom/url_response_head.mojom index fa737edd89d..d24f063b8eb 100644 --- a/chromium/services/network/public/mojom/url_response_head.mojom +++ b/chromium/services/network/public/mojom/url_response_head.mojom @@ -134,6 +134,12 @@ struct URLResponseHead { // https://fetch.spec.whatwg.org/#concept-response-type FetchResponseType response_type = FetchResponseType.kDefault; + // Pre-computed padding. This should only be non-zero when |response_type| + // is set to kOpaque. Note, this is not set by network service, but will be + // populated if the response was provided by a service worker FetchEvent + // handler. + int64 padding = 0; + // The cache name of the CacheStorage from where the response is served via // the ServiceWorker. Empty if the response isn't from the CacheStorage. string cache_storage_cache_name; diff --git a/chromium/storage/browser/BUILD.gn b/chromium/storage/browser/BUILD.gn index 99e3c71c1dd..389b129e9ee 100644 --- a/chromium/storage/browser/BUILD.gn +++ b/chromium/storage/browser/BUILD.gn @@ -183,8 +183,6 @@ jumbo_component("browser") { "file_system/watcher_manager.h", "quota/client_usage_tracker.cc", "quota/client_usage_tracker.h", - "quota/padding_key.cc", - "quota/padding_key.h", "quota/quota_callbacks.h", "quota/quota_client.h", "quota/quota_client_type.cc", diff --git a/chromium/storage/browser/quota/padding_key.cc b/chromium/storage/browser/quota/padding_key.cc deleted file mode 100644 index 788f6f4634c..00000000000 --- a/chromium/storage/browser/quota/padding_key.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "storage/browser/quota/padding_key.h" - -#include <cstdint> -#include <vector> - -#include "base/no_destructor.h" -#include "crypto/hmac.h" -#include "net/http/http_request_headers.h" - -using crypto::SymmetricKey; - -namespace storage { - -namespace { - -const SymmetricKey::Algorithm kPaddingKeyAlgorithm = SymmetricKey::AES; - -// The range of the padding added to response sizes for opaque resources. -// Increment the CacheStorage padding version if changed. -constexpr uint64_t kPaddingRange = 14431 * 1024; - -std::unique_ptr<SymmetricKey>* GetPaddingKeyInternal() { - static base::NoDestructor<std::unique_ptr<SymmetricKey>> s_padding_key([] { - return SymmetricKey::GenerateRandomKey(kPaddingKeyAlgorithm, 128); - }()); - return s_padding_key.get(); -} - -} // namespace - -const SymmetricKey* GetDefaultPaddingKey() { - return GetPaddingKeyInternal()->get(); -} - -std::unique_ptr<SymmetricKey> CopyDefaultPaddingKey() { - return SymmetricKey::Import(kPaddingKeyAlgorithm, - (*GetPaddingKeyInternal())->key()); -} - -std::unique_ptr<SymmetricKey> DeserializePaddingKey( - const std::string& raw_key) { - return SymmetricKey::Import(kPaddingKeyAlgorithm, raw_key); -} - -std::string SerializeDefaultPaddingKey() { - return (*GetPaddingKeyInternal())->key(); -} - -void ResetPaddingKeyForTesting() { - *GetPaddingKeyInternal() = - SymmetricKey::GenerateRandomKey(kPaddingKeyAlgorithm, 128); -} - -int64_t ComputeResponsePadding(const std::string& response_url, - const crypto::SymmetricKey* padding_key, - bool has_metadata, - bool loaded_with_credentials, - const std::string& request_method) { - DCHECK(!response_url.empty()); - - crypto::HMAC hmac(crypto::HMAC::SHA256); - CHECK(hmac.Init(padding_key)); - - std::string key = response_url; - if (has_metadata) - key += "METADATA"; - if (loaded_with_credentials) - key += "CREDENTIALED"; - - // It should only be possible to have a CORS safelisted method here since - // the spec does not permit other methods for no-cors requests. - DCHECK(request_method == net::HttpRequestHeaders::kGetMethod || - request_method == net::HttpRequestHeaders::kHeadMethod || - request_method == net::HttpRequestHeaders::kPostMethod); - key += request_method; - - uint64_t digest_start; - CHECK(hmac.Sign(key, reinterpret_cast<uint8_t*>(&digest_start), - sizeof(digest_start))); - return digest_start % kPaddingRange; -} - -} // namespace storage diff --git a/chromium/storage/browser/quota/padding_key.h b/chromium/storage/browser/quota/padding_key.h deleted file mode 100644 index 4b5978a82e7..00000000000 --- a/chromium/storage/browser/quota/padding_key.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ -#define STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ - -#include <memory> -#include <string> - -#include "base/component_export.h" -#include "crypto/symmetric_key.h" -#include "url/gurl.h" - -namespace storage { - -COMPONENT_EXPORT(STORAGE_BROWSER) -const crypto::SymmetricKey* GetDefaultPaddingKey(); - -// Returns a copy of the default key used to calculate padding sizes. -// -// The default padding key is a singleton object whose value is randomly -// generated the first time it is requested on every browser startup. In -// CacheStorage, when a cache does not have a padding key, it is assigned the -// current default key. -COMPONENT_EXPORT(STORAGE_BROWSER) -std::unique_ptr<crypto::SymmetricKey> CopyDefaultPaddingKey(); - -// Builds a key whose value is the given string. -// -// May return null if deserializing fails (e.g. if the raw key is the wrong -// size). -COMPONENT_EXPORT(STORAGE_BROWSER) -std::unique_ptr<crypto::SymmetricKey> DeserializePaddingKey( - const std::string& raw_key); - -// Gets the raw value of the default padding key. -// -// Each cache stores the raw value of the key that should be used when -// calculating its padding size. -COMPONENT_EXPORT(STORAGE_BROWSER) -std::string SerializeDefaultPaddingKey(); - -// Resets the default key to a random value. -// -// Simulating a key change across a browser restart lets us test that padding -// calculations are using the appropriate key. -COMPONENT_EXPORT(STORAGE_BROWSER) -void ResetPaddingKeyForTesting(); - -// Computes the padding size for a resource. -// -// For AppCache, which does not support storing metadata for a resource, -// |has_metadata| will always be false. -// -// For CacheStorage, the padding size of an entry depends on whether it contains -// metadata (a.k.a. "side data") and if the response was loaded with -// credentials. If metadata is added to the entry, the entry must be assigned a -// new padding size. Otherwise, the growth in the entry's size would leak the -// exact size of the added metadata. -COMPONENT_EXPORT(STORAGE_BROWSER) -int64_t ComputeResponsePadding(const std::string& response_url, - const crypto::SymmetricKey* padding_key, - bool has_metadata, - bool loaded_with_credentials, - const std::string& request_method); - -} // namespace storage - -#endif // STORAGE_BROWSER_QUOTA_PADDING_KEY_H_ diff --git a/chromium/storage/common/BUILD.gn b/chromium/storage/common/BUILD.gn index 5e76c440e76..8fdf0dc0951 100644 --- a/chromium/storage/common/BUILD.gn +++ b/chromium/storage/common/BUILD.gn @@ -17,6 +17,8 @@ component("common") { "file_system/file_system_types.h", "file_system/file_system_util.cc", "file_system/file_system_util.h", + "quota/padding_key.cc", + "quota/padding_key.h", ] # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. diff --git a/chromium/storage/common/quota/padding_key.cc b/chromium/storage/common/quota/padding_key.cc new file mode 100644 index 00000000000..24cd429e3fe --- /dev/null +++ b/chromium/storage/common/quota/padding_key.cc @@ -0,0 +1,168 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "storage/common/quota/padding_key.h" + +#include <inttypes.h> +#include <cstdint> +#include <vector> +#include "base/no_destructor.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "crypto/hmac.h" +#include "crypto/random.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/base/url_util.h" +#include "net/http/http_request_headers.h" +#include "services/network/public/mojom/url_response_head.mojom-shared.h" +#include "url/gurl.h" +#include "url/origin.h" +#include "url/url_canon.h" +#include "url/url_constants.h" +#include "url/url_util.h" + +using crypto::SymmetricKey; + +namespace storage { + +namespace { + +const SymmetricKey::Algorithm kPaddingKeyAlgorithm = SymmetricKey::AES; + +// The range of the padding added to response sizes for opaque resources. +// Increment the CacheStorage padding version if changed. +constexpr uint64_t kPaddingRange = 14431 * 1024; + +std::unique_ptr<SymmetricKey>* GetPaddingKeyInternal() { + static base::NoDestructor<std::unique_ptr<SymmetricKey>> s_padding_key([] { + return SymmetricKey::GenerateRandomKey(kPaddingKeyAlgorithm, 128); + }()); + return s_padding_key.get(); +} + +static bool IsStandardSchemeWithNetworkHost(base::StringPiece scheme) { + // file scheme is special. Windows file share origins can have network hosts. + if (scheme == url::kFileScheme) + return true; + + url::SchemeType scheme_type; + if (!url::GetStandardSchemeType( + scheme.data(), url::Component(0, scheme.length()), &scheme_type)) { + return false; + } + return scheme_type == url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION || + scheme_type == url::SCHEME_WITH_HOST_AND_PORT; +} + + +static url::Origin SchemefuleSiteReplacement( + const url::Origin& origin) { + // 1. If origin is an opaque origin, then return origin. + if (origin.opaque()) + return origin; + + std::string registerable_domain; + + // Non-normative step. + // We only lookup the registerable domain for schemes with network hosts, this + // is non-normative. Other schemes for non-opaque origins do not + // meaningfully have a registerable domain for their host, so they are + // skipped. + if (IsStandardSchemeWithNetworkHost(origin.scheme())) { + registerable_domain = GetDomainAndRegistry( + origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + } + + // If origin's host's registrable domain is null, then return (origin's + // scheme, origin's host). + // + // `GetDomainAndRegistry()` returns an empty string for IP literals and + // effective TLDs. + // + // Note that `registerable_domain` could still end up empty, since the + // `origin` might have a scheme that permits empty hostnames, such as "file". + bool used_registerable_domain = !registerable_domain.empty(); + if (!used_registerable_domain) + registerable_domain = origin.host(); + + int port = url::DefaultPortForScheme(origin.scheme().c_str(), + origin.scheme().length()); + + // Provide a default port of 0 for non-standard schemes. + if (port == url::PORT_UNSPECIFIED) + port = 0; + + return url::Origin::CreateFromNormalizedTuple(origin.scheme(), + registerable_domain, port); +} + +} // namespace + +const SymmetricKey* GetDefaultPaddingKey() { + return GetPaddingKeyInternal()->get(); +} + +std::unique_ptr<SymmetricKey> CopyDefaultPaddingKey() { + return SymmetricKey::Import(kPaddingKeyAlgorithm, + (*GetPaddingKeyInternal())->key()); +} + +std::unique_ptr<SymmetricKey> DeserializePaddingKey( + const std::string& raw_key) { + return SymmetricKey::Import(kPaddingKeyAlgorithm, raw_key); +} + +std::string SerializeDefaultPaddingKey() { + return (*GetPaddingKeyInternal())->key(); +} + +void ResetPaddingKeyForTesting() { + *GetPaddingKeyInternal() = + SymmetricKey::GenerateRandomKey(kPaddingKeyAlgorithm, 128); +} + +bool ShouldPadResponseType(network::mojom::FetchResponseType type) { + return type == network::mojom::FetchResponseType::kOpaque || + type == network::mojom::FetchResponseType::kOpaqueRedirect; +} + +int64_t ComputeRandomResponsePadding() { + uint64_t raw_random = 0; + crypto::RandBytes(&raw_random, sizeof(uint64_t)); + return raw_random % kPaddingRange; +} + +int64_t ComputeStableResponsePadding(const url::Origin& origin, + const std::string& response_url, + const base::Time& response_time, + const std::string& request_method, + int64_t side_data_size) { + DCHECK(!response_url.empty()); + + url::Origin site = SchemefuleSiteReplacement(origin); + + DCHECK_GT(response_time, base::Time::UnixEpoch()); + int64_t microseconds = + (response_time - base::Time::UnixEpoch()).InMicroseconds(); + + // It should only be possible to have a CORS safelisted method here since + // the spec does not permit other methods for no-cors requests. + DCHECK(request_method == net::HttpRequestHeaders::kGetMethod || + request_method == net::HttpRequestHeaders::kHeadMethod || + request_method == net::HttpRequestHeaders::kPostMethod); + + std::string key = base::StringPrintf( + "%s-%" PRId64 "-%s-%s-%" PRId64, response_url.c_str(), microseconds, + site.Serialize().c_str(), request_method.c_str(), side_data_size); + + crypto::HMAC hmac(crypto::HMAC::SHA256); + CHECK(hmac.Init(GetDefaultPaddingKey())); + + uint64_t digest_start = 0; + CHECK(hmac.Sign(key, reinterpret_cast<uint8_t*>(&digest_start), + sizeof(digest_start))); + return digest_start % kPaddingRange; +} + +} // namespace storage diff --git a/chromium/storage/common/quota/padding_key.h b/chromium/storage/common/quota/padding_key.h new file mode 100644 index 00000000000..ad463cb1e49 --- /dev/null +++ b/chromium/storage/common/quota/padding_key.h @@ -0,0 +1,86 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef STORAGE_COMMON_QUOTA_PADDING_KEY_H_ +#define STORAGE_COMMON_QUOTA_PADDING_KEY_H_ + +#include <memory> +#include <string> + +#include "base/component_export.h" +#include "crypto/symmetric_key.h" +#include "services/network/public/mojom/url_response_head.mojom-shared.h" +#include "url/gurl.h" + +namespace base { +class Time; +} // namespace base + +namespace url { +class Origin; +} // namespace url + +namespace storage { + +COMPONENT_EXPORT(STORAGE_COMMON) +const crypto::SymmetricKey* GetDefaultPaddingKey(); + +// Returns a copy of the default key used to calculate padding sizes. +// +// The default padding key is a singleton object whose value is randomly +// generated the first time it is requested on every browser startup. In +// CacheStorage, when a cache does not have a padding key, it is assigned the +// current default key. +COMPONENT_EXPORT(STORAGE_COMMON) +std::unique_ptr<crypto::SymmetricKey> CopyDefaultPaddingKey(); + +// Builds a key whose value is the given string. +// +// May return null if deserializing fails (e.g. if the raw key is the wrong +// size). +COMPONENT_EXPORT(STORAGE_COMMON) +std::unique_ptr<crypto::SymmetricKey> DeserializePaddingKey( + const std::string& raw_key); + +// Gets the raw value of the default padding key. +// +// Each cache stores the raw value of the key that should be used when +// calculating its padding size. +COMPONENT_EXPORT(STORAGE_COMMON) +std::string SerializeDefaultPaddingKey(); + +// Resets the default key to a random value. +// +// Simulating a key change across a browser restart lets us test that padding +// calculations are using the appropriate key. +COMPONENT_EXPORT(STORAGE_COMMON) +void ResetPaddingKeyForTesting(); + +// Utility method to determine if a given type of response should be padded. +COMPONENT_EXPORT(STORAGE_COMMON) +bool ShouldPadResponseType(network::mojom::FetchResponseType type); + +// Compute a purely random padding size for a resource. A random padding is +// preferred except in cases where a site could rapidly trigger a large number +// of padded values for the same resource; e.g. from http cache. +COMPONENT_EXPORT(STORAGE_COMMON) +int64_t ComputeRandomResponsePadding(); + +// Compute a stable padding value for a resource. This should be used for +// cases where a site could trigger a large number of padding values to be +// generated for the same resource; e.g. http cache. The |origin| is the +// origin of the context that loaded the resource. Note, its important that the +// |response_time| be the time stored in the cache and not just the current +// time. The |side_data_size| should only be passed if padding is being +// computed for a side data blob. +COMPONENT_EXPORT(STORAGE_COMMON) +int64_t ComputeStableResponsePadding(const url::Origin& origin, + const std::string& response_url, + const base::Time& response_time, + const std::string& request_method, + int64_t side_data_size = 0); + +} // namespace storage + +#endif // STORAGE_COMMON_QUOTA_PADDING_KEY_H_ diff --git a/chromium/third_party/blink/public/mojom/fetch/fetch_api_response.mojom b/chromium/third_party/blink/public/mojom/fetch/fetch_api_response.mojom index 678de941eb3..de5ab4ea2fd 100644 --- a/chromium/third_party/blink/public/mojom/fetch/fetch_api_response.mojom +++ b/chromium/third_party/blink/public/mojom/fetch/fetch_api_response.mojom @@ -36,6 +36,10 @@ struct FetchAPIResponse { network.mojom.FetchResponseType response_type = network.mojom.FetchResponseType.kDefault; + // Pre-computed padding for this response. This should only be non-zero + // for when |response_type| is kOpaque. + int64 padding = 0; + // The source of this response, e.g. network, CacheStorage. network.mojom.FetchResponseSource response_source = network.mojom.FetchResponseSource.kUnspecified; diff --git a/chromium/third_party/blink/public/platform/web_url_response.h b/chromium/third_party/blink/public/platform/web_url_response.h index 412624130e2..5d4aa60d001 100644 --- a/chromium/third_party/blink/public/platform/web_url_response.h +++ b/chromium/third_party/blink/public/platform/web_url_response.h @@ -264,6 +264,12 @@ class WebURLResponse { BLINK_PLATFORM_EXPORT void SetType(network::mojom::FetchResponseType); BLINK_PLATFORM_EXPORT network::mojom::FetchResponseType GetType() const; + // Pre-computed padding. This should only be non-zero if the type is + // kOpaque. In addition, it is only set for responses provided by a + // service worker FetchEvent handler. + BLINK_PLATFORM_EXPORT void SetPadding(int64_t); + BLINK_PLATFORM_EXPORT int64_t GetPadding() const; + // The URL list of the Response object the ServiceWorker passed to // respondWith(). See // network::ResourceResponseInfo::url_list_via_service_worker for details. diff --git a/chromium/third_party/blink/renderer/core/BUILD.gn b/chromium/third_party/blink/renderer/core/BUILD.gn index 1b10ab1a079..5dc17a42531 100644 --- a/chromium/third_party/blink/renderer/core/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/BUILD.gn @@ -199,6 +199,7 @@ component("core") { ] deps = [ "//components/paint_preview/common", + "//storage/common", "//third_party/blink/public/common", "//third_party/blink/renderer/platform", "//third_party/blink/renderer/platform/wtf", diff --git a/chromium/third_party/blink/renderer/core/fetch/DEPS b/chromium/third_party/blink/renderer/core/fetch/DEPS index 26583be0e15..5a78494f81c 100644 --- a/chromium/third_party/blink/renderer/core/fetch/DEPS +++ b/chromium/third_party/blink/renderer/core/fetch/DEPS @@ -9,6 +9,7 @@ include_rules = [ "+net/http/http_response_info.h", "+services/network/public/cpp", "+services/network/public/mojom", + "+storage/common/quota/padding_key.h", "+url/gurl.h", ] diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc index fc3052584fc..df680aa38a4 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc @@ -423,16 +423,21 @@ void FetchManager::Loader::DidReceiveResponse( FetchResponseData* response_data = FetchResponseData::CreateWithBuffer( BodyStreamBuffer::Create(script_state, place_holder_body_, signal_)); - response_data->InitFromResourceResponse( - url_list_, fetch_request_data_->Method(), - fetch_request_data_->Credentials(), tainting, response); - - FetchResponseData* tainted_response = nullptr; - DCHECK(!(network_utils::IsRedirectResponseCode(response_http_status_code_) && HasNonEmptyLocationHeader(response_data->HeaderList()) && fetch_request_data_->Redirect() != RedirectMode::kManual)); + auto response_type = response.GetType(); + if (network_utils::IsRedirectResponseCode(response_http_status_code_) && + fetch_request_data_->Redirect() == RedirectMode::kManual) + response_type = network::mojom::FetchResponseType::kOpaqueRedirect; + + response_data->InitFromResourceResponse( + execution_context_, response_type, url_list_, + fetch_request_data_->Method(), fetch_request_data_->Credentials(), + tainting, response); + + FetchResponseData* tainted_response = nullptr; if (network_utils::IsRedirectResponseCode(response_http_status_code_) && fetch_request_data_->Redirect() == RedirectMode::kManual) { tainted_response = response_data->CreateOpaqueRedirectFilteredResponse(); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc index 00870a35078..8188a1ebb20 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/fetch/fetch_response_data.h" +#include "storage/common/quota/padding_key.h" #include "third_party/blink/public/mojom/fetch/fetch_api_response.mojom-blink.h" #include "third_party/blink/renderer/core/fetch/fetch_header_list.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" @@ -188,6 +189,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, ExceptionState& exception_state) { FetchResponseData* new_response = Create(); new_response->type_ = type_; + new_response->padding_ = padding_; new_response->response_source_ = response_source_; if (termination_reason_) { new_response->termination_reason_ = std::make_unique<TerminationReason>(); @@ -269,6 +271,7 @@ mojom::blink::FetchAPIResponsePtr FetchResponseData::PopulateFetchAPIResponse( response->status_code = status_; response->status_text = status_message_; response->response_type = type_; + response->padding = padding_; response->response_source = response_source_; response->mime_type = mime_type_; response->request_method = request_method_; @@ -289,6 +292,8 @@ mojom::blink::FetchAPIResponsePtr FetchResponseData::PopulateFetchAPIResponse( } void FetchResponseData::InitFromResourceResponse( + ExecutionContext* context, + network::mojom::FetchResponseType response_type, const Vector<KURL>& request_url_list, const AtomicString& request_method, network::mojom::CredentialsMode request_credentials, @@ -347,6 +352,22 @@ void FetchResponseData::InitFromResourceResponse( tainting == FetchRequestData::kBasicTainting)); SetHasRangeRequested(response.HasRangeRequested()); + + // Use the explicit padding in the response provided by a service worker + // or compute a new padding if necessary. + if (response.GetPadding()) { + SetPadding(response.GetPadding()); + } else { + if (storage::ShouldPadResponseType(response_type)) { + int64_t padding = response.WasCached() + ? storage::ComputeStableResponsePadding( + context->GetSecurityOrigin()->ToUrlOrigin(), + Url()->GetString().Utf8(), ResponseTime(), + request_method.Utf8()) + : storage::ComputeRandomResponsePadding(); + SetPadding(padding); + } + } } FetchResponseData::FetchResponseData(Type type, @@ -354,6 +375,7 @@ FetchResponseData::FetchResponseData(Type type, uint16_t status, AtomicString status_message) : type_(type), + padding_(0), response_source_(source), status_(status), status_message_(status_message), diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h index 33657bb877c..31bc68df135 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h @@ -85,6 +85,8 @@ class CORE_EXPORT FetchResponseData final } bool HasRangeRequested() const { return has_range_requested_; } + int64_t GetPadding() const { return padding_; } + void SetPadding(int64_t padding) { padding_ = padding; } void SetResponseSource(network::mojom::FetchResponseSource response_source) { response_source_ = response_source; } @@ -138,6 +140,8 @@ class CORE_EXPORT FetchResponseData final // Initialize non-body data from the given |response|. void InitFromResourceResponse( + ExecutionContext* context, + network::mojom::FetchResponseType response_type, const Vector<KURL>& request_url_list, const AtomicString& request_method, network::mojom::CredentialsMode request_credentials, @@ -148,6 +152,7 @@ class CORE_EXPORT FetchResponseData final private: network::mojom::FetchResponseType type_; + int64_t padding_; network::mojom::FetchResponseSource response_source_; std::unique_ptr<TerminationReason> termination_reason_; Vector<KURL> url_list_; diff --git a/chromium/third_party/blink/renderer/core/fetch/response.cc b/chromium/third_party/blink/renderer/core/fetch/response.cc index c6cc155d70d..73905e17b1e 100644 --- a/chromium/third_party/blink/renderer/core/fetch/response.cc +++ b/chromium/third_party/blink/renderer/core/fetch/response.cc @@ -374,6 +374,7 @@ FetchResponseData* Response::CreateUnfilteredFetchResponseDataWithoutBody( else response = FetchResponseData::CreateNetworkErrorResponse(); + response->SetPadding(fetch_api_response.padding); response->SetResponseSource(fetch_api_response.response_source); response->SetURLList(fetch_api_response.url_list); response->SetStatus(fetch_api_response.status_code); diff --git a/chromium/third_party/blink/renderer/modules/service_worker/fetch_event.cc b/chromium/third_party/blink/renderer/modules/service_worker/fetch_event.cc index 28d7ddd6787..7d4e830eae6 100644 --- a/chromium/third_party/blink/renderer/modules/service_worker/fetch_event.cc +++ b/chromium/third_party/blink/renderer/modules/service_worker/fetch_event.cc @@ -158,13 +158,19 @@ void FetchEvent::OnNavigationPreloadResponse( Vector<KURL> url_list(1); url_list[0] = preload_response_->CurrentRequestUrl(); + auto response_type = + network_utils::IsRedirectResponseCode(preload_response_->HttpStatusCode()) + ? network::mojom::FetchResponseType::kOpaqueRedirect + : network::mojom::FetchResponseType::kBasic; + response_data->InitFromResourceResponse( - url_list, http_names::kGET, network::mojom::CredentialsMode::kInclude, + ExecutionContext::From(script_state), response_type, url_list, + http_names::kGET, network::mojom::CredentialsMode::kInclude, FetchRequestData::kBasicTainting, preload_response_->ToResourceResponse()); FetchResponseData* tainted_response = - network_utils::IsRedirectResponseCode(preload_response_->HttpStatusCode()) + response_type == network::mojom::FetchResponseType::kOpaqueRedirect ? response_data->CreateOpaqueRedirectFilteredResponse() : response_data->CreateBasicFilteredResponse(); preload_response_property_->Resolve( diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc index 3f3efff75b0..cf5836a0bbc 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc @@ -374,6 +374,13 @@ network::mojom::FetchResponseType WebURLResponse::GetType() const { return resource_response_->GetType(); } +void WebURLResponse::SetPadding(int64_t padding) { + resource_response_->SetPadding(padding); +} +int64_t WebURLResponse::GetPadding() const { + return resource_response_->GetPadding(); +} + void WebURLResponse::SetUrlListViaServiceWorker( const WebVector<WebURL>& url_list_via_service_worker) { Vector<KURL> url_list(url_list_via_service_worker.size()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h index 5fadb1c34d9..5201e70e02a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h @@ -358,6 +358,9 @@ class PLATFORM_EXPORT ResourceResponse final { // https://html.spec.whatwg.org/C/#cors-cross-origin bool IsCorsCrossOrigin() const; + int64_t GetPadding() const { return padding_; } + void SetPadding(int64_t padding) { padding_ = padding; } + // See network::ResourceResponseInfo::url_list_via_service_worker. const Vector<KURL>& UrlListViaServiceWorker() const { return url_list_via_service_worker_; @@ -597,6 +600,11 @@ class PLATFORM_EXPORT ResourceResponse final { network::mojom::FetchResponseType response_type_ = network::mojom::FetchResponseType::kDefault; + // Pre-computed padding. This should only be non-zero if |response_type| is + // set to kOpaque. In addition, it is only set if the response was provided + // by a service worker FetchEvent handler. + int64_t padding_ = 0; + // HTTP version used in the response, if known. HTTPVersion http_version_ = kHTTPVersionUnknown; diff --git a/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index d063e8f2508..fe8b5e5d618 100755 --- a/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/chromium/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py @@ -1172,6 +1172,15 @@ _CONFIG = [ 'allowed': ['net::RequestPriority'], }, { + 'paths': + ['third_party/blink/renderer/core/fetch/fetch_response_data.cc'], + 'allowed': [ + 'storage::ComputeRandomResponsePadding', + 'storage::ComputeStableResponsePadding', + 'storage::ShouldPadResponseType' + ], + }, + { 'paths': ['third_party/blink/renderer/core/frame/local_frame_view.cc'], 'allowed': ['cc::frame_viewer_instrumentation::IsTracingLayerTreeSnapshots'], diff --git a/chromium/tools/metrics/histograms/enums.xml b/chromium/tools/metrics/histograms/enums.xml index 798b69c07a3..f524d6df7fe 100644 --- a/chromium/tools/metrics/histograms/enums.xml +++ b/chromium/tools/metrics/histograms/enums.xml @@ -8777,6 +8777,8 @@ Called by update_bad_message_reasons.py.--> <int value="21" label="kKeysImplBackendClosed"/> <int value="22" label="kCreateBackendDidCreateFailed"/> <int value="23" label="kStorageGetAllMatchedEntriesBackendClosed"/> + <int value="24" label="kStorageHandleNull"/> + <int value="25" label="kWriteSideDataDidWriteMetadataWrongBytes"/> </enum> <enum name="CacheStorageErrorType"> |