diff options
Diffstat (limited to 'chromium/services/network')
161 files changed, 4328 insertions, 1411 deletions
diff --git a/chromium/services/network/BUILD.gn b/chromium/services/network/BUILD.gn index 5c963f42c7f..1a4135ea4b7 100644 --- a/chromium/services/network/BUILD.gn +++ b/chromium/services/network/BUILD.gn @@ -36,8 +36,6 @@ jumbo_component("network_service") { "data_pipe_element_reader.h", "dns_config_change_manager.cc", "dns_config_change_manager.h", - "empty_url_loader_client.cc", - "empty_url_loader_client.h", "host_resolver.cc", "host_resolver.h", "host_resolver_mdns_listener.cc", @@ -231,12 +229,12 @@ jumbo_component("network_service") { "//components/os_crypt", "//components/prefs", "//jingle:fake_ssl_socket", - "//mojo/core/embedder", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//net", "//net:extras", "//services/network/public/cpp", + "//services/network/public/cpp:crash_keys", "//services/network/public/cpp/cert_verifier:cert_verifier_creation", "//services/network/public/cpp/cert_verifier:mojo_cert_verifier", "//services/network/public/mojom", @@ -380,7 +378,6 @@ source_set("tests") { "//components/variations:test_support", "//crypto:test_support", "//jingle:fake_ssl_socket", - "//mojo/core/embedder", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//net", @@ -417,6 +414,8 @@ jumbo_source_set("test_support") { sources = [ "mojo_socket_test_util.cc", "mojo_socket_test_util.h", + "test/fake_test_cert_verifier_params_factory.cc", + "test/fake_test_cert_verifier_params_factory.h", "test/test_cookie_manager.cc", "test/test_cookie_manager.h", "test/test_data_pipe_getter.cc", diff --git a/chromium/services/network/README.md b/chromium/services/network/README.md index 58b776b4775..d9f0ca04baf 100644 --- a/chromium/services/network/README.md +++ b/chromium/services/network/README.md @@ -12,7 +12,7 @@ Some design goals not have hooks here. The only exception is when it's impossible for these features to function without some hooks in the network service. In that case, we add the minimal code required. Some examples included traffic - shaping for devtools and CORB blocking. + shaping for devtools, CORB blocking, and CORS. * every PostTask, thread hop and process hop (IPC) should be counted carefully as they introduce delays which could harm this performance critical code. * `NetworkContext` and `NetworkService` are trusted interfaces that aren't @@ -24,6 +24,77 @@ See https://bugs.chromium.org/p/chromium/issues/detail?id=598073 See the design doc https://docs.google.com/document/d/1wAHLw9h7gGuqJNCgG1mP1BmLtCGfZ2pys-PdZQ1vg7M/edit?pref=2&pli=1# +# Related docs + +* [URLLoader](url_loader.md) + +# Where does the network service run? + +> Note: For more background about this section, see also [Multi-process +Architecture](https://www.chromium.org/developers/design-documents/multi-process-architecture) +for an overview of the processes in Chromium. + +The network service is designed as a [Mojo service](/docs/mojo_and_services.md) +that in general doesn't need to be aware of which thread/process it runs on. +The browser process launches the network service and decides whether to run it +inside the browser process (*in-process*) or in a dedicated utility process +(*out-of-process*). + +The out-of-process configuration is preferred for isolation and stability, and +is the default on most platforms. The in-process configuration is the default on +Android because of some unresolved issues; see https://crbug.com/1049008. It +can also be useful for debugging; for example, it's used in Chromium's +[`--single-process`](https://www.chromium.org/developers/design-documents/process-models) +mode. + +*In the out-of-process case*: The network service runs on the [IO +thread](/docs/threading_and_tasks.md) of the utility process (see this +[comment][1] in `content/utility/services.cc` for why). The utility process +houses only the network service, so there is nothing running on its main thread. + +[1]: https://source.chromium.org/chromium/chromium/src/+/master:content/utility/services.cc;l=197-198;drc=9b85cd82c52e13ed685dd74c726d91067bbd34d5 + +*In the in-process case*: The network service runs on its own dedicated thread +in the browser process. Exception: on Chrome OS, it currently runs on the IO +thread; see https://crbug.com/1086738. + +# How does the network service start? + +*In the out-of-process case*: The browser creates the utility process and asks +it to launch the network service. For the browser-side code, see +`GetNetworkService()` in `content/browser/network_service_instance_impl.cc`. +For the utility process code, see `GetIOThreadServiceFactory` in +content/utility/services.cc. This calls `RunNetworkService()` which creates the +`network::NetworkService` instance. For more background about Chromium's +services architecture, see [Mojo and Services](/docs/mojo_and_services.md). + +*In the in-process case*: The browser process starts the network service. See +`CreateInProcessNetworkService()` in +`content/browser/network_service_instance_impl.cc`, which posts a task to create +the `network::NetworkService` instance. + +# What happens if the network service crashes? + +*In the out-of-process case*: If the network service crashes, it gets restarted +in a new utility process. The goal is for the failure to be mostly recoverable. +It is important to note that any URLLoaderFactories bound to the Network Service +before it crashes become disconnected, and will no longer continue to work. +Therefore it is useful to establish reconnection logic if it is detected that +the URLLoaderFactory is no longer connected. + +For example, a navigation request's URLLoaderFactory comes from +`StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessInternal` in the +browser process. This method has logic to detect if the URLLoaderFactory it +would normally return is disconnected. In that case, it creates a new one which +is used for all future navigation requests. Since most URLLoaderFactory users +use factories that are not created out-of-band, and are provided by some +service, reconnection logic is often implemented for free, and is usually not +something to worry about. + +*In the in-process case*: If the network service crashes in this case, of +course, the entire browser crashes. This is one reason for the goal to always +run it out-of-process. + # Buildbot The [Network Service @@ -41,13 +112,9 @@ Its steps are: Chrome on Android runs with the network service in-process by default (https://crbug.com/1049008). However, `browser_tests` are not well-supported on Android (https://crbug.com/611756), so we run them on this Linux bot. - Furthermore, there is a flag and group policy to run the network service - in-process on Desktop, but there are efforts to remove this - (https://crbug.com/1036230). * **`network_service_in_process_content_browsertests`**: Same as above but for `content_browsertests`. We might consider removing this from the bot, since - the Android bots run `content_browsertests` which should give enough coverage, - but maybe we can remove the Desktop flag and group policy first. + the Android bots run `content_browsertests` which should give enough coverage. * **`network_service_web_request_proxy_browser_tests`**: Runs `browser_tests` while forcing the "network request proxying" code path that is taken when the browser has an extension installed that uses the diff --git a/chromium/services/network/cert_verifier_with_trust_anchors.cc b/chromium/services/network/cert_verifier_with_trust_anchors.cc index 80081f16797..32b648d6028 100644 --- a/chromium/services/network/cert_verifier_with_trust_anchors.cc +++ b/chromium/services/network/cert_verifier_with_trust_anchors.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/bind.h" -#include "base/logging.h" +#include "base/check_op.h" #include "net/base/net_errors.h" #include "net/cert/caching_cert_verifier.h" #include "net/cert/cert_verifier.h" @@ -38,13 +38,17 @@ void CompleteAndSignalAnchorUse( std::move(completion_callback).Run(error); } -net::CertVerifier::Config ExtendTrustAnchors( +net::CertVerifier::Config ExtendTrustAnchorsAndTempCerts( const net::CertVerifier::Config& config, - const net::CertificateList& trust_anchors) { + const net::CertificateList& trust_anchors, + const net::CertificateList& untrusted_authorities) { net::CertVerifier::Config new_config = config; new_config.additional_trust_anchors.insert( new_config.additional_trust_anchors.begin(), trust_anchors.begin(), trust_anchors.end()); + new_config.additional_untrusted_authorities.insert( + new_config.additional_untrusted_authorities.begin(), + untrusted_authorities.begin(), untrusted_authorities.end()); return new_config; } @@ -64,18 +68,23 @@ void CertVerifierWithTrustAnchors::InitializeOnIOThread( std::unique_ptr<net::CertVerifier> delegate) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); delegate_ = std::move(delegate); - delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_)); + delegate_->SetConfig(ExtendTrustAnchorsAndTempCerts( + orig_config_, trust_anchors_, untrusted_authorities_)); } -void CertVerifierWithTrustAnchors::SetTrustAnchors( - const net::CertificateList& trust_anchors) { +void CertVerifierWithTrustAnchors::SetAdditionalCerts( + const net::CertificateList& trust_anchors, + const net::CertificateList& untrusted_authorities) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (trust_anchors == trust_anchors_) + if (std::tie(trust_anchors, untrusted_authorities) == + std::tie(trust_anchors_, untrusted_authorities_)) return; trust_anchors_ = trust_anchors; + untrusted_authorities_ = untrusted_authorities; if (!delegate_) return; - delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_)); + delegate_->SetConfig(ExtendTrustAnchorsAndTempCerts( + orig_config_, trust_anchors_, untrusted_authorities_)); } int CertVerifierWithTrustAnchors::Verify( @@ -100,7 +109,8 @@ int CertVerifierWithTrustAnchors::Verify( void CertVerifierWithTrustAnchors::SetConfig(const Config& config) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); orig_config_ = config; - delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_)); + delegate_->SetConfig(ExtendTrustAnchorsAndTempCerts( + orig_config_, trust_anchors_, untrusted_authorities_)); } } // namespace network diff --git a/chromium/services/network/cert_verifier_with_trust_anchors.h b/chromium/services/network/cert_verifier_with_trust_anchors.h index 271eed95ead..6e0c02a1f4f 100644 --- a/chromium/services/network/cert_verifier_with_trust_anchors.h +++ b/chromium/services/network/cert_verifier_with_trust_anchors.h @@ -42,8 +42,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CertVerifierWithTrustAnchors // this method. void InitializeOnIOThread(std::unique_ptr<net::CertVerifier> delegate); - // Sets the additional trust anchors. - void SetTrustAnchors(const net::CertificateList& trust_anchors); + // Sets the additional trust anchors and untrusted authorities to be + // considered as intermediates. + void SetAdditionalCerts(const net::CertificateList& trust_anchors, + const net::CertificateList& untrusted_authorities); // CertVerifier: int Verify(const RequestParams& params, @@ -56,6 +58,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CertVerifierWithTrustAnchors private: net::CertVerifier::Config orig_config_; net::CertificateList trust_anchors_; + net::CertificateList untrusted_authorities_; base::RepeatingClosure anchor_used_callback_; std::unique_ptr<CertVerifier> delegate_; THREAD_CHECKER(thread_checker_); diff --git a/chromium/services/network/cert_verifier_with_trust_anchors_unittest.cc b/chromium/services/network/cert_verifier_with_trust_anchors_unittest.cc index 7ec5f95ab8c..26acf6497ff 100644 --- a/chromium/services/network/cert_verifier_with_trust_anchors_unittest.cc +++ b/chromium/services/network/cert_verifier_with_trust_anchors_unittest.cc @@ -227,7 +227,8 @@ TEST_F(CertVerifierWithTrustAnchorsTest, VerifyUsingAdditionalTrustAnchor) { ASSERT_FALSE(test_ca_x509cert_list.empty()); // Verify() again with the additional trust anchors. - cert_verifier_->SetTrustAnchors(test_ca_x509cert_list); + cert_verifier_->SetAdditionalCerts(test_ca_x509cert_list, + net::CertificateList()); { net::CertVerifyResult verify_result; net::TestCompletionCallback callback; @@ -242,7 +243,8 @@ TEST_F(CertVerifierWithTrustAnchorsTest, VerifyUsingAdditionalTrustAnchor) { EXPECT_TRUE(WasTrustAnchorUsedAndReset()); // Verify() again with the additional trust anchors will hit the cache. - cert_verifier_->SetTrustAnchors(test_ca_x509cert_list); + cert_verifier_->SetAdditionalCerts(test_ca_x509cert_list, + net::CertificateList()); { net::CertVerifyResult verify_result; net::TestCompletionCallback callback; @@ -254,7 +256,8 @@ TEST_F(CertVerifierWithTrustAnchorsTest, VerifyUsingAdditionalTrustAnchor) { EXPECT_TRUE(WasTrustAnchorUsedAndReset()); // Verifying after removing the trust anchors should now fail. - cert_verifier_->SetTrustAnchors(net::CertificateList()); + cert_verifier_->SetAdditionalCerts(net::CertificateList(), + net::CertificateList()); { net::CertVerifyResult verify_result; net::TestCompletionCallback callback; @@ -296,7 +299,8 @@ TEST_F(CertVerifierWithTrustAnchorsTest, ASSERT_FALSE(test_ca_x509cert_list.empty()); // Verify() again with the additional trust anchors. - cert_verifier_->SetTrustAnchors(test_ca_x509cert_list); + cert_verifier_->SetAdditionalCerts(test_ca_x509cert_list, + net::CertificateList()); { net::CertVerifyResult verify_result; net::TestCompletionCallback callback; diff --git a/chromium/services/network/chunked_data_pipe_upload_data_stream.cc b/chromium/services/network/chunked_data_pipe_upload_data_stream.cc index e5c53e606e2..305bfa2ef3c 100644 --- a/chromium/services/network/chunked_data_pipe_upload_data_stream.cc +++ b/chromium/services/network/chunked_data_pipe_upload_data_stream.cc @@ -33,6 +33,10 @@ ChunkedDataPipeUploadDataStream::ChunkedDataPipeUploadDataStream( ChunkedDataPipeUploadDataStream::~ChunkedDataPipeUploadDataStream() {} +bool ChunkedDataPipeUploadDataStream::AllowHTTP1() const { + return resource_request_body_->AllowHTTP1ForStreamingUpload(); +} + int ChunkedDataPipeUploadDataStream::InitInternal( const net::NetLogWithSource& net_log) { // If there was an error either passed to the ReadCallback or as a result of diff --git a/chromium/services/network/chunked_data_pipe_upload_data_stream.h b/chromium/services/network/chunked_data_pipe_upload_data_stream.h index 45f7c2b5aa4..10590039c39 100644 --- a/chromium/services/network/chunked_data_pipe_upload_data_stream.h +++ b/chromium/services/network/chunked_data_pipe_upload_data_stream.h @@ -43,6 +43,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ChunkedDataPipeUploadDataStream ~ChunkedDataPipeUploadDataStream() override; + bool AllowHTTP1() const override; + private: // net::UploadDataStream implementation. int InitInternal(const net::NetLogWithSource& net_log) override; diff --git a/chromium/services/network/cookie_manager_unittest.cc b/chromium/services/network/cookie_manager_unittest.cc index 9f7bad7172d..b66d553eb79 100644 --- a/chromium/services/network/cookie_manager_unittest.cc +++ b/chromium/services/network/cookie_manager_unittest.cc @@ -18,6 +18,7 @@ #include "base/test/task_environment.h" #include "base/time/time.h" #include "net/cookies/cookie_constants.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_store_test_callbacks.h" @@ -109,25 +110,26 @@ class SynchronousCookieManager { url, options, base::BindLambdaForTesting( [&run_loop, &cookies_out]( - const net::CookieStatusList& cookies, - const net::CookieStatusList& excluded_cookies) { - cookies_out = net::cookie_util::StripStatuses(cookies); + const net::CookieAccessResultList& cookies, + const net::CookieAccessResultList& excluded_cookies) { + cookies_out = net::cookie_util::StripAccessResults(cookies); run_loop.Quit(); })); run_loop.Run(); return cookies_out; } - net::CookieStatusList GetExcludedCookieList(const GURL& url, - net::CookieOptions options) { + net::CookieAccessResultList GetExcludedCookieList( + const GURL& url, + net::CookieOptions options) { base::RunLoop run_loop; - net::CookieStatusList cookies_out; + net::CookieAccessResultList cookies_out; cookie_service_->GetCookieList( url, options, base::BindLambdaForTesting( [&run_loop, &cookies_out]( - const net::CookieStatusList& cookies, - const net::CookieStatusList& excluded_cookies) { + const net::CookieAccessResultList& cookies, + const net::CookieAccessResultList& excluded_cookies) { cookies_out = excluded_cookies; run_loop.Quit(); })); @@ -139,8 +141,8 @@ class SynchronousCookieManager { std::string source_scheme, bool modify_http_only) { base::RunLoop run_loop; - net::CanonicalCookie::CookieInclusionStatus result_out( - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR); + net::CookieInclusionStatus result_out( + net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR); net::CookieOptions options; options.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::MakeInclusive()); @@ -150,8 +152,7 @@ class SynchronousCookieManager { cookie, net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, base::BindLambdaForTesting( - [&run_loop, - &result_out](net::CanonicalCookie::CookieInclusionStatus result) { + [&run_loop, &result_out](net::CookieInclusionStatus result) { result_out = result; run_loop.Quit(); })); @@ -160,7 +161,7 @@ class SynchronousCookieManager { return result_out.IsInclude(); } - net::CanonicalCookie::CookieInclusionStatus SetCanonicalCookieWithStatus( + net::CookieInclusionStatus SetCanonicalCookieWithStatus( const net::CanonicalCookie& cookie, std::string source_scheme, bool modify_http_only) { @@ -170,14 +171,13 @@ class SynchronousCookieManager { net::CookieOptions::SameSiteCookieContext::MakeInclusive()); if (modify_http_only) options.set_include_httponly(); - net::CanonicalCookie::CookieInclusionStatus result_out( - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR); + net::CookieInclusionStatus result_out( + net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR); cookie_service_->SetCanonicalCookie( cookie, net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, base::BindLambdaForTesting( - [&run_loop, - &result_out](net::CanonicalCookie::CookieInclusionStatus result) { + [&run_loop, &result_out](net::CookieInclusionStatus result) { result_out = result; run_loop.Quit(); })); @@ -262,8 +262,7 @@ class CookieManagerTest : public testing::Test { bool SetCanonicalCookie(const net::CanonicalCookie& cookie, std::string source_scheme, bool can_modify_httponly) { - net::ResultSavingCookieCallback<net::CanonicalCookie::CookieInclusionStatus> - callback; + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; net::CookieOptions options; options.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::MakeInclusive()); @@ -273,9 +272,9 @@ class CookieManagerTest : public testing::Test { cookie_monster_->SetCanonicalCookieAsync( std::make_unique<net::CanonicalCookie>(cookie), net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, - base::BindOnce(&net::ResultSavingCookieCallback< - net::CanonicalCookie::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback))); callback.WaitUntilDone(); return callback.result().IsInclude(); } @@ -589,7 +588,7 @@ TEST_F(CookieManagerTest, GetCookieList) { net::CookieOptions excluded_options = options; excluded_options.set_return_excluded_cookies(); - net::CookieStatusList excluded_cookies = + net::CookieAccessResultList excluded_cookies = service_wrapper()->GetExcludedCookieList( GURL("https://foo_host.com/with/path"), excluded_options); @@ -597,8 +596,9 @@ TEST_F(CookieManagerTest, GetCookieList) { EXPECT_EQ("HttpOnly", excluded_cookies[0].cookie.Name()); EXPECT_EQ("F", excluded_cookies[0].cookie.Value()); - EXPECT_TRUE(excluded_cookies[0].status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_HTTP_ONLY})); + EXPECT_TRUE(excluded_cookies[0] + .access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_HTTP_ONLY})); } TEST_F(CookieManagerTest, GetCookieListHttpOnly) { @@ -634,7 +634,7 @@ TEST_F(CookieManagerTest, GetCookieListHttpOnly) { options.set_return_excluded_cookies(); - net::CookieStatusList excluded_cookies = + net::CookieAccessResultList excluded_cookies = service_wrapper()->GetExcludedCookieList( GURL("https://foo_host.com/with/path"), options); ASSERT_EQ(1u, excluded_cookies.size()); @@ -690,7 +690,7 @@ TEST_F(CookieManagerTest, GetCookieListSameSite) { options.set_return_excluded_cookies(); - net::CookieStatusList excluded_cookies = + net::CookieAccessResultList excluded_cookies = service_wrapper()->GetExcludedCookieList( GURL("https://foo_host.com/with/path"), options); ASSERT_EQ(2u, excluded_cookies.size()); @@ -842,8 +842,7 @@ TEST_F(CookieManagerTest, ConfirmSecureSetFails) { net::COOKIE_PRIORITY_MEDIUM), "http", false) .HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_SECURE_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -861,8 +860,7 @@ TEST_F(CookieManagerTest, ConfirmHttpOnlySetFails) { net::COOKIE_PRIORITY_MEDIUM), "http", false) .HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_HTTP_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_HTTP_ONLY})); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -888,8 +886,7 @@ TEST_F(CookieManagerTest, ConfirmSecureOverwriteFails) { net::COOKIE_PRIORITY_MEDIUM), "http", false) .HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_OVERWRITE_SECURE})); + {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE})); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -918,8 +915,7 @@ TEST_F(CookieManagerTest, ConfirmHttpOnlyOverwriteFails) { net::COOKIE_PRIORITY_MEDIUM), "https", false) .HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_OVERWRITE_HTTP_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY})); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); diff --git a/chromium/services/network/cookie_settings.cc b/chromium/services/network/cookie_settings.cc index 099a4518415..5122960ccdd 100644 --- a/chromium/services/network/cookie_settings.cc +++ b/chromium/services/network/cookie_settings.cc @@ -184,16 +184,25 @@ void CookieSettings::GetCookieSettingInternal( // We'll only utilize the SAA grant if our value is set to // CONTENT_SETTING_ALLOW as other values would indicate the user // rejected a prompt to allow access. - if (storage_access_setting == CONTENT_SETTING_ALLOW) + if (storage_access_setting == CONTENT_SETTING_ALLOW) { block = false; + FireStorageAccessHistogram(net::cookie_util::StorageAccessResult:: + ACCESS_ALLOWED_STORAGE_ACCESS_GRANT); + } break; } } + } else { + FireStorageAccessHistogram( + net::cookie_util::StorageAccessResult::ACCESS_ALLOWED); } - if (block) + if (block) { *cookie_setting = CONTENT_SETTING_BLOCK; + FireStorageAccessHistogram( + net::cookie_util::StorageAccessResult::ACCESS_BLOCKED); + } } bool CookieSettings::HasSessionOnlyOrigins() const { diff --git a/chromium/services/network/cookie_settings_unittest.cc b/chromium/services/network/cookie_settings_unittest.cc index 04bdf94e067..4e902b52288 100644 --- a/chromium/services/network/cookie_settings_unittest.cc +++ b/chromium/services/network/cookie_settings_unittest.cc @@ -4,6 +4,7 @@ #include "services/network/cookie_settings.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "net/base/features.h" @@ -15,6 +16,9 @@ namespace network { namespace { +constexpr char kAllowedRequestsHistogram[] = + "API.StorageAccess.AllowedRequests"; + constexpr char kDomainURL[] = "http://example.com"; constexpr char kURL[] = "http://foo.com"; constexpr char kOtherURL[] = "http://other.com"; @@ -90,6 +94,9 @@ TEST_F(CookieSettingsTest, GetCookieSettingGetsFirstSetting) { } TEST_F(CookieSettingsTest, GetCookieSettingDontBlockThirdParty) { + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0); + CookieSettings settings; settings.set_content_settings( {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)}); @@ -97,6 +104,11 @@ TEST_F(CookieSettingsTest, GetCookieSettingDontBlockThirdParty) { ContentSetting setting; settings.GetCookieSetting(GURL(kURL), GURL(kOtherURL), nullptr, &setting); EXPECT_EQ(setting, CONTENT_SETTING_ALLOW); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult::ACCESS_ALLOWED), + 1); } TEST_F(CookieSettingsTest, GetCookieSettingBlockThirdParty) { @@ -126,6 +138,9 @@ TEST_F(CookieSettingsTest, GetCookieSettingSAAUnblocks) { GURL url = GURL(kOtherURL); GURL third_url = GURL(kDomainURL); + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0); + CookieSettings settings; settings.set_content_settings( {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)}); @@ -140,11 +155,27 @@ TEST_F(CookieSettingsTest, GetCookieSettingSAAUnblocks) { ContentSetting setting; settings.GetCookieSetting(url, top_level_url, nullptr, &setting); EXPECT_EQ(setting, CONTENT_SETTING_ALLOW); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult:: + ACCESS_ALLOWED_STORAGE_ACCESS_GRANT), + 1); // Invalid pair the |top_level_url| granting access to |url| is now // being loaded under |url| as the top level url. settings.GetCookieSetting(top_level_url, url, nullptr, &setting); EXPECT_EQ(setting, CONTENT_SETTING_BLOCK); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 2); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult:: + ACCESS_ALLOWED_STORAGE_ACCESS_GRANT), + 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult::ACCESS_BLOCKED), + 1); // Invalid pairs where a |third_url| is used. settings.GetCookieSetting(url, third_url, nullptr, &setting); diff --git a/chromium/services/network/cors/cors_url_loader.cc b/chromium/services/network/cors/cors_url_loader.cc index 30bb273eae1..2e7f769c7a3 100644 --- a/chromium/services/network/cors/cors_url_loader.cc +++ b/chromium/services/network/cors/cors_url_loader.cc @@ -11,6 +11,7 @@ #include "base/stl_util.h" #include "base/strings/string_split.h" #include "net/base/load_flags.h" +#include "net/http/http_status_code.h" #include "services/network/cors/cors_url_loader_factory.h" #include "services/network/cors/preflight_controller.h" #include "services/network/public/cpp/cors/cors.h" @@ -102,7 +103,8 @@ CorsURLLoader::CorsURLLoader( const OriginAccessList* factory_bound_origin_access_list, PreflightController* preflight_controller, const base::flat_set<std::string>* allowed_exempt_headers, - bool allow_any_cors_exempt_header) + bool allow_any_cors_exempt_header, + const net::IsolationInfo& isolation_info) : receiver_(this, std::move(loader_receiver)), process_id_(process_id), routing_id_(routing_id), @@ -118,7 +120,8 @@ CorsURLLoader::CorsURLLoader( preflight_controller_(preflight_controller), allowed_exempt_headers_(allowed_exempt_headers), skip_cors_enabled_scheme_check_(skip_cors_enabled_scheme_check), - allow_any_cors_exempt_header_(allow_any_cors_exempt_header) { + allow_any_cors_exempt_header_(allow_any_cors_exempt_header), + isolation_info_(isolation_info) { if (ignore_isolated_world_origin) request_.isolated_world_origin = base::nullopt; @@ -215,6 +218,13 @@ void CorsURLLoader::FollowRedirect( request_.method = redirect_info_.new_method; request_.referrer = GURL(redirect_info_.new_referrer); request_.referrer_policy = redirect_info_.new_referrer_policy; + request_.site_for_cookies = redirect_info_.new_site_for_cookies; + + if (request_.trusted_params) { + request_.trusted_params->isolation_info = + request_.trusted_params->isolation_info.CreateForRedirect( + url::Origin::Create(request_.url)); + } // The request method can be changed to "GET". In this case we need to // reset the request body manually. @@ -358,10 +368,13 @@ void CorsURLLoader::OnReceiveRedirect(const net::RedirectInfo& redirect_info, return; } - // TODO(yhirano): Implement the following (Note: this is needed when upload - // streaming is implemented): // If |actualResponse|’s status is not 303, |request|’s body is non-null, and // |request|’s body’s source is null, then return a network error. + if (redirect_info.status_code != net::HTTP_SEE_OTHER && + network::URLLoader::HasFetchStreamingUploadBody(&request_)) { + HandleComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT)); + return; + } // If |actualResponse|’s location URL’s origin is not same origin with // |request|’s current url’s origin and |request|’s origin is not same origin @@ -515,7 +528,7 @@ void CorsURLLoader::StartRequest() { PreflightController::WithTrustedHeaderClient( options_ & mojom::kURLLoadOptionUseHeaderClient), tainted_, net::NetworkTrafficAnnotationTag(traffic_annotation_), - network_loader_factory_, process_id_); + network_loader_factory_, process_id_, isolation_info_); } void CorsURLLoader::StartNetworkRequest( @@ -532,10 +545,12 @@ void CorsURLLoader::StartNetworkRequest( // network::URLLoader doesn't understand |kSameOrigin|. // TODO(crbug.com/943939): Fix this. auto original_credentials_mode = request_.credentials_mode; - request_.credentials_mode = - CalculateCredentialsFlag(original_credentials_mode, response_tainting_) - ? mojom::CredentialsMode::kInclude - : mojom::CredentialsMode::kOmit; + if (original_credentials_mode == mojom::CredentialsMode::kSameOrigin) { + request_.credentials_mode = + CalculateCredentialsFlag(original_credentials_mode, response_tainting_) + ? mojom::CredentialsMode::kInclude + : mojom::CredentialsMode::kOmit; + } // Binding |this| as an unretained pointer is safe because // |network_client_receiver_| shares this object's lifetime. diff --git a/chromium/services/network/cors/cors_url_loader.h b/chromium/services/network/cors/cors_url_loader.h index d3d5059e7de..98de4924bdc 100644 --- a/chromium/services/network/cors/cors_url_loader.h +++ b/chromium/services/network/cors/cors_url_loader.h @@ -54,7 +54,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CorsURLLoader const OriginAccessList* factory_bound_origin_access_list, PreflightController* preflight_controller, const base::flat_set<std::string>* allowed_exempt_headers, - bool allow_any_cors_exempt_header); + bool allow_any_cors_exempt_header, + const net::IsolationInfo& isolation_info); ~CorsURLLoader() override; @@ -192,6 +193,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CorsURLLoader const bool allow_any_cors_exempt_header_; + net::IsolationInfo isolation_info_; + // Used to run asynchronous class instance bound callbacks safely. base::WeakPtrFactory<CorsURLLoader> weak_factory_{this}; diff --git a/chromium/services/network/cors/cors_url_loader_factory.cc b/chromium/services/network/cors/cors_url_loader_factory.cc index 26dfa0b5df2..9c73e446778 100644 --- a/chromium/services/network/cors/cors_url_loader_factory.cc +++ b/chromium/services/network/cors/cors_url_loader_factory.cc @@ -23,8 +23,10 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/header_util.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" #include "services/network/public/cpp/request_mode.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/trust_token_operation_authorization.h" #include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" #include "services/network/url_loader.h" @@ -35,6 +37,58 @@ namespace network { namespace cors { +namespace { + +// Verifies state that should hold for Trust Tokens parameters provided by a +// functioning renderer: +// - Trust Tokens should be enabled +// - the request should come from a trustworthy context +// - if the request is for redemption or signing, it should be from a context +// where these operations are permitted (as specified by +// URLLoaderFactoryParams::trust_token_redemption_policy). +bool VerifyTrustTokenParamsIntegrityIfPresent( + const ResourceRequest& url_request, + const NetworkContext* context, + mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy) { + if (!url_request.trust_token_params) + return true; + + if (!context->trust_token_store()) { + // Got a request with Trust Tokens parameters with Trust tokens + // disabled. + // + // Here and below, we use concise error messages to make them easier to + // search search. + mojo::ReportBadMessage( + "TrustTokenParamsIntegrity: TrustTokensRequestWithTrustTokensDisabled"); + return false; + } + + if (url_request.request_initiator && + !IsOriginPotentiallyTrustworthy(*url_request.request_initiator)) { + // Got a request with Trust Tokens parameters from an insecure context, + // but Trust Tokens operations may only be executed from secure + // contexts. + mojo::ReportBadMessage("TrustTokenParamsIntegrity: NotFromSecureContext"); + return false; + } + + if (trust_token_redemption_policy == + mojom::TrustTokenRedemptionPolicy::kForbid && + DoesTrustTokenOperationRequireFeaturePolicy( + url_request.trust_token_params->type)) { + // Got a request configured for Trust Tokens redemption or signing from + // a context in which this operation is prohibited. + mojo::ReportBadMessage( + "TrustTokenParamsIntegrity: MissingRequiredFeaturePolicy"); + return false; + } + + return true; +} + +} // namespace + class CorsURLLoaderFactory::FactoryOverride final { public: class ExposedNetworkLoaderFactory final : public mojom::URLLoaderFactory { @@ -114,10 +168,14 @@ CorsURLLoaderFactory::CorsURLLoaderFactory( process_id_(params->process_id), request_initiator_site_lock_(params->request_initiator_site_lock), ignore_isolated_world_origin_(params->ignore_isolated_world_origin), + trust_token_redemption_policy_(params->trust_token_redemption_policy), + isolation_info_(params->isolation_info), origin_access_list_(origin_access_list) { DCHECK(context_); DCHECK(origin_access_list_); DCHECK_NE(mojom::kInvalidProcessId, process_id_); + DCHECK_EQ(net::IsolationInfo::RedirectMode::kUpdateNothing, + params->isolation_info.redirect_mode()); if (params->automatically_assign_isolation_info) { DCHECK(params->isolation_info.IsEmpty()); // Only the browser process is currently permitted to use automatically @@ -202,7 +260,7 @@ void CorsURLLoaderFactory::CreateLoaderAndStart( origin_access_list_, factory_bound_origin_access_list_.get(), context_->cors_preflight_controller(), context_->cors_exempt_header_list(), - GetAllowAnyCorsExemptHeaderForBrowser()); + GetAllowAnyCorsExemptHeaderForBrowser(), isolation_info_); auto* raw_loader = loader.get(); OnLoaderCreated(std::move(loader)); raw_loader->Start(); @@ -347,7 +405,15 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, case InitiatorLockCompatibility::kIncorrectLock: // Requests from the renderer need to always specify a correct initiator. NOTREACHED(); - // TODO(lukasza): https://crbug.com/920634: Return false below. + if (base::FeatureList::IsEnabled( + features::kRequestInitiatorSiteLockEnfocement)) { + url::debug::ScopedOriginCrashKey initiator_lock_crash_key( + debug::GetRequestInitiatorSiteLockCrashKey(), + base::OptionalOrNullptr(request_initiator_site_lock_)); + mojo::ReportBadMessage( + "CorsURLLoaderFactory: lock VS initiator mismatch"); + return false; + } break; } @@ -395,6 +461,13 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, } } + if (!VerifyTrustTokenParamsIntegrityIfPresent( + request, context_, trust_token_redemption_policy_)) { + // VerifyTrustTokenParamsIntegrityIfPresent will report an appropriate bad + // message. + return false; + } + // TODO(yhirano): If the request mode is "no-cors", the redirect mode should // be "follow". return true; diff --git a/chromium/services/network/cors/cors_url_loader_factory.h b/chromium/services/network/cors/cors_url_loader_factory.h index ca9f7940484..1dfdeac49f8 100644 --- a/chromium/services/network/cors/cors_url_loader_factory.h +++ b/chromium/services/network/cors/cors_url_loader_factory.h @@ -107,6 +107,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CorsURLLoaderFactory final const int32_t process_id_ = mojom::kInvalidProcessId; const base::Optional<url::Origin> request_initiator_site_lock_; const bool ignore_isolated_world_origin_; + const mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy_; + net::IsolationInfo isolation_info_; // Relative order of |network_loader_factory_| and |loaders_| matters - // URLLoaderFactory needs to live longer than URLLoaders created using the diff --git a/chromium/services/network/cors/cors_url_loader_factory_unittest.cc b/chromium/services/network/cors/cors_url_loader_factory_unittest.cc index a31fd61f6f2..20e261e4a92 100644 --- a/chromium/services/network/cors/cors_url_loader_factory_unittest.cc +++ b/chromium/services/network/cors/cors_url_loader_factory_unittest.cc @@ -21,6 +21,7 @@ #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/resource_scheduler/resource_scheduler.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_url_loader_client.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -54,6 +55,10 @@ class CorsURLLoaderFactoryTest : public testing::Test { network_service_ = NetworkService::CreateForTesting(); auto context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); // Use a fixed proxy config, to avoid dependencies on local network // configuration. context_params->initial_proxy_config = diff --git a/chromium/services/network/cors/cors_url_loader_unittest.cc b/chromium/services/network/cors/cors_url_loader_unittest.cc index 09b1097cddb..3d64f7ef8c1 100644 --- a/chromium/services/network/cors/cors_url_loader_unittest.cc +++ b/chromium/services/network/cors/cors_url_loader_unittest.cc @@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "base/bind_helpers.h" #include "base/check.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -20,8 +21,8 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" -#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/system/functions.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" @@ -39,6 +40,7 @@ #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/resource_scheduler/resource_scheduler.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_url_loader_client.h" #include "services/network/url_loader.h" #include "testing/gmock/include/gmock/gmock.h" @@ -162,6 +164,12 @@ class CorsURLLoaderTest : public testing::Test { network_service_ = NetworkService::CreateForTesting(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); + // Use a fixed proxy config, to avoid dependencies on local network + // configuration. context_params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect(); context_params->cors_exempt_header_list.push_back(kTestCorsExemptHeader); @@ -325,13 +333,15 @@ class CorsURLLoaderTest : public testing::Test { base::StringPiece method, const GURL& url, base::StringPiece referrer = base::StringPiece(), - ReferrerPolicy referrer_policy = net::URLRequest::NO_REFERRER) { + ReferrerPolicy referrer_policy = net::URLRequest::NO_REFERRER, + net::SiteForCookies site_for_cookies = net::SiteForCookies()) { net::RedirectInfo redirect_info; redirect_info.status_code = status_code; redirect_info.new_method = method.as_string(); redirect_info.new_url = url; redirect_info.new_referrer = referrer.as_string(); redirect_info.new_referrer_policy = referrer_policy; + redirect_info.new_site_for_cookies = site_for_cookies; return redirect_info; } @@ -431,13 +441,12 @@ class BadMessageTestHelper { public: BadMessageTestHelper() : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) { - mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating( + mojo::SetDefaultProcessErrorHandler(base::BindRepeating( &BadMessageTestHelper::OnBadMessage, base::Unretained(this))); } ~BadMessageTestHelper() { - mojo::core::SetDefaultProcessErrorCallback( - mojo::core::ProcessErrorCallback()); + mojo::SetDefaultProcessErrorHandler(base::NullCallback()); } const std::vector<std::string>& bad_message_reports() const { @@ -1154,9 +1163,11 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { RunUntilCreateLoaderAndStartCalled(); EXPECT_EQ(1, num_created_loaders()); - EXPECT_EQ(GetRequest().url, url); - EXPECT_EQ(GetRequest().method, "POST"); - EXPECT_EQ(GetRequest().referrer, url); + EXPECT_EQ(url, GetRequest().url); + EXPECT_EQ("POST", GetRequest().method); + EXPECT_EQ(url, GetRequest().referrer); + EXPECT_EQ(net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, + GetRequest().referrer_policy); NotifyLoaderClientOnReceiveRedirect(CreateRedirectInfo( 303, "GET", new_url, "https://other.example.com", @@ -1173,9 +1184,86 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { RunUntilCreateLoaderAndStartCalled(); EXPECT_EQ(2, num_created_loaders()); - EXPECT_EQ(GetRequest().url, new_url); - EXPECT_EQ(GetRequest().referrer, GURL("https://other.example.com")); - EXPECT_EQ(GetRequest().method, "GET"); + EXPECT_EQ(new_url, GetRequest().url); + EXPECT_EQ("GET", GetRequest().method); + EXPECT_EQ(GURL("https://other.example.com"), GetRequest().referrer); + EXPECT_EQ(net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, + GetRequest().referrer_policy); + + NotifyLoaderClientOnReceiveResponse( + {{"Access-Control-Allow-Origin", "https://example.com"}}); + NotifyLoaderClientOnComplete(net::OK); + RunUntilComplete(); + + EXPECT_FALSE(client().has_received_redirect()); + EXPECT_TRUE(client().has_received_response()); + EXPECT_TRUE(client().has_received_completion()); + EXPECT_EQ(net::OK, client().completion_status().error_code); +} + +// Makes sure that if an intercepted redirect updates the IsolationInfo and the +// SiteForCookies values, the CorsURLLoader respects those changes. The former +// only happens for frames, and the latter for subframes, but should make +// assumptions about whether these need to be updated in CorsURLLoader. +TEST_F(CorsURLLoaderTest, + InterceptedRedirectChangesIsolationInfoAndSiteForCookies) { + auto params = network::mojom::URLLoaderFactoryParams::New(); + ResetFactory(base::nullopt, kRendererProcessId, true /* is_trusted */, + params->ignore_isolated_world_origin, + false /* skip_cors_enabled_scheme_check */); + + const GURL url("https://example.com/foo.png"); + const url::Origin url_origin = url::Origin::Create(url); + const net::SiteForCookies url_site_for_cookies = + net::SiteForCookies::FromOrigin(url_origin); + + const GURL new_url("https://other.example.com/foo.png"); + const url::Origin new_url_origin = url::Origin::Create(new_url); + const net::SiteForCookies new_url_site_for_cookies = + net::SiteForCookies::FromOrigin(new_url_origin); + + ResourceRequest request; + request.mode = mojom::RequestMode::kCors; + request.credentials_mode = mojom::CredentialsMode::kOmit; + request.url = url; + request.request_initiator = url_origin; + request.site_for_cookies = url_site_for_cookies; + request.update_first_party_url_on_redirect = true; + request.trusted_params = ResourceRequest::TrustedParams(); + request.trusted_params->isolation_info = net::IsolationInfo::Create( + net::IsolationInfo::RedirectMode::kUpdateTopFrame, + url_origin /* top_frame_origin */, url_origin /* frame_origin */, + url_site_for_cookies); + CreateLoaderAndStart(request); + RunUntilCreateLoaderAndStartCalled(); + + EXPECT_EQ(1, num_created_loaders()); + EXPECT_EQ(url, GetRequest().url); + + NotifyLoaderClientOnReceiveRedirect(CreateRedirectInfo( + 303, "GET", new_url, "" /* referrer */, net::URLRequest::NO_REFERRER, + new_url_site_for_cookies)); + RunUntilRedirectReceived(); + + EXPECT_TRUE(IsNetworkLoaderStarted()); + EXPECT_FALSE(client().has_received_completion()); + EXPECT_FALSE(client().has_received_response()); + EXPECT_TRUE(client().has_received_redirect()); + + ClearHasReceivedRedirect(); + FollowRedirect(); + RunUntilCreateLoaderAndStartCalled(); + + EXPECT_EQ(2, num_created_loaders()); + EXPECT_EQ(new_url, GetRequest().url); + EXPECT_EQ("GET", GetRequest().method); + EXPECT_TRUE( + GetRequest().site_for_cookies.IsEquivalent(new_url_site_for_cookies)); + EXPECT_TRUE(GetRequest().trusted_params->isolation_info.IsEqualForTesting( + net::IsolationInfo::Create( + net::IsolationInfo::RedirectMode::kUpdateTopFrame, + new_url_origin /* top_frame_origin */, + new_url_origin /* frame_origin */, new_url_site_for_cookies))); NotifyLoaderClientOnReceiveResponse( {{"Access-Control-Allow-Origin", "https://example.com"}}); diff --git a/chromium/services/network/cors/preflight_controller.cc b/chromium/services/network/cors/preflight_controller.cc index 24f48caf747..84bc1ec35d0 100644 --- a/chromium/services/network/cors/preflight_controller.cc +++ b/chromium/services/network/cors/preflight_controller.cc @@ -200,12 +200,14 @@ class PreflightController::PreflightLoader final { WithTrustedHeaderClient with_trusted_header_client, bool tainted, const net::NetworkTrafficAnnotationTag& annotation_tag, - int32_t process_id) + int32_t process_id, + const net::NetworkIsolationKey& network_isolation_key) : controller_(controller), completion_callback_(std::move(completion_callback)), original_request_(request), tainted_(tainted), - process_id_(process_id) { + process_id_(process_id), + network_isolation_key_(network_isolation_key) { auto* network_service_client = MaybeGetNetworkServiceClientForDevTools(); if (network_service_client) devtools_request_id_ = base::UnguessableToken::Create(); @@ -296,7 +298,8 @@ class PreflightController::PreflightLoader final { if (!(original_request_.load_flags & net::LOAD_DISABLE_CACHE) && !detected_error_status) { controller_->AppendToCache(*original_request_.request_initiator, - original_request_.url, std::move(result)); + original_request_.url, network_isolation_key_, + std::move(result)); } std::move(completion_callback_) @@ -357,6 +360,7 @@ class PreflightController::PreflightLoader final { const bool tainted_; const int32_t process_id_; base::Optional<base::UnguessableToken> devtools_request_id_; + const net::NetworkIsolationKey network_isolation_key_; DISALLOW_COPY_AND_ASSIGN(PreflightLoader); }; @@ -397,21 +401,28 @@ void PreflightController::PerformPreflightCheck( bool tainted, const net::NetworkTrafficAnnotationTag& annotation_tag, mojom::URLLoaderFactory* loader_factory, - int32_t process_id) { + int32_t process_id, + const net::IsolationInfo& isolation_info) { DCHECK(request.request_initiator); + const net::NetworkIsolationKey& network_isolation_key = + !isolation_info.IsEmpty() + ? isolation_info.network_isolation_key() + : request.trusted_params.has_value() + ? request.trusted_params->isolation_info.network_isolation_key() + : net::NetworkIsolationKey(); if (!RetrieveCacheFlags(request.load_flags) && !request.is_external_request && cache_.CheckIfRequestCanSkipPreflight( - request.request_initiator.value(), request.url, - net::NetworkIsolationKey::Todo(), request.credentials_mode, - request.method, request.headers, request.is_revalidating)) { + request.request_initiator.value(), request.url, network_isolation_key, + request.credentials_mode, request.method, request.headers, + request.is_revalidating)) { std::move(callback).Run(net::OK, base::nullopt); return; } auto emplaced_pair = loaders_.emplace(std::make_unique<PreflightLoader>( this, std::move(callback), request, with_trusted_header_client, tainted, - annotation_tag, process_id)); + annotation_tag, process_id, network_isolation_key)); (*emplaced_pair.first)->Request(loader_factory); } @@ -424,9 +435,9 @@ void PreflightController::RemoveLoader(PreflightLoader* loader) { void PreflightController::AppendToCache( const url::Origin& origin, const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, std::unique_ptr<PreflightResult> result) { - cache_.AppendEntry(origin, url, net::NetworkIsolationKey::Todo(), - std::move(result)); + cache_.AppendEntry(origin, url, network_isolation_key, std::move(result)); } } // namespace cors diff --git a/chromium/services/network/cors/preflight_controller.h b/chromium/services/network/cors/preflight_controller.h index 737bfcabfb7..437840d7049 100644 --- a/chromium/services/network/cors/preflight_controller.h +++ b/chromium/services/network/cors/preflight_controller.h @@ -67,7 +67,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final { bool tainted, const net::NetworkTrafficAnnotationTag& traffic_annotation, mojom::URLLoaderFactory* loader_factory, - int32_t process_id); + int32_t process_id, + const net::IsolationInfo& isolation_info); const base::flat_set<std::string>& extra_safelisted_header_names() const { return extra_safelisted_header_names_; @@ -84,6 +85,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final { void RemoveLoader(PreflightLoader* loader); void AppendToCache(const url::Origin& origin, const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, std::unique_ptr<PreflightResult> result); NetworkService* network_service() { return network_service_; } diff --git a/chromium/services/network/cors/preflight_controller_unittest.cc b/chromium/services/network/cors/preflight_controller_unittest.cc index 3e6f9595364..c252df4121a 100644 --- a/chromium/services/network/cors/preflight_controller_unittest.cc +++ b/chromium/services/network/cors/preflight_controller_unittest.cc @@ -21,8 +21,10 @@ #include "services/network/cors/cors_url_loader_factory.h" #include "services/network/network_service.h" #include "services/network/public/cpp/cors/cors.h" +#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_network_service_client.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" @@ -236,12 +238,14 @@ TEST(PreflightControllerOptionsTest, CheckOptions) { preflight_controller.PerformPreflightCheck( base::BindOnce([](int, base::Optional<CorsErrorStatus>) {}), request, WithTrustedHeaderClient(false), false /* tainted */, - TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, 0 /* process_id */); + TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, 0 /* process_id */, + net::IsolationInfo()); preflight_controller.PerformPreflightCheck( base::BindOnce([](int, base::Optional<CorsErrorStatus>) {}), request, WithTrustedHeaderClient(true), false /* tainted */, - TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, 0 /* process_id */); + TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, 0 /* process_id */, + net::IsolationInfo()); ASSERT_EQ(2, url_loader_factory.NumPending()); EXPECT_EQ(mojom::kURLLoadOptionAsCorsPreflight, @@ -288,7 +292,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { int32_t process_id, int32_t routing_id, const std::string& devtools_request_id, - const net::CookieStatusList& cookies_with_status, + const net::CookieAccessResultList& cookies_with_access_result, std::vector<network::mojom::HttpRawHeaderPairPtr> headers) override { on_raw_request_called_ = true; } @@ -341,15 +345,21 @@ class PreflightControllerTest : public testing::Test { PreflightControllerTest() : task_environment_(base::test::TaskEnvironment::MainThreadType::IO), test_initiator_origin_( - url::Origin::Create(GURL("http://example.com/"))) { + url::Origin::Create(GURL("http://example.com/"))), + access_control_allow_origin_(test_initiator_origin_) { CorsURLLoaderFactory::SetAllowExternalPreflightsForTesting(true); mojo::Remote<mojom::NetworkService> network_service_remote; network_service_ = NetworkService::Create( network_service_remote.BindNewPipeAndPassReceiver()); + auto context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); network_service_remote->CreateNetworkContext( network_context_remote_.BindNewPipeAndPassReceiver(), - mojom::NetworkContextParams::New()); + std::move(context_params)); network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New(); @@ -373,8 +383,10 @@ class PreflightControllerTest : public testing::Test { GURL GetURL(const std::string& path) { return test_server_.GetURL(path); } - void PerformPreflightCheck(const ResourceRequest& request, - bool tainted = false) { + void PerformPreflightCheck( + const ResourceRequest& request, + bool tainted = false, + net::IsolationInfo isolation_info = net::IsolationInfo()) { DCHECK(preflight_controller_); run_loop_ = std::make_unique<base::RunLoop>(); preflight_controller_->PerformPreflightCheck( @@ -382,13 +394,20 @@ class PreflightControllerTest : public testing::Test { base::Unretained(this)), request, WithTrustedHeaderClient(false), tainted, TRAFFIC_ANNOTATION_FOR_TESTS, url_loader_factory_remote_.get(), - 0 /* process_id */); + 0 /* process_id */, isolation_info); run_loop_->Run(); } + void SetAccessControlAllowOrigin(const url::Origin origin) { + access_control_allow_origin_ = origin; + } + const url::Origin& test_initiator_origin() const { return test_initiator_origin_; } + const url::Origin& access_control_allow_origin() const { + return access_control_allow_origin_; + } int net_error() const { return net_error_; } base::Optional<CorsErrorStatus> status() { return status_; } base::Optional<CorsErrorStatus> success() { return base::nullopt; } @@ -397,6 +416,8 @@ class PreflightControllerTest : public testing::Test { private: void SetUp() override { + SetAccessControlAllowOrigin(test_initiator_origin_); + preflight_controller_ = std::make_unique<PreflightController>( std::vector<std::string>(), network_service_.get()); @@ -423,7 +444,7 @@ class PreflightControllerTest : public testing::Test { const url::Origin origin = net::test_server::ShouldHandle(request, "/tainted") ? url::Origin() - : test_initiator_origin(); + : access_control_allow_origin(); response->AddCustomHeader(header_names::kAccessControlAllowOrigin, origin.Serialize()); response->AddCustomHeader(header_names::kAccessControlAllowMethods, @@ -438,6 +459,7 @@ class PreflightControllerTest : public testing::Test { base::test::TaskEnvironment task_environment_; const url::Origin test_initiator_origin_; + url::Origin access_control_allow_origin_; std::unique_ptr<base::RunLoop> run_loop_; std::unique_ptr<NetworkService> network_service_; @@ -503,6 +525,69 @@ TEST_F(PreflightControllerTest, CheckValidRequest) { EXPECT_EQ(4u, access_count()); } +TEST_F(PreflightControllerTest, CheckRequestNetworkIsolationKey) { + ResourceRequest request; + request.mode = mojom::RequestMode::kCors; + request.credentials_mode = mojom::CredentialsMode::kOmit; + request.url = GetURL("/allow"); + const url::Origin& origin = test_initiator_origin(); + request.request_initiator = origin; + ResourceRequest::TrustedParams trusted_params; + trusted_params.isolation_info = net::IsolationInfo::Create( + net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin, + net::SiteForCookies()); + request.trusted_params = {trusted_params}; + + PerformPreflightCheck(request); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(1u, access_count()); + + PerformPreflightCheck(request); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(1u, access_count()); // Should be from the preflight cache. + + url::Origin second_origin = url::Origin::Create(GURL("https://example.com/")); + request.request_initiator = second_origin; + SetAccessControlAllowOrigin(second_origin); + request.trusted_params->isolation_info = net::IsolationInfo::Create( + net::IsolationInfo::RedirectMode::kUpdateNothing, origin, second_origin, + net::SiteForCookies()); + PerformPreflightCheck(request); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(2u, access_count()); +} + +TEST_F(PreflightControllerTest, CheckFactoryNetworkIsolationKey) { + ResourceRequest request; + request.mode = mojom::RequestMode::kCors; + request.credentials_mode = mojom::CredentialsMode::kOmit; + request.url = GetURL("/allow"); + const url::Origin& origin = test_initiator_origin(); + request.request_initiator = origin; + + const net::IsolationInfo isolation_info = net::IsolationInfo::Create( + net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin, + net::SiteForCookies()); + + PerformPreflightCheck(request, false, isolation_info); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(1u, access_count()); + + PerformPreflightCheck(request, false, isolation_info); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(1u, access_count()); // Should be from the preflight cache. + + PerformPreflightCheck(request, false, net::IsolationInfo()); + EXPECT_EQ(net::OK, net_error()); + ASSERT_FALSE(status()); + EXPECT_EQ(2u, access_count()); // Should not be from the preflight cache. +} + TEST_F(PreflightControllerTest, CheckTaintedRequest) { ResourceRequest request; request.mode = mojom::RequestMode::kCors; diff --git a/chromium/services/network/crl_set_distributor.cc b/chromium/services/network/crl_set_distributor.cc index 82753f97e24..21914560b65 100644 --- a/chromium/services/network/crl_set_distributor.cc +++ b/chromium/services/network/crl_set_distributor.cc @@ -8,10 +8,12 @@ #include "base/bind.h" #include "base/containers/span.h" +#include "base/feature_list.h" #include "base/location.h" #include "base/strings/string_piece.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" +#include "services/network/public/cpp/features.h" namespace network { @@ -21,8 +23,13 @@ namespace { // CRLSet. scoped_refptr<net::CRLSet> ParseCRLSet(std::string crl_set) { scoped_refptr<net::CRLSet> result; - if (!net::CRLSet::Parse(crl_set, &result)) - return nullptr; + if (base::FeatureList::IsEnabled(network::features::kCertVerifierService)) { + if (!net::CRLSet::ParseAndStoreUnparsedData(std::move(crl_set), &result)) + return nullptr; + } else { + if (!net::CRLSet::Parse(std::move(crl_set), &result)) + return nullptr; + } return result; } diff --git a/chromium/services/network/expect_ct_reporter.cc b/chromium/services/network/expect_ct_reporter.cc index cf7ab32f2c5..31123b7be52 100644 --- a/chromium/services/network/expect_ct_reporter.cc +++ b/chromium/services/network/expect_ct_reporter.cc @@ -20,6 +20,7 @@ #include "base/time/time.h" #include "base/time/time_to_iso8601.h" #include "base/values.h" +#include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/cert/ct_serialization.h" #include "net/http/http_request_headers.h" @@ -158,7 +159,8 @@ void ExpectCTReporter::OnExpectCTFailed( const net::X509Certificate* validated_certificate_chain, const net::X509Certificate* served_certificate_chain, const net::SignedCertificateTimestampAndStatusList& - signed_certificate_timestamps) { + signed_certificate_timestamps, + const net::NetworkIsolationKey& network_isolation_key) { if (report_uri.is_empty()) return; @@ -193,7 +195,7 @@ void ExpectCTReporter::OnExpectCTFailed( UMA_HISTOGRAM_BOOLEAN("SSL.ExpectCTReportSendingAttempt", true); - SendPreflight(report_uri, serialized_report); + SendPreflight(report_uri, serialized_report, network_isolation_key); } void ExpectCTReporter::OnResponseStarted(net::URLRequest* request, @@ -228,6 +230,8 @@ void ExpectCTReporter::OnResponseStarted(net::URLRequest* request, return; } + // TODO(https://crbug.com/993805): Pass in preflight->network_isolation_key, + // once reporting API accepts NetworkIsolationKeys. report_sender_->Send(preflight->report_uri, "application/expect-ct-report+json; charset=utf-8", preflight->serialized_report, success_callback_, @@ -247,21 +251,27 @@ void ExpectCTReporter::OnReadCompleted(net::URLRequest* request, ExpectCTReporter::PreflightInProgress::PreflightInProgress( std::unique_ptr<net::URLRequest> request, const std::string& serialized_report, - const GURL& report_uri) + const GURL& report_uri, + const net::NetworkIsolationKey& network_isolation_key) : request(std::move(request)), serialized_report(serialized_report), - report_uri(report_uri) {} + report_uri(report_uri), + network_isolation_key(network_isolation_key) {} ExpectCTReporter::PreflightInProgress::~PreflightInProgress() {} -void ExpectCTReporter::SendPreflight(const GURL& report_uri, - const std::string& serialized_report) { +void ExpectCTReporter::SendPreflight( + const GURL& report_uri, + const std::string& serialized_report, + const net::NetworkIsolationKey& network_isolation_key) { std::unique_ptr<net::URLRequest> url_request = request_context_->CreateRequest(report_uri, net::DEFAULT_PRIORITY, this, kExpectCTReporterTrafficAnnotation); url_request->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE); url_request->set_allow_credentials(false); url_request->set_method(net::HttpRequestHeaders::kOptionsMethod); + url_request->set_isolation_info(net::IsolationInfo::CreatePartial( + net::IsolationInfo::RedirectMode::kUpdateNothing, network_isolation_key)); net::HttpRequestHeaders extra_headers; extra_headers.SetHeader("Origin", "null"); @@ -272,7 +282,8 @@ void ExpectCTReporter::SendPreflight(const GURL& report_uri, net::URLRequest* raw_request = url_request.get(); inflight_preflights_[raw_request] = std::make_unique<PreflightInProgress>( - std::move(url_request), serialized_report, report_uri); + std::move(url_request), serialized_report, report_uri, + network_isolation_key); raw_request->Start(); } diff --git a/chromium/services/network/expect_ct_reporter.h b/chromium/services/network/expect_ct_reporter.h index 141b3702fd9..559229601f2 100644 --- a/chromium/services/network/expect_ct_reporter.h +++ b/chromium/services/network/expect_ct_reporter.h @@ -10,6 +10,7 @@ #include "base/component_export.h" #include "base/macros.h" +#include "net/base/network_isolation_key.h" #include "net/http/transport_security_state.h" #include "net/url_request/url_request.h" @@ -42,13 +43,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ExpectCTReporter ~ExpectCTReporter() override; // net::TransportSecurityState::ExpectCTReporter: - void OnExpectCTFailed(const net::HostPortPair& host_port_pair, - const GURL& report_uri, - base::Time expiration, - const net::X509Certificate* validated_certificate_chain, - const net::X509Certificate* served_certificate_chain, - const net::SignedCertificateTimestampAndStatusList& - signed_certificate_timestamps) override; + void OnExpectCTFailed( + const net::HostPortPair& host_port_pair, + const GURL& report_uri, + base::Time expiration, + const net::X509Certificate* validated_certificate_chain, + const net::X509Certificate* served_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps, + const net::NetworkIsolationKey& network_isolation_key) override; // net::URLRequest::Delegate: void OnResponseStarted(net::URLRequest* request, int net_error) override; @@ -61,19 +64,24 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ExpectCTReporter struct PreflightInProgress { PreflightInProgress(std::unique_ptr<net::URLRequest> request, const std::string& serialized_report, - const GURL& report_uri); + const GURL& report_uri, + const net::NetworkIsolationKey& network_isolation_key); ~PreflightInProgress(); + // The preflight request. const std::unique_ptr<net::URLRequest> request; // |serialized_report| should be sent to |report_uri| if the preflight // succeeds. const std::string serialized_report; const GURL report_uri; + const net::NetworkIsolationKey network_isolation_key; }; FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, FeatureDisabled); FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, EmptyReportURI); FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, SendReport); + FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, + PreflightUsesNetworkIsolationKey); FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, PreflightContainsWhitespace); FRIEND_TEST_ALL_PREFIXES(ExpectCTReporterTest, BadCorsPreflightResponseOrigin); @@ -87,7 +95,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ExpectCTReporter // preflight result is checked in OnResponseStarted(), and an actual report is // sent with |report_sender_| if the preflight succeeds. void SendPreflight(const GURL& report_uri, - const std::string& serialized_report); + const std::string& serialized_report, + const net::NetworkIsolationKey& network_isolation_key); // When a report fails to send, this method records an UMA histogram and calls // |failure_callback_|. diff --git a/chromium/services/network/expect_ct_reporter_unittest.cc b/chromium/services/network/expect_ct_reporter_unittest.cc index 452e8a9b765..08449291e7c 100644 --- a/chromium/services/network/expect_ct_reporter_unittest.cc +++ b/chromium/services/network/expect_ct_reporter_unittest.cc @@ -12,10 +12,12 @@ #include "base/command_line.h" #include "base/json/json_reader.h" #include "base/run_loop.h" +#include "base/test/bind_test_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/values.h" +#include "net/base/network_isolation_key.h" #include "net/cert/ct_serialization.h" #include "net/cert/signed_certificate_timestamp_and_status.h" #include "net/test/cert_test_util.h" @@ -270,8 +272,15 @@ void CheckExpectCTReport(const std::string& serialized_report, // be run whenever a net::URLRequest is destroyed. class TestExpectCTNetworkDelegate : public net::NetworkDelegateImpl { public: - TestExpectCTNetworkDelegate() - : url_request_destroyed_callback_(base::NullCallback()) {} + TestExpectCTNetworkDelegate() = default; + + using OnBeforeURLRequestCallback = + base::RepeatingCallback<void(net::URLRequest* request)>; + + void set_on_before_url_request_callback( + const OnBeforeURLRequestCallback& on_before_url_request_callback) { + on_before_url_request_callback_ = on_before_url_request_callback; + } void set_url_request_destroyed_callback( const base::RepeatingClosure& callback) { @@ -279,11 +288,20 @@ class TestExpectCTNetworkDelegate : public net::NetworkDelegateImpl { } // net::NetworkDelegateImpl: + int OnBeforeURLRequest(net::URLRequest* request, + net::CompletionOnceCallback callback, + GURL* new_url) override { + if (on_before_url_request_callback_) + on_before_url_request_callback_.Run(request); + return net::OK; + } void OnURLRequestDestroyed(net::URLRequest* request) override { - url_request_destroyed_callback_.Run(); + if (url_request_destroyed_callback_) + url_request_destroyed_callback_.Run(); } private: + OnBeforeURLRequestCallback on_before_url_request_callback_; base::RepeatingClosure url_request_destroyed_callback_; DISALLOW_COPY_AND_ASSIGN(TestExpectCTNetworkDelegate); @@ -298,7 +316,8 @@ class ExpectCTReporterWaitTest : public ::testing::Test { void SetUp() override { // Initializes URLRequestContext after the thread is set up. - context_.reset(new net::TestURLRequestContext(true)); + context_.reset( + new net::TestURLRequestContext(true /* delay_initialization */)); context_->set_network_delegate(&network_delegate_); context_->Init(); net::URLRequestFailedJob::AddUrlHandler(); @@ -321,7 +340,8 @@ class ExpectCTReporterWaitTest : public ::testing::Test { run_loop.QuitClosure()); reporter->OnExpectCTFailed( host_port, report_uri, expiration, ssl_info.cert.get(), - ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps); + ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, + net::NetworkIsolationKey()); run_loop.Run(); } @@ -423,7 +443,8 @@ class ExpectCTReporterTest : public ::testing::Test { reporter->OnExpectCTFailed( host_port, fail_report_uri, base::Time(), ssl_info.cert.get(), - ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps); + ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, + net::NetworkIsolationKey()); bad_cors_run_loop.Run(); // The CORS preflight response may not even have been received yet, so // these expectations are mostly aspirational. @@ -437,7 +458,8 @@ class ExpectCTReporterTest : public ::testing::Test { // be 2. reporter->OnExpectCTFailed( host_port, successful_report_uri, base::Time(), ssl_info.cert.get(), - ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps); + ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, + net::NetworkIsolationKey()); sender->WaitForReport(successful_report_uri); EXPECT_EQ(successful_report_uri, sender->latest_report_uri()); EXPECT_EQ(1, sender->sent_report_count()); @@ -482,7 +504,8 @@ TEST_F(ExpectCTReporterTest, FeatureDisabled) { reporter.OnExpectCTFailed( host_port, report_uri, base::Time(), ssl_info.cert.get(), - ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps); + ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, + net::NetworkIsolationKey()); EXPECT_TRUE(sender->latest_report_uri().is_empty()); EXPECT_TRUE(sender->latest_serialized_report().empty()); @@ -499,7 +522,8 @@ TEST_F(ExpectCTReporterTest, FeatureDisabled) { scoped_feature_list.InitAndEnableFeature(features::kExpectCTReporting); reporter.OnExpectCTFailed( host_port, report_uri, base::Time(), ssl_info.cert.get(), - ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps); + ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, + net::NetworkIsolationKey()); sender->WaitForReport(report_uri); EXPECT_EQ(report_uri, sender->latest_report_uri()); EXPECT_EQ(1, sender->sent_report_count()); @@ -521,7 +545,8 @@ TEST_F(ExpectCTReporterTest, EmptyReportURI) { reporter.OnExpectCTFailed(net::HostPortPair(), GURL(), base::Time(), nullptr, nullptr, - net::SignedCertificateTimestampAndStatusList()); + net::SignedCertificateTimestampAndStatusList(), + net::NetworkIsolationKey()); EXPECT_TRUE(sender->latest_report_uri().is_empty()); EXPECT_TRUE(sender->latest_serialized_report().empty()); @@ -662,10 +687,10 @@ TEST_F(ExpectCTReporterTest, SendReport) { const GURL report_uri = test_server().GetURL(report_path); // Check that the report is sent and contains the correct information. - reporter.OnExpectCTFailed(net::HostPortPair::FromURL(report_uri), report_uri, - expiration, ssl_info.cert.get(), - ssl_info.unverified_cert.get(), - ssl_info.signed_certificate_timestamps); + reporter.OnExpectCTFailed( + net::HostPortPair::FromURL(report_uri), report_uri, expiration, + ssl_info.cert.get(), ssl_info.unverified_cert.get(), + ssl_info.signed_certificate_timestamps, net::NetworkIsolationKey()); // A CORS preflight request should be sent before the actual report. cors_run_loop.Run(); @@ -725,15 +750,72 @@ TEST_F(ExpectCTReporterTest, SendReportSuccessCallback) { const GURL report_uri = test_server().GetURL("/report"); - reporter.OnExpectCTFailed(net::HostPortPair::FromURL(report_uri), report_uri, - expiration, ssl_info.cert.get(), - ssl_info.unverified_cert.get(), - ssl_info.signed_certificate_timestamps); + reporter.OnExpectCTFailed( + net::HostPortPair::FromURL(report_uri), report_uri, expiration, + ssl_info.cert.get(), ssl_info.unverified_cert.get(), + ssl_info.signed_certificate_timestamps, net::NetworkIsolationKey()); // Wait to check that the success callback is run. run_loop.Run(); } +// Test that report preflight requests use the correct NetworkIsolationKey. +TEST_F(ExpectCTReporterTest, PreflightUsesNetworkIsolationKey) { + net::NetworkIsolationKey network_isolation_key = + net::NetworkIsolationKey::CreateTransient(); + + const std::string report_path = "/report"; + std::map<std::string, std::string> cors_headers = kGoodCorsHeaders; + base::RunLoop cors_run_loop; + test_server().RegisterRequestHandler( + base::BindRepeating(&HandleReportPreflightForPath, report_path, + cors_headers, cors_run_loop.QuitClosure())); + ASSERT_TRUE(test_server().Start()); + + TestCertificateReportSender* sender = new TestCertificateReportSender(); + + TestExpectCTNetworkDelegate network_delegate; + net::TestURLRequestContext context(true /* delay_initialization*/); + context.set_network_delegate(&network_delegate); + context.Init(); + + ExpectCTReporter reporter(&context, base::NullCallback(), + base::NullCallback()); + reporter.report_sender_.reset(sender); + EXPECT_TRUE(sender->latest_report_uri().is_empty()); + EXPECT_TRUE(sender->latest_serialized_report().empty()); + + net::SSLInfo ssl_info; + ssl_info.cert = + net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"); + ssl_info.unverified_cert = net::ImportCertFromFile( + net::GetTestCertsDirectory(), "localhost_cert.pem"); + + base::RunLoop before_url_request_run_loop; + network_delegate.set_on_before_url_request_callback( + base::BindLambdaForTesting([&](net::URLRequest* request) { + EXPECT_EQ(network_isolation_key, + request->isolation_info().network_isolation_key()); + before_url_request_run_loop.Quit(); + })); + + const GURL report_uri = test_server().GetURL(report_path); + reporter.OnExpectCTFailed( + net::HostPortPair::FromURL(report_uri), report_uri, base::Time::Now(), + ssl_info.cert.get(), ssl_info.unverified_cert.get(), + ssl_info.signed_certificate_timestamps, network_isolation_key); + + // Make sure the OnBeforeURLRequestCallback is hit. + before_url_request_run_loop.Run(); + + // A CORS preflight request should be sent before the actual report. + cors_run_loop.Run(); + sender->WaitForReport(report_uri); + + EXPECT_EQ(report_uri, sender->latest_report_uri()); + EXPECT_FALSE(sender->latest_serialized_report().empty()); +} + // Test that report preflight responses can contain whitespace. TEST_F(ExpectCTReporterTest, PreflightContainsWhitespace) { const std::string report_path = "/report"; @@ -760,10 +842,10 @@ TEST_F(ExpectCTReporterTest, PreflightContainsWhitespace) { net::GetTestCertsDirectory(), "localhost_cert.pem"); const GURL report_uri = test_server().GetURL(report_path); - reporter.OnExpectCTFailed(net::HostPortPair::FromURL(report_uri), report_uri, - base::Time::Now(), ssl_info.cert.get(), - ssl_info.unverified_cert.get(), - ssl_info.signed_certificate_timestamps); + reporter.OnExpectCTFailed( + net::HostPortPair::FromURL(report_uri), report_uri, base::Time::Now(), + ssl_info.cert.get(), ssl_info.unverified_cert.get(), + ssl_info.signed_certificate_timestamps, net::NetworkIsolationKey()); // A CORS preflight request should be sent before the actual report. cors_run_loop.Run(); diff --git a/chromium/services/network/http_cache_data_counter_unittest.cc b/chromium/services/network/http_cache_data_counter_unittest.cc index e5c6bf5422c..ea7d6c5d1a3 100644 --- a/chromium/services/network/http_cache_data_counter_unittest.cc +++ b/chromium/services/network/http_cache_data_counter_unittest.cc @@ -30,6 +30,7 @@ #include "net/url_request/url_request_context.h" #include "services/network/network_context.h" #include "services/network/network_service.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { @@ -54,6 +55,10 @@ constexpr CacheTestEntry kCacheEntries[] = { mojom::NetworkContextParamsPtr CreateContextParams() { mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); // Use a fixed proxy config, to avoid dependencies on local network // configuration. params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect(); diff --git a/chromium/services/network/http_cache_data_remover_unittest.cc b/chromium/services/network/http_cache_data_remover_unittest.cc index 1952008d076..899b438fe02 100644 --- a/chromium/services/network/http_cache_data_remover_unittest.cc +++ b/chromium/services/network/http_cache_data_remover_unittest.cc @@ -29,6 +29,7 @@ #include "net/url_request/url_request_context_builder.h" #include "services/network/network_context.h" #include "services/network/network_service.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { @@ -61,6 +62,10 @@ constexpr CacheTestEntry kCacheEntries[] = { mojom::NetworkContextParamsPtr CreateContextParams() { mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); // Use a fixed proxy config, to avoid dependencies on local network // configuration. params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect(); diff --git a/chromium/services/network/ignore_errors_cert_verifier.cc b/chromium/services/network/ignore_errors_cert_verifier.cc index e3b9c7a8eb8..2038664a563 100644 --- a/chromium/services/network/ignore_errors_cert_verifier.cc +++ b/chromium/services/network/ignore_errors_cert_verifier.cc @@ -20,6 +20,7 @@ #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" #include "services/network/public/cpp/network_switches.h" +#include "services/network/public/cpp/spki_hash_set.h" using ::net::CertVerifier; using ::net::HashValue; @@ -42,31 +43,13 @@ std::unique_ptr<CertVerifier> IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( switches::kIgnoreCertificateErrorsSPKIList), ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); return std::make_unique<IgnoreErrorsCertVerifier>( - std::move(verifier), IgnoreErrorsCertVerifier::MakeWhitelist(spki_list)); -} - -// static -IgnoreErrorsCertVerifier::SPKIHashSet IgnoreErrorsCertVerifier::MakeWhitelist( - const std::vector<std::string>& fingerprints) { - IgnoreErrorsCertVerifier::SPKIHashSet whitelist; - for (const std::string& fingerprint : fingerprints) { - HashValue hash; - if (!hash.FromString("sha256/" + fingerprint)) { - LOG(ERROR) << "Invalid SPKI: " << fingerprint; - continue; - } - SHA256HashValue sha256; - DCHECK_EQ(hash.size(), sizeof(sha256)); - memcpy(&sha256, hash.data(), sizeof(sha256)); - whitelist.insert(sha256); - } - return whitelist; + std::move(verifier), CreateSPKIHashSet(spki_list)); } IgnoreErrorsCertVerifier::IgnoreErrorsCertVerifier( std::unique_ptr<CertVerifier> verifier, - IgnoreErrorsCertVerifier::SPKIHashSet whitelist) - : verifier_(std::move(verifier)), whitelist_(std::move(whitelist)) {} + SPKIHashSet allowlist) + : verifier_(std::move(verifier)), allowlist_(std::move(allowlist)) {} IgnoreErrorsCertVerifier::~IgnoreErrorsCertVerifier() {} @@ -95,17 +78,17 @@ int IgnoreErrorsCertVerifier::Verify(const RequestParams& params, } } - // Intersect SPKI hashes from the chain with the whitelist. - auto whitelist_begin = whitelist_.begin(); - auto whitelist_end = whitelist_.end(); + // Intersect SPKI hashes from the chain with the allowlist. + auto allowlist_begin = allowlist_.begin(); + auto allowlist_end = allowlist_.end(); auto fingerprints_begin = spki_fingerprints.begin(); auto fingerprints_end = spki_fingerprints.end(); bool ignore_errors = false; - while (whitelist_begin != whitelist_end && + while (allowlist_begin != allowlist_end && fingerprints_begin != fingerprints_end) { - if (*whitelist_begin < *fingerprints_begin) { - ++whitelist_begin; - } else if (*fingerprints_begin < *whitelist_begin) { + if (*allowlist_begin < *fingerprints_begin) { + ++allowlist_begin; + } else if (*fingerprints_begin < *allowlist_begin) { ++fingerprints_begin; } else { ignore_errors = true; @@ -136,8 +119,9 @@ void IgnoreErrorsCertVerifier::SetConfig(const Config& config) { verifier_->SetConfig(config); } -void IgnoreErrorsCertVerifier::set_whitelist(const SPKIHashSet& whitelist) { - whitelist_ = whitelist; +void IgnoreErrorsCertVerifier::SetAllowlistForTesting( + const SPKIHashSet& allowlist) { + allowlist_ = allowlist; } } // namespace network diff --git a/chromium/services/network/ignore_errors_cert_verifier.h b/chromium/services/network/ignore_errors_cert_verifier.h index f122a0e9ce8..a2ab2338d19 100644 --- a/chromium/services/network/ignore_errors_cert_verifier.h +++ b/chromium/services/network/ignore_errors_cert_verifier.h @@ -14,27 +14,21 @@ #include "base/containers/flat_set.h" #include "net/base/completion_once_callback.h" #include "net/cert/cert_verifier.h" - -namespace net { -struct SHA256HashValue; -} // namespace net +#include "services/network/public/cpp/spki_hash_set.h" namespace network { // IgnoreErrorsCertVerifier wraps another CertVerifier in order to ignore -// verification errors from certificate chains that match a whitelist of SPKI +// verification errors from certificate chains that match a allowlist of SPKI // fingerprints. class COMPONENT_EXPORT(NETWORK_SERVICE) IgnoreErrorsCertVerifier : public net::CertVerifier { public: - // SPKIHashSet is a set of SHA-256 SPKI fingerprints (RFC 7469, Section 2.4). - using SPKIHashSet = base::flat_set<net::SHA256HashValue>; - // If the |user_data_dir_switch| is passed in as a valid pointer but // --user-data-dir flag is missing, or --ignore-certificate-errors-spki-list // flag is missing then MaybeWrapCertVerifier returns the supplied verifier. // Otherwise it returns an IgnoreErrorsCertVerifier wrapping the supplied - // verifier using the whitelist from the + // verifier using the allowlist from the // --ignore-certificate-errors-spki-list flag. // // As the --user-data-dir flag is embedder defined, the flag to check for @@ -44,19 +38,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) IgnoreErrorsCertVerifier const char* user_data_dir_switch, std::unique_ptr<net::CertVerifier> verifier); - // MakeWhitelist converts a vector of Base64-encoded SHA-256 SPKI fingerprints - // into an SPKIHashSet. Invalid fingerprints are logged and skipped. - static SPKIHashSet MakeWhitelist( - const std::vector<std::string>& fingerprints); - IgnoreErrorsCertVerifier(std::unique_ptr<net::CertVerifier> verifier, - SPKIHashSet whitelist); + SPKIHashSet allowlist); ~IgnoreErrorsCertVerifier() override; // Verify skips certificate verification and returns OK if any of the // certificates from the chain in |params| match one of the SPKI fingerprints - // from the whitelist. Otherwise, it invokes Verify on the wrapped verifier + // from the allowlist. Otherwise, it invokes Verify on the wrapped verifier // and returns the result. int Verify(const RequestParams& params, net::CertVerifyResult* verify_result, @@ -67,10 +56,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) IgnoreErrorsCertVerifier private: friend class IgnoreErrorsCertVerifierTest; - void set_whitelist(const SPKIHashSet& whitelist); // Testing only. + + void SetAllowlistForTesting(const SPKIHashSet& whitelist); std::unique_ptr<net::CertVerifier> verifier_; - SPKIHashSet whitelist_; + SPKIHashSet allowlist_; DISALLOW_COPY_AND_ASSIGN(IgnoreErrorsCertVerifier); }; diff --git a/chromium/services/network/ignore_errors_cert_verifier_unittest.cc b/chromium/services/network/ignore_errors_cert_verifier_unittest.cc index afd4c3d0128..699018a6069 100644 --- a/chromium/services/network/ignore_errors_cert_verifier_unittest.cc +++ b/chromium/services/network/ignore_errors_cert_verifier_unittest.cc @@ -22,6 +22,7 @@ #include "net/test/gtest_util.h" #include "net/test/test_data_directory.h" #include "services/network/public/cpp/network_switches.h" +#include "services/network/public/cpp/spki_hash_set.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -45,7 +46,7 @@ namespace network { static const char kTestUserDataDirSwitch[] = "test-user-data-dir"; -static std::vector<std::string> MakeWhitelist() { +static std::vector<std::string> MakeAllowlist() { base::FilePath certs_dir = net::GetTestCertsDirectory(); net::CertificateList certs = net::CreateCertificateListFromFile( certs_dir, "x509_verify_results.chain.pem", X509Certificate::FORMAT_AUTO); @@ -68,14 +69,12 @@ class IgnoreErrorsCertVerifierTest : public ::testing::Test { public: IgnoreErrorsCertVerifierTest() : mock_verifier_(new MockCertVerifier()), - verifier_(base::WrapUnique(mock_verifier_), - IgnoreErrorsCertVerifier::SPKIHashSet()) {} + verifier_(base::WrapUnique(mock_verifier_), SPKIHashSet()) {} ~IgnoreErrorsCertVerifierTest() override {} protected: void SetUp() override { - verifier_.set_whitelist( - IgnoreErrorsCertVerifier::MakeWhitelist(MakeWhitelist())); + verifier_.SetAllowlistForTesting(CreateSPKIHashSet(MakeAllowlist())); } // The wrapped CertVerifier. Defaults to returning ERR_CERT_INVALID. Owned by @@ -84,7 +83,7 @@ class IgnoreErrorsCertVerifierTest : public ::testing::Test { IgnoreErrorsCertVerifier verifier_; }; -static void GetNonWhitelistedTestCert(scoped_refptr<X509Certificate>* out) { +static void GetNonAllowlistedTestCert(scoped_refptr<X509Certificate>* out) { base::FilePath certs_dir = net::GetTestCertsDirectory(); scoped_refptr<X509Certificate> test_cert( net::ImportCertFromFile(certs_dir, "ok_cert.pem")); @@ -99,7 +98,7 @@ static CertVerifier::RequestParams MakeRequestParams( /*sct_list=*/std::string()); } -static void GetWhitelistedTestCert(scoped_refptr<X509Certificate>* out) { +static void GetAllowlistedTestCert(scoped_refptr<X509Certificate>* out) { base::FilePath certs_dir = net::GetTestCertsDirectory(); *out = net::CreateCertificateChainFromFile( certs_dir, "x509_verify_results.chain.pem", X509Certificate::FORMAT_AUTO); @@ -111,7 +110,7 @@ TEST_F(IgnoreErrorsCertVerifierTest, TestNoMatchCertOk) { mock_verifier_->set_default_result(OK); scoped_refptr<X509Certificate> test_cert; - ASSERT_NO_FATAL_FAILURE(GetNonWhitelistedTestCert(&test_cert)); + ASSERT_NO_FATAL_FAILURE(GetNonAllowlistedTestCert(&test_cert)); CertVerifyResult verify_result; TestCompletionCallback callback; std::unique_ptr<CertVerifier::Request> request; @@ -124,7 +123,7 @@ TEST_F(IgnoreErrorsCertVerifierTest, TestNoMatchCertOk) { TEST_F(IgnoreErrorsCertVerifierTest, TestNoMatchCertError) { scoped_refptr<X509Certificate> test_cert; - ASSERT_NO_FATAL_FAILURE(GetNonWhitelistedTestCert(&test_cert)); + ASSERT_NO_FATAL_FAILURE(GetNonAllowlistedTestCert(&test_cert)); CertVerifyResult verify_result; TestCompletionCallback callback; std::unique_ptr<CertVerifier::Request> request; @@ -137,7 +136,7 @@ TEST_F(IgnoreErrorsCertVerifierTest, TestNoMatchCertError) { TEST_F(IgnoreErrorsCertVerifierTest, TestMatch) { scoped_refptr<X509Certificate> test_cert; - ASSERT_NO_FATAL_FAILURE(GetWhitelistedTestCert(&test_cert)); + ASSERT_NO_FATAL_FAILURE(GetAllowlistedTestCert(&test_cert)); CertVerifyResult verify_result; TestCompletionCallback callback; std::unique_ptr<CertVerifier::Request> request; @@ -157,7 +156,7 @@ class IgnoreCertificateErrorsSPKIListFlagTest command_line.AppendSwitchASCII(kTestUserDataDirSwitch, "/foo/bar/baz"); } command_line.AppendSwitchASCII(switches::kIgnoreCertificateErrorsSPKIList, - base::JoinString(MakeWhitelist(), ",")); + base::JoinString(MakeAllowlist(), ",")); auto mock_verifier = std::make_unique<MockCertVerifier>(); mock_verifier->set_default_result(ERR_CERT_INVALID); @@ -174,7 +173,7 @@ class IgnoreCertificateErrorsSPKIListFlagTest // are present, certificate verification is bypassed. TEST_P(IgnoreCertificateErrorsSPKIListFlagTest, TestUserDataDirSwitchRequired) { scoped_refptr<X509Certificate> test_cert; - ASSERT_NO_FATAL_FAILURE(GetWhitelistedTestCert(&test_cert)); + ASSERT_NO_FATAL_FAILURE(GetAllowlistedTestCert(&test_cert)); CertVerifyResult verify_result; TestCompletionCallback callback; std::unique_ptr<CertVerifier::Request> request; diff --git a/chromium/services/network/legacy_tls_config_distributor.h b/chromium/services/network/legacy_tls_config_distributor.h index aa8cba2f156..1b05570061c 100644 --- a/chromium/services/network/legacy_tls_config_distributor.h +++ b/chromium/services/network/legacy_tls_config_distributor.h @@ -11,6 +11,7 @@ #include <memory> #include <string> +#include "base/callback_forward.h" #include "base/component_export.h" #include "base/containers/span.h" #include "base/macros.h" diff --git a/chromium/services/network/net_log_exporter.cc b/chromium/services/network/net_log_exporter.cc index e09b9c6cef0..717076e1fe1 100644 --- a/chromium/services/network/net_log_exporter.cc +++ b/chromium/services/network/net_log_exporter.cc @@ -181,7 +181,9 @@ void NetLogExporter::StartWithScratchDir( state_ = STATE_RUNNING; - std::unique_ptr<base::DictionaryValue> constants = net::GetNetConstants(); + std::unique_ptr<base::DictionaryValue> constants = + base::DictionaryValue::From( + base::Value::ToUniquePtrValue(net::GetNetConstants())); if (extra_constants) constants->MergeDictionary(extra_constants); diff --git a/chromium/services/network/network_context.cc b/chromium/services/network/network_context.cc index 2c3f75ea4ad..3bb208bad23 100644 --- a/chromium/services/network/network_context.cc +++ b/chromium/services/network/network_context.cc @@ -39,6 +39,7 @@ #include "components/prefs/pref_service.h" #include "components/prefs/pref_service_factory.h" #include "crypto/sha2.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/base/network_delegate.h" @@ -53,7 +54,6 @@ #include "net/dns/host_cache.h" #include "net/dns/mapped_host_resolver.h" #include "net/extras/sqlite/sqlite_persistent_cookie_store.h" -#include "net/http/failing_http_transaction_factory.h" #include "net/http/http_auth.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_auth_preferences.h" @@ -86,11 +86,14 @@ #include "services/network/proxy_lookup_request.h" #include "services/network/proxy_resolving_socket_factory_mojo.h" #include "services/network/public/cpp/cert_verifier/cert_verifier_creation.h" +#include "services/network/public/cpp/cert_verifier/mojo_cert_verifier.h" #include "services/network/public/cpp/content_security_policy/content_security_policy.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/network_switches.h" #include "services/network/public/cpp/parsed_headers.h" +#include "services/network/public/mojom/network_context.mojom-forward.h" #include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/quic_transport.h" #include "services/network/resolve_host_request.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" @@ -126,7 +129,6 @@ #if defined(OS_CHROMEOS) #include "services/network/cert_verifier_with_trust_anchors.h" -#include "services/network/nss_temp_certs_cache_chromeos.h" #endif // defined(OS_CHROMEOS) #if !defined(OS_IOS) @@ -345,7 +347,15 @@ NetworkContext::NetworkContext( cors_preflight_controller_( params_->cors_extra_safelisted_request_header_names, network_service) { - url_request_context_owner_ = MakeURLRequestContext(); + mojo::PendingRemote<mojom::URLLoaderFactory> + url_loader_factory_for_cert_net_fetcher; + mojo::PendingReceiver<mojom::URLLoaderFactory> + url_loader_factory_for_cert_net_fetcher_receiver = + url_loader_factory_for_cert_net_fetcher + .InitWithNewPipeAndPassReceiver(); + + url_request_context_owner_ = + MakeURLRequestContext(std::move(url_loader_factory_for_cert_net_fetcher)); url_request_context_ = url_request_context_owner_.url_request_context.get(); network_service_->RegisterNetworkContext(this); @@ -383,6 +393,9 @@ NetworkContext::NetworkContext( if (params_->cookie_manager) GetCookieManager(std::move(params_->cookie_manager)); #endif + + CreateURLLoaderFactoryForCertNetFetcher( + std::move(url_loader_factory_for_cert_net_fetcher_receiver)); } NetworkContext::NetworkContext( @@ -463,15 +476,12 @@ NetworkContext::~NetworkContext() { } } +// static void NetworkContext::SetCertVerifierForTesting( net::CertVerifier* cert_verifier) { g_cert_verifier_for_testing = cert_verifier; } -bool NetworkContext::IsPrimaryNetworkContext() const { - return params_ && params_->primary_network_context; -} - void NetworkContext::CreateURLLoaderFactory( mojo::PendingReceiver<mojom::URLLoaderFactory> receiver, mojom::URLLoaderFactoryParamsPtr params, @@ -481,6 +491,18 @@ void NetworkContext::CreateURLLoaderFactory( std::move(receiver), &cors_origin_access_list_)); } +void NetworkContext::CreateURLLoaderFactoryForCertNetFetcher( + mojo::PendingReceiver<mojom::URLLoaderFactory> factory_receiver) { + // TODO(crbug.com/1087790): investigate changing these params. + auto url_loader_factory_params = mojom::URLLoaderFactoryParams::New(); + url_loader_factory_params->is_trusted = true; + url_loader_factory_params->process_id = mojom::kBrowserProcessId; + url_loader_factory_params->automatically_assign_isolation_info = true; + url_loader_factory_params->is_corb_enabled = false; + CreateURLLoaderFactory(std::move(factory_receiver), + std::move(url_loader_factory_params)); +} + void NetworkContext::ActivateDohProbes() { DCHECK(url_request_context_->host_resolver()); @@ -946,19 +968,18 @@ void NetworkContext::SetEnableReferrers(bool enable_referrers) { void NetworkContext::UpdateAdditionalCertificates( mojom::AdditionalCertificatesPtr additional_certificates) { if (!cert_verifier_with_trust_anchors_) { - CHECK(g_cert_verifier_for_testing || - params_->cert_verifier_creation_params->username_hash.empty()); + CHECK(g_cert_verifier_for_testing); return; } if (!additional_certificates) { - nss_temp_certs_cache_.reset(); - cert_verifier_with_trust_anchors_->SetTrustAnchors(net::CertificateList()); + cert_verifier_with_trust_anchors_->SetAdditionalCerts( + net::CertificateList(), net::CertificateList()); return; } - nss_temp_certs_cache_ = std::make_unique<network::NSSTempCertsCacheChromeOS>( + + cert_verifier_with_trust_anchors_->SetAdditionalCerts( + additional_certificates->trust_anchors, additional_certificates->all_certificates); - cert_verifier_with_trust_anchors_->SetTrustAnchors( - additional_certificates->trust_anchors); } #endif // defined(OS_CHROMEOS) @@ -972,11 +993,13 @@ void NetworkContext::SetCTPolicy(mojom::CTPolicyPtr ct_policy) { ct_policy->excluded_spkis, ct_policy->excluded_legacy_spkis); } -void NetworkContext::AddExpectCT(const std::string& domain, - base::Time expiry, - bool enforce, - const GURL& report_uri, - AddExpectCTCallback callback) { +void NetworkContext::AddExpectCT( + const std::string& domain, + base::Time expiry, + bool enforce, + const GURL& report_uri, + const net::NetworkIsolationKey& network_isolation_key, + AddExpectCTCallback callback) { net::TransportSecurityState* transport_security_state = url_request_context()->transport_security_state(); if (!transport_security_state) { @@ -984,7 +1007,8 @@ void NetworkContext::AddExpectCT(const std::string& domain, return; } - transport_security_state->AddExpectCT(domain, expiry, enforce, report_uri); + transport_security_state->AddExpectCT(domain, expiry, enforce, report_uri, + network_isolation_key); std::move(callback).Run(true); } @@ -1010,7 +1034,11 @@ void NetworkContext::SetExpectCTTestReport( net::SignedCertificateTimestampAndStatusList dummy_sct_list; expect_ct_reporter_->OnExpectCTFailed( net::HostPortPair("expect-ct-report.test", 443), report_uri, - base::Time::Now(), dummy_cert.get(), dummy_cert.get(), dummy_sct_list); + base::Time::Now(), dummy_cert.get(), dummy_cert.get(), dummy_sct_list, + // No need for a shared NetworkIsolationKey here, as this is test-only + // code and none + // of the tests that call it care about the NetworkIsolationKey. + net::NetworkIsolationKey::CreateTransient()); } void NetworkContext::LazyCreateExpectCTReporter( @@ -1042,8 +1070,10 @@ void NetworkContext::OnSetExpectCTTestReportFailure() { outstanding_set_expect_ct_callbacks_.pop(); } -void NetworkContext::GetExpectCTState(const std::string& domain, - GetExpectCTStateCallback callback) { +void NetworkContext::GetExpectCTState( + const std::string& domain, + const net::NetworkIsolationKey& network_isolation_key, + GetExpectCTStateCallback callback) { base::DictionaryValue result; if (base::IsStringASCII(domain)) { net::TransportSecurityState* transport_security_state = @@ -1051,7 +1081,7 @@ void NetworkContext::GetExpectCTState(const std::string& domain, if (transport_security_state) { net::TransportSecurityState::ExpectCTState dynamic_expect_ct_state; bool found = transport_security_state->GetDynamicExpectCTState( - domain, &dynamic_expect_ct_state); + domain, network_isolation_key, &dynamic_expect_ct_state); // TODO(estark): query static Expect-CT state as well. if (found) { @@ -1192,10 +1222,12 @@ void NetworkContext::CreateQuicTransport( const GURL& url, const url::Origin& origin, const net::NetworkIsolationKey& key, + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints, mojo::PendingRemote<mojom::QuicTransportHandshakeClient> pending_handshake_client) { - quic_transports_.insert(std::make_unique<QuicTransport>( - url, origin, key, this, std::move(pending_handshake_client))); + quic_transports_.insert( + std::make_unique<QuicTransport>(url, origin, key, fingerprints, this, + std::move(pending_handshake_client))); } void NetworkContext::CreateNetLogExporter( @@ -1384,7 +1416,7 @@ void NetworkContext::GetHSTSState(const std::string& domain, net::TransportSecurityState::STSState dynamic_sts_state; net::TransportSecurityState::PKPState dynamic_pkp_state; bool found_sts_dynamic = transport_security_state->GetDynamicSTSState( - domain, &dynamic_sts_state, nullptr); + domain, &dynamic_sts_state); bool found_pkp_dynamic = transport_security_state->GetDynamicPKPState( domain, &dynamic_pkp_state); @@ -1510,14 +1542,15 @@ void NetworkContext::PreconnectSockets( } void NetworkContext::CreateP2PSocketManager( + const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> client, mojo::PendingReceiver<mojom::P2PTrustedSocketManager> trusted_socket_manager, mojo::PendingReceiver<mojom::P2PSocketManager> socket_manager_receiver) { std::unique_ptr<P2PSocketManager> socket_manager = std::make_unique<P2PSocketManager>( - std::move(client), std::move(trusted_socket_manager), - std::move(socket_manager_receiver), + network_isolation_key, std::move(client), + std::move(trusted_socket_manager), std::move(socket_manager_receiver), base::BindRepeating(&NetworkContext::DestroySocketManager, base::Unretained(this)), url_request_context_); @@ -1674,26 +1707,56 @@ void NetworkContext::OnHttpAuthDynamicParamsChanged( #endif } -URLRequestContextOwner NetworkContext::MakeURLRequestContext() { +URLRequestContextOwner NetworkContext::MakeURLRequestContext( + mojo::PendingRemote<mojom::URLLoaderFactory> + url_loader_factory_for_cert_net_fetcher) { URLRequestContextBuilderMojo builder; const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + DCHECK( + g_cert_verifier_for_testing || + !base::FeatureList::IsEnabled(network::features::kCertVerifierService) || + (params_->cert_verifier_params && + params_->cert_verifier_params->is_remote_params())) + << "If cert verification service is on, the creator of the " + "NetworkContext should pass CertVerifierServiceRemoteParams."; + std::unique_ptr<net::CertVerifier> cert_verifier; if (g_cert_verifier_for_testing) { cert_verifier = std::make_unique<WrappedTestingCertVerifier>(); } else { - mojom::CertVerifierCreationParams* creation_params = nullptr; - if (params_->cert_verifier_creation_params) - creation_params = params_->cert_verifier_creation_params.get(); + if (params_->cert_verifier_params && + params_->cert_verifier_params->is_remote_params()) { + // base::Unretained() is safe below because |this| will own + // |cert_verifier|. + // TODO(https://crbug.com/1085233): this cert verifier should deal with + // disconnections if the CertVerifierService is run outside of the browser + // process. + cert_verifier = std::make_unique<cert_verifier::MojoCertVerifier>( + std::move(params_->cert_verifier_params->get_remote_params() + ->cert_verifier_service), + std::move(url_loader_factory_for_cert_net_fetcher), + base::BindRepeating( + &NetworkContext::CreateURLLoaderFactoryForCertNetFetcher, + base::Unretained(this))); + } else { + mojom::CertVerifierCreationParams* creation_params = nullptr; + if (params_->cert_verifier_params && + params_->cert_verifier_params->is_creation_params()) { + creation_params = + params_->cert_verifier_params->get_creation_params().get(); + } - if (IsUsingCertNetFetcher()) - cert_net_fetcher_ = base::MakeRefCounted<net::CertNetFetcherURLRequest>(); + if (IsUsingCertNetFetcher()) + cert_net_fetcher_ = + base::MakeRefCounted<net::CertNetFetcherURLRequest>(); - cert_verifier = CreateCertVerifier(creation_params, cert_net_fetcher_); + cert_verifier = CreateCertVerifier(creation_params, cert_net_fetcher_); + } - // Wrap the cert verifier in caching and coalescing layers to avoid extra - // verifications. + // Whether the cert verifier is remote or in-process, we should wrap it in + // caching and coalescing layers to avoid extra verifications and IPCs. cert_verifier = std::make_unique<net::CachingCertVerifier>( std::make_unique<net::CoalescingCertVerifier>( std::move(cert_verifier))); @@ -2223,6 +2286,9 @@ void NetworkContext::OnCertVerifyForSignedExchangeComplete(int cert_verify_id, pending_cert_verify->result->cert_status &= ~net::CERT_STATUS_IS_EV; } + // TODO(https://crbug.com/1087091): Update + // NetworkContext::VerifyCertForSignedExchange() to take a + // NetworkIsolationKey, and pass it in here. net::TransportSecurityState::CTRequirementsStatus ct_requirement_status = url_request_context_->transport_security_state()->CheckCTRequirements( net::HostPortPair::FromURL(pending_cert_verify->url), @@ -2230,7 +2296,8 @@ void NetworkContext::OnCertVerifyForSignedExchangeComplete(int cert_verify_id, pending_cert_verify->result->public_key_hashes, verified_cert, pending_cert_verify->certificate.get(), ct_verify_result.scts, net::TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, - ct_verify_result.policy_compliance); + ct_verify_result.policy_compliance, + net::NetworkIsolationKey::Todo()); switch (ct_requirement_status) { case net::TransportSecurityState::CT_REQUIREMENTS_NOT_MET: diff --git a/chromium/services/network/network_context.h b/chromium/services/network/network_context.h index 932ca19462c..eced38ff04f 100644 --- a/chromium/services/network/network_context.h +++ b/chromium/services/network/network_context.h @@ -93,7 +93,6 @@ class NetworkService; class NetworkServiceNetworkDelegate; class NetworkServiceProxyDelegate; class MdnsResponderManager; -class NSSTempCertsCacheChromeOS; class P2PSocketManager; class ProxyLookupRequest; class QuicTransport; @@ -138,13 +137,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext // Sets a global CertVerifier to use when initializing all profiles. static void SetCertVerifierForTesting(net::CertVerifier* cert_verifier); - // Whether the NetworkContext should be used for certain URL fetches of - // global scope (validating certs on some platforms, DNS over HTTPS). - // May only be set to true the first NetworkContext created using the - // NetworkService. Destroying the NetworkContext with this set to true - // will destroy all other NetworkContexts. - bool IsPrimaryNetworkContext() const; - net::URLRequestContext* url_request_context() { return url_request_context_; } NetworkService* network_service() { return network_service_; } @@ -179,6 +171,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext mojom::URLLoaderFactoryParamsPtr params, scoped_refptr<ResourceSchedulerClient> resource_scheduler_client); + // Creates a URLLoaderFactory with params specific to the + // CertVerifierService. A URLLoaderFactory created by this function will be + // used by a CertNetFetcherURLLoader to perform AIA and OCSP fetching. + // These URLLoaderFactories should only ever be used by the + // CertVerifierService, and should never be passed to a renderer. + void CreateURLLoaderFactoryForCertNetFetcher( + mojo::PendingReceiver<mojom::URLLoaderFactory> factory_receiver); + // Enables DoH probes to be sent using this context whenever the DNS // configuration contains DoH servers. void ActivateDohProbes(); @@ -251,10 +251,12 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext base::Time expiry, bool enforce, const GURL& report_uri, + const net::NetworkIsolationKey& network_isolation_key, AddExpectCTCallback callback) override; void SetExpectCTTestReport(const GURL& report_uri, SetExpectCTTestReportCallback callback) override; void GetExpectCTState(const std::string& domain, + const net::NetworkIsolationKey& network_isolation_key, GetExpectCTStateCallback callback) override; #endif // BUILDFLAG(IS_CT_SUPPORTED) void CreateUDPSocket( @@ -305,6 +307,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext const GURL& url, const url::Origin& origin, const net::NetworkIsolationKey& network_isolation_key, + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints, mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client) override; void CreateNetLogExporter( @@ -359,6 +362,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext bool allow_credentials, const net::NetworkIsolationKey& network_isolation_key) override; void CreateP2PSocketManager( + const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> client, mojo::PendingReceiver<mojom::P2PTrustedSocketManager> trusted_socket_manager, @@ -486,9 +490,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext PendingTrustTokenStore* trust_token_store() { return trust_token_store_.get(); } + const PendingTrustTokenStore* trust_token_store() const { + return trust_token_store_.get(); + } private: - URLRequestContextOwner MakeURLRequestContext(); + URLRequestContextOwner MakeURLRequestContext( + mojo::PendingRemote<mojom::URLLoaderFactory> + url_loader_factory_for_cert_net_fetcher); // Invoked when the HTTP cache was cleared. Invokes |callback|. void OnHttpCacheCleared(ClearHttpCacheCallback callback, @@ -642,13 +651,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext #if defined(OS_CHROMEOS) CertVerifierWithTrustAnchors* cert_verifier_with_trust_anchors_ = nullptr; - // Additional certificates made available to NSS cert validation as temporary - // certificates. - std::unique_ptr<network::NSSTempCertsCacheChromeOS> nss_temp_certs_cache_; #endif // CertNetFetcher used by the context's CertVerifier. May be nullptr if - // CertNetFetcher is not used by the current platform. + // CertNetFetcher is not used by the current platform, or if the actual + // net::CertVerifier is instantiated outside of the network service. scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher_; // Created on-demand. Null if unused. diff --git a/chromium/services/network/network_context_unittest.cc b/chromium/services/network/network_context_unittest.cc index e23b961a115..22955e89804 100644 --- a/chromium/services/network/network_context_unittest.cc +++ b/chromium/services/network/network_context_unittest.cc @@ -13,6 +13,7 @@ #include "base/barrier_closure.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/command_line.h" #include "base/containers/span.h" #include "base/files/file.h" @@ -28,6 +29,7 @@ #include "base/stl_util.h" #include "base/strings/strcat.h" #include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/bind_test_util.h" @@ -47,9 +49,9 @@ #include "components/network_session_configurator/common/network_switches.h" #include "components/prefs/testing_pref_service.h" #include "crypto/sha2.h" -#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/system/data_pipe_utils.h" +#include "mojo/public/cpp/system/functions.h" #include "net/base/cache_type.h" #include "net/base/features.h" #include "net/base/hash_value.h" @@ -65,6 +67,7 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/mock_cert_verifier.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_util.h" @@ -116,6 +119,8 @@ #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/proxy_config.mojom.h" +#include "services/network/public/mojom/url_loader.mojom-shared.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_url_loader_client.h" #include "services/network/trust_tokens/pending_trust_token_store.h" #include "services/network/trust_tokens/trust_token_parameterization.h" @@ -376,6 +381,19 @@ class TestProxyLookupClient : public mojom::ProxyLookupClient { DISALLOW_COPY_AND_ASSIGN(TestProxyLookupClient); }; +class MockP2PTrustedSocketManagerClient + : public mojom::P2PTrustedSocketManagerClient { + public: + MockP2PTrustedSocketManagerClient() = default; + ~MockP2PTrustedSocketManagerClient() override = default; + + // mojom::P2PTrustedSocketManagerClient: + void InvalidSocketPortRangeRequested() override {} + void DumpPacket(const std::vector<uint8_t>& packet_header, + uint64_t packet_length, + bool incoming) override {} +}; + class NetworkContextTest : public testing::Test { public: explicit NetworkContextTest( @@ -390,6 +408,11 @@ class NetworkContextTest : public testing::Test { std::unique_ptr<NetworkContext> CreateContextWithParams( mojom::NetworkContextParamsPtr context_params) { + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + DCHECK(!context_params->cert_verifier_params); + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); network_context_remote_.reset(); return std::make_unique<NetworkContext>( network_service_.get(), @@ -1070,11 +1093,11 @@ TEST_F(NetworkContextTest, TransportSecurityStatePersisted) { net::TransportSecurityState::STSState sts_state; net::TransportSecurityState* state = network_context->url_request_context()->transport_security_state(); - EXPECT_FALSE(state->GetDynamicSTSState(kDomain, &sts_state, nullptr)); + EXPECT_FALSE(state->GetDynamicSTSState(kDomain, &sts_state)); state->AddHSTS(kDomain, base::Time::Now() + base::TimeDelta::FromSecondsD(1000), false /* include subdomains */); - EXPECT_TRUE(state->GetDynamicSTSState(kDomain, &sts_state, nullptr)); + EXPECT_TRUE(state->GetDynamicSTSState(kDomain, &sts_state)); ASSERT_EQ(kDomain, sts_state.domain); // Destroy the network context, and wait for all tasks to write state to @@ -1095,7 +1118,7 @@ TEST_F(NetworkContextTest, TransportSecurityStatePersisted) { // Wait for the entry to load. task_environment_.RunUntilIdle(); state = network_context->url_request_context()->transport_security_state(); - ASSERT_EQ(on_disk, state->GetDynamicSTSState(kDomain, &sts_state, nullptr)); + ASSERT_EQ(on_disk, state->GetDynamicSTSState(kDomain, &sts_state)); if (on_disk) EXPECT_EQ(kDomain, sts_state.domain); } @@ -1253,6 +1276,85 @@ TEST_F(NetworkContextTest, HostResolutionFailure) { client.completion_status().resolve_error_info.error); } +// Test the P2PSocketManager::GetHostAddress() works and uses the correct +// NetworkIsolationKey. +TEST_F(NetworkContextTest, P2PHostResolution) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + net::features::kSplitHostCacheByNetworkIsolationKey); + + const char kHostname[] = "foo.test."; + net::IPAddress ip_address; + ASSERT_TRUE(ip_address.AssignFromIPLiteral("1.2.3.4")); + net::NetworkIsolationKey network_isolation_key = + net::NetworkIsolationKey::CreateTransient(); + net::MockCachingHostResolver host_resolver; + std::unique_ptr<net::TestURLRequestContext> url_request_context = + std::make_unique<net::TestURLRequestContext>( + true /* delay_initialization */); + url_request_context->set_host_resolver(&host_resolver); + host_resolver.rules()->AddRule(kHostname, ip_address.ToString()); + url_request_context->Init(); + + network_context_remote_.reset(); + std::unique_ptr<NetworkContext> network_context = + std::make_unique<NetworkContext>( + network_service_.get(), + network_context_remote_.BindNewPipeAndPassReceiver(), + url_request_context.get(), + std::vector<std::string>() /* cors_exempt_header_list */); + + MockP2PTrustedSocketManagerClient client; + mojo::Receiver<network::mojom::P2PTrustedSocketManagerClient> receiver( + &client); + + mojo::Remote<mojom::P2PTrustedSocketManager> trusted_socket_manager; + mojo::Remote<mojom::P2PSocketManager> socket_manager; + network_context_remote_->CreateP2PSocketManager( + network_isolation_key, receiver.BindNewPipeAndPassRemote(), + trusted_socket_manager.BindNewPipeAndPassReceiver(), + socket_manager.BindNewPipeAndPassReceiver()); + + base::RunLoop run_loop; + std::vector<net::IPAddress> results; + socket_manager->GetHostAddress( + kHostname, false /* enable_mdns */, + base::BindLambdaForTesting( + [&](const std::vector<net::IPAddress>& addresses) { + EXPECT_EQ(std::vector<net::IPAddress>{ip_address}, addresses); + run_loop.Quit(); + })); + run_loop.Run(); + + // Check that the URL in kHostname is in the HostCache, with + // |network_isolation_key|. + const net::HostPortPair kHostPortPair = net::HostPortPair(kHostname, 0); + net::HostResolver::ResolveHostParameters params; + params.source = net::HostResolverSource::LOCAL_ONLY; + std::unique_ptr<net::HostResolver::ResolveHostRequest> request1 = + host_resolver.CreateRequest(kHostPortPair, network_isolation_key, + net::NetLogWithSource(), params); + net::TestCompletionCallback callback1; + int result = request1->Start(callback1.callback()); + EXPECT_EQ(net::OK, callback1.GetResult(result)); + + // Check that the hostname is not in the DNS cache for other possible NIKs. + const url::Origin kDestinationOrigin = + url::Origin::Create(GURL(base::StringPrintf("https://%s", kHostname))); + const net::NetworkIsolationKey kOtherNiks[] = { + net::NetworkIsolationKey(), + net::NetworkIsolationKey(kDestinationOrigin /* top_frame_origin */, + kDestinationOrigin /* frame_origin */)}; + for (const auto& other_nik : kOtherNiks) { + std::unique_ptr<net::HostResolver::ResolveHostRequest> request2 = + host_resolver.CreateRequest(kHostPortPair, other_nik, + net::NetLogWithSource(), params); + net::TestCompletionCallback callback2; + int result = request2->Start(callback2.callback()); + EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, callback2.GetResult(result)); + } +} + // Test that valid referrers are allowed, while invalid ones result in errors. TEST_F(NetworkContextTest, Referrers) { const GURL kReferrer = GURL("http://referrer/"); @@ -2162,16 +2264,17 @@ TEST_F(NetworkContextTest, ClearEmptyNetworkErrorLoggingWithNoService) { void SetCookieCallback(base::RunLoop* run_loop, bool* result_out, - net::CanonicalCookie::CookieInclusionStatus result) { + net::CookieInclusionStatus result) { *result_out = result.IsInclude(); run_loop->Quit(); } -void GetCookieListCallback(base::RunLoop* run_loop, - net::CookieList* result_out, - const net::CookieStatusList& result, - const net::CookieStatusList& excluded_cookies) { - *result_out = net::cookie_util::StripStatuses(result); +void GetCookieListCallback( + base::RunLoop* run_loop, + net::CookieList* result_out, + const net::CookieAccessResultList& result, + const net::CookieAccessResultList& excluded_cookies) { + *result_out = net::cookie_util::StripAccessResults(result); run_loop->Quit(); } @@ -3539,7 +3642,6 @@ TEST_F(NetworkContextTest, CreateHostResolverWithConfigOverrides) { TEST_F(NetworkContextTest, ActivateDohProbes) { auto resolver = std::make_unique<net::MockHostResolver>(); mojom::NetworkContextParamsPtr params = CreateContextParams(); - params->primary_network_context = true; std::unique_ptr<NetworkContext> network_context = CreateContextWithParams(std::move(params)); network_context->url_request_context()->set_host_resolver(resolver.get()); @@ -3678,7 +3780,6 @@ TEST_F(NetworkContextTest, CanSetCookieFalseIfCookiesBlocked) { base::Time(), base::Time(), base::Time(), false, false, net::CookieSameSite::LAX_MODE, net::COOKIE_PRIORITY_LOW); - EXPECT_TRUE( network_context->url_request_context()->network_delegate()->CanSetCookie( *request, cookie, nullptr, true)); @@ -3716,11 +3817,11 @@ TEST_F(NetworkContextTest, CanGetCookiesFalseIfCookiesBlocked) { EXPECT_TRUE( network_context->url_request_context()->network_delegate()->CanGetCookies( - *request, {}, true)); + *request, true)); SetDefaultContentSetting(CONTENT_SETTING_BLOCK, network_context.get()); EXPECT_FALSE( network_context->url_request_context()->network_delegate()->CanGetCookies( - *request, {}, true)); + *request, true)); } TEST_F(NetworkContextTest, CanGetCookiesTrueIfCookiesAllowed) { @@ -3734,7 +3835,7 @@ TEST_F(NetworkContextTest, CanGetCookiesTrueIfCookiesAllowed) { SetDefaultContentSetting(CONTENT_SETTING_ALLOW, network_context.get()); EXPECT_TRUE( network_context->url_request_context()->network_delegate()->CanGetCookies( - *request, {}, true)); + *request, true)); } // Gets notified by the EmbeddedTestServer on incoming connections being @@ -4298,6 +4399,10 @@ TEST_F(NetworkContextTest, FactoryParams_DisableSecureDns) { #if BUILDFLAG(IS_CT_SUPPORTED) TEST_F(NetworkContextTest, ExpectCT) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + net::features::kPartitionExpectCTStateByNetworkIsolationKey); + std::unique_ptr<NetworkContext> network_context = CreateContextWithParams(CreateContextParams()); @@ -4307,12 +4412,15 @@ TEST_F(NetworkContextTest, ExpectCT) { const bool enforce = true; const GURL report_uri = GURL("https://example.com/foo/bar"); + net::NetworkIsolationKey network_isolation_key = + net::NetworkIsolationKey::CreateTransient(); + // Assert we start with no data for the test host. { base::Value state; base::RunLoop run_loop; network_context->GetExpectCTState( - kTestDomain, + kTestDomain, network_isolation_key, base::BindOnce(&StoreValue, &state, run_loop.QuitClosure())); run_loop.Run(); EXPECT_TRUE(state.is_dict()); @@ -4328,7 +4436,7 @@ TEST_F(NetworkContextTest, ExpectCT) { base::RunLoop run_loop; bool result = false; network_context->AddExpectCT( - kTestDomain, expiry, enforce, report_uri, + kTestDomain, expiry, enforce, report_uri, network_isolation_key, base::BindOnce(&StoreBool, &result, run_loop.QuitClosure())); run_loop.Run(); EXPECT_TRUE(result); @@ -4339,7 +4447,7 @@ TEST_F(NetworkContextTest, ExpectCT) { base::Value state; base::RunLoop run_loop; network_context->GetExpectCTState( - kTestDomain, + kTestDomain, network_isolation_key, base::BindOnce(&StoreValue, &state, run_loop.QuitClosure())); run_loop.Run(); EXPECT_TRUE(state.is_dict()); @@ -4365,6 +4473,22 @@ TEST_F(NetworkContextTest, ExpectCT) { EXPECT_EQ(report_uri, value->GetString()); } + // Using a different NetworkIsolationKey should return no result. + { + base::Value state; + base::RunLoop run_loop; + network_context->GetExpectCTState( + kTestDomain, net::NetworkIsolationKey::CreateTransient(), + base::BindOnce(&StoreValue, &state, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_TRUE(state.is_dict()); + + const base::Value* result = + state.FindKeyOfType("result", base::Value::Type::BOOLEAN); + ASSERT_TRUE(result != nullptr); + EXPECT_FALSE(result->GetBool()); + } + // Delete host data. { bool result; @@ -4381,7 +4505,7 @@ TEST_F(NetworkContextTest, ExpectCT) { base::Value state; base::RunLoop run_loop; network_context->GetExpectCTState( - kTestDomain, + kTestDomain, network_isolation_key, base::BindOnce(&StoreValue, &state, run_loop.QuitClosure())); run_loop.Run(); EXPECT_TRUE(state.is_dict()); @@ -6144,87 +6268,6 @@ TEST_F(NetworkContextTest, FactoriesDeletedWhenBindingsCleared) { EXPECT_EQ(network_context->num_url_loader_factories_for_testing(), 0u); } -#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) -TEST_F(NetworkContextTest, UseCertVerifierBuiltin) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - net::test_server::RegisterDefaultHandlers(&test_server); - ASSERT_TRUE(test_server.Start()); - - // This just happens to be the only histogram that directly records which - // verifier was used. - const char kBuiltinVerifierHistogram[] = - "Net.CertVerifier.NameNormalizationPrivateRoots.Builtin"; - - for (bool builtin_verifier_enabled : {false, true}) { - SCOPED_TRACE(builtin_verifier_enabled); - - mojom::NetworkContextParamsPtr params = CreateContextParams(); - auto creation_params = mojom::CertVerifierCreationParams::New(); - creation_params->use_builtin_cert_verifier = - builtin_verifier_enabled - ? mojom::CertVerifierCreationParams::CertVerifierImpl::kBuiltin - : mojom::CertVerifierCreationParams::CertVerifierImpl::kSystem; - params->cert_verifier_creation_params = std::move(creation_params); - std::unique_ptr<NetworkContext> network_context = - CreateContextWithParams(std::move(params)); - - ResourceRequest request; - request.url = test_server.GetURL("/nocontent"); - base::HistogramTester histogram_tester; - std::unique_ptr<TestURLLoaderClient> client = - FetchRequest(request, network_context.get()); - EXPECT_EQ(net::OK, client->completion_status().error_code); - histogram_tester.ExpectTotalCount(kBuiltinVerifierHistogram, - builtin_verifier_enabled ? 1 : 0); - } -} - -class NetworkContextCertVerifierBuiltinFeatureFlagTest - : public NetworkContextTest, - public testing::WithParamInterface<bool> { - public: - NetworkContextCertVerifierBuiltinFeatureFlagTest() { - scoped_feature_list_.InitWithFeatureState( - net::features::kCertVerifierBuiltinFeature, - /*enabled=*/GetParam()); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_P(NetworkContextCertVerifierBuiltinFeatureFlagTest, - DefaultNetworkContextParamsUsesCorrectVerifier) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - net::test_server::RegisterDefaultHandlers(&test_server); - ASSERT_TRUE(test_server.Start()); - - // This just happens to be the only histogram that directly records which - // verifier was used. - const char kBuiltinVerifierHistogram[] = - "Net.CertVerifier.NameNormalizationPrivateRoots.Builtin"; - - // Test creating a NetworkContextParams without specifying a value for - // use_builtin_cert_verifier. Should use whatever the default cert verifier - // implementation is according to the feature flag. - std::unique_ptr<NetworkContext> network_context = - CreateContextWithParams(CreateContextParams()); - - ResourceRequest request; - request.url = test_server.GetURL("/nocontent"); - base::HistogramTester histogram_tester; - std::unique_ptr<TestURLLoaderClient> client = - FetchRequest(request, network_context.get()); - EXPECT_EQ(net::OK, client->completion_status().error_code); - histogram_tester.ExpectTotalCount(kBuiltinVerifierHistogram, - GetParam() ? 1 : 0); -} - -INSTANTIATE_TEST_SUITE_P(All, - NetworkContextCertVerifierBuiltinFeatureFlagTest, - ::testing::Bool()); -#endif // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) - static ResourceRequest CreateResourceRequest(const char* method, const GURL& url) { ResourceRequest request; @@ -6535,15 +6578,14 @@ TEST_F(NetworkContextTest, DisableTrustTokens) { class NetworkContextExpectBadMessageTest : public NetworkContextTest { public: NetworkContextExpectBadMessageTest() { - mojo::core::SetDefaultProcessErrorCallback( + mojo::SetDefaultProcessErrorHandler( base::BindLambdaForTesting([&](const std::string&) { EXPECT_FALSE(got_bad_message_); got_bad_message_ = true; })); } ~NetworkContextExpectBadMessageTest() override { - mojo::core::SetDefaultProcessErrorCallback( - mojo::core::ProcessErrorCallback()); + mojo::SetDefaultProcessErrorHandler(base::NullCallback()); } protected: @@ -6603,30 +6645,86 @@ TEST_F(NetworkContextExpectBadMessageTest, AssertBadMessage(); } -TEST_F(NetworkContextTest, - AttemptsTrustTokenBearingRequestWhenTrustTokensIsEnabled) { +TEST_F(NetworkContextExpectBadMessageTest, + FailsTrustTokenRedemptionWhenForbidden) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kTrustTokens); std::unique_ptr<NetworkContext> network_context = CreateContextWithParams(mojom::NetworkContextParams::New()); - // Allow the store time to initialize asynchronously. + // Allow |network_context|'s Trust Tokens store time to initialize + // asynchronously, if necessary. task_environment_.RunUntilIdle(); + ASSERT_TRUE(network_context->trust_token_store()); + ResourceRequest my_request; my_request.trust_token_params = OptionalTrustTokenParams(mojom::TrustTokenParams::New()); + my_request.trust_token_params->type = + mojom::TrustTokenOperationType::kRedemption; auto factory_params = mojom::URLLoaderFactoryParams::New(); - factory_params->top_frame_origin = - url::Origin::Create(GURL("https://topframe.com/")); + factory_params->trust_token_redemption_policy = + mojom::TrustTokenRedemptionPolicy::kForbid; + std::unique_ptr<TestURLLoaderClient> client = + FetchRequest(my_request, network_context.get(), mojom::kURLLoadOptionNone, + mojom::kBrowserProcessId, std::move(factory_params)); - // Since the request doesn't have a destination URL suitable for use as a - // Trust Tokens issuer, it should fail. + AssertBadMessage(); +} + +TEST_F(NetworkContextExpectBadMessageTest, + FailsTrustTokenSigningWhenForbidden) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kTrustTokens); + + std::unique_ptr<NetworkContext> network_context = + CreateContextWithParams(mojom::NetworkContextParams::New()); + + // Allow |network_context|'s Trust Tokens store time to initialize + // asynchronously, if necessary. + task_environment_.RunUntilIdle(); + + ASSERT_TRUE(network_context->trust_token_store()); + + ResourceRequest my_request; + my_request.trust_token_params = + OptionalTrustTokenParams(mojom::TrustTokenParams::New()); + my_request.trust_token_params->type = + mojom::TrustTokenOperationType::kSigning; + + auto factory_params = mojom::URLLoaderFactoryParams::New(); + factory_params->trust_token_redemption_policy = + mojom::TrustTokenRedemptionPolicy::kForbid; std::unique_ptr<TestURLLoaderClient> client = FetchRequest(my_request, network_context.get(), mojom::kURLLoadOptionNone, mojom::kBrowserProcessId, std::move(factory_params)); + + AssertBadMessage(); +} + +TEST_F(NetworkContextTest, + AttemptsTrustTokenBearingRequestWhenTrustTokensIsEnabled) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kTrustTokens); + + std::unique_ptr<NetworkContext> network_context = + CreateContextWithParams(mojom::NetworkContextParams::New()); + + // Allow the store time to initialize asynchronously. + task_environment_.RunUntilIdle(); + + ResourceRequest my_request; + my_request.trust_token_params = + OptionalTrustTokenParams(mojom::TrustTokenParams::New()); + + // Since the request doesn't have a destination URL suitable for use as a + // Trust Tokens issuer, it should fail. + std::unique_ptr<TestURLLoaderClient> client = FetchRequest( + my_request, network_context.get(), mojom::kURLLoadOptionNone, + mojom::kBrowserProcessId, mojom::URLLoaderFactoryParams::New()); EXPECT_EQ(client->completion_status().error_code, net::ERR_TRUST_TOKEN_OPERATION_FAILED); EXPECT_EQ(client->completion_status().trust_token_operation_status, @@ -6654,13 +6752,9 @@ TEST_F(NetworkContextTest, my_request.trust_token_params = OptionalTrustTokenParams(mojom::TrustTokenParams::New()); - auto factory_params = mojom::URLLoaderFactoryParams::New(); - factory_params->top_frame_origin = - url::Origin::Create(GURL("https://topframe.com/")); - - std::unique_ptr<TestURLLoaderClient> client = - FetchRequest(my_request, network_context.get(), mojom::kURLLoadOptionNone, - mojom::kBrowserProcessId, std::move(factory_params)); + std::unique_ptr<TestURLLoaderClient> client = FetchRequest( + my_request, network_context.get(), mojom::kURLLoadOptionNone, + mojom::kBrowserProcessId, mojom::URLLoaderFactoryParams::New()); EXPECT_EQ(client->completion_status().error_code, net::ERR_TRUST_TOKEN_OPERATION_FAILED); EXPECT_EQ(client->completion_status().trust_token_operation_status, diff --git a/chromium/services/network/network_service.cc b/chromium/services/network/network_service.cc index b289d86c03b..75b0c7e2e97 100644 --- a/chromium/services/network/network_service.cc +++ b/chromium/services/network/network_service.cc @@ -26,8 +26,8 @@ #include "build/chromecast_buildflags.h" #include "components/network_session_configurator/common/network_features.h" #include "components/os_crypt/os_crypt.h" -#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h" +#include "mojo/public/cpp/system/functions.h" #include "net/base/logging_network_change_observer.h" #include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier_posix.h" @@ -56,6 +56,7 @@ #include "services/network/net_log_proxy_sink.h" #include "services/network/network_context.h" #include "services/network/network_usage_accumulator.h" +#include "services/network/public/cpp/crash_keys.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/cpp/load_info_util.h" @@ -184,10 +185,38 @@ void HandleBadMessage(const std::string& error) { LOG(WARNING) << "Mojo error in NetworkService:" << error; mojo::debug::ScopedMessageErrorCrashKey crash_key_value(error); base::debug::DumpWithoutCrashing(); + network::debug::ClearDeserializationCrashKeyString(); } } // namespace +DataPipeUseTracker::DataPipeUseTracker(NetworkService* network_service, + DataPipeUser user) + : network_service_(network_service), user_(user) {} + +DataPipeUseTracker::DataPipeUseTracker(DataPipeUseTracker&& that) + : DataPipeUseTracker(that.network_service_, that.user_) { + this->state_ = that.state_; + that.state_ = State::kReset; +} + +DataPipeUseTracker::~DataPipeUseTracker() { + Reset(); +} + +void DataPipeUseTracker::Activate() { + DCHECK_EQ(state_, State::kInit); + network_service_->OnDataPipeCreated(user_); + state_ = State::kActivated; +} + +void DataPipeUseTracker::Reset() { + if (state_ == State::kActivated) { + network_service_->OnDataPipeDropped(user_); + } + state_ = State::kReset; +} + // static const base::TimeDelta NetworkService::kInitialDohProbeTimeout = base::TimeDelta::FromSeconds(5); @@ -249,16 +278,17 @@ NetworkService::NetworkService( // |registry_| is nullptr when an in-process NetworkService is // created directly, like in most unit tests. - if (registry_) { - mojo::core::SetDefaultProcessErrorCallback( - base::BindRepeating(&HandleBadMessage)); - } + if (registry_) + mojo::SetDefaultProcessErrorHandler(base::BindRepeating(&HandleBadMessage)); if (receiver.is_valid()) Bind(std::move(receiver)); if (!delay_initialization_until_set_client) Initialize(mojom::NetworkServiceParams::New()); + + metrics_trigger_timer_.Start(FROM_HERE, base::TimeDelta::FromMinutes(20), + this, &NetworkService::ReportMetrics); } void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params, @@ -375,11 +405,6 @@ std::unique_ptr<NetworkService> NetworkService::CreateForTesting() { } void NetworkService::RegisterNetworkContext(NetworkContext* network_context) { - // If IsPrimaryNetworkContext() is true, there must be no other - // NetworkContexts created yet. - DCHECK(!network_context->IsPrimaryNetworkContext() || - network_contexts_.empty()); - DCHECK_EQ(0u, network_contexts_.count(network_context)); network_contexts_.insert(network_context); if (quic_disabled_) @@ -399,11 +424,6 @@ void NetworkService::RegisterNetworkContext(NetworkContext* network_context) { } void NetworkService::DeregisterNetworkContext(NetworkContext* network_context) { - // If the NetworkContext is the primary network context, all other - // NetworkContexts must already have been destroyed. - DCHECK(!network_context->IsPrimaryNetworkContext() || - network_contexts_.size() == 1); - DCHECK_EQ(1u, network_contexts_.count(network_context)); network_contexts_.erase(network_context); } @@ -442,7 +462,9 @@ void NetworkService::StartNetLog(base::File file, net::NetLogCaptureMode capture_mode, base::Value client_constants) { DCHECK(client_constants.is_dict()); - std::unique_ptr<base::DictionaryValue> constants = net::GetNetConstants(); + std::unique_ptr<base::DictionaryValue> constants = + base::DictionaryValue::From( + base::Value::ToUniquePtrValue(net::GetNetConstants())); constants->MergeDictionary(&client_constants); file_net_log_observer_ = net::FileNetLogObserver::CreateUnboundedPreExisting( @@ -467,10 +489,6 @@ void NetworkService::SetSSLKeyLogFile(base::File file) { void NetworkService::CreateNetworkContext( mojo::PendingReceiver<mojom::NetworkContext> receiver, mojom::NetworkContextParamsPtr params) { - // Only the first created NetworkContext can have |primary_next_context| set - // to true. - DCHECK(!params->primary_network_context || network_contexts_.empty()); - owned_network_contexts_.emplace(std::make_unique<NetworkContext>( this, std::move(receiver), std::move(params), base::BindOnce(&NetworkService::OnNetworkContextConnectionClosed, @@ -504,6 +522,14 @@ void NetworkService::ConfigureStubHostResolver( overrides.disabled_upgrade_providers = SplitString(features::kDnsOverHttpsUpgradeDisabledProvidersParam.Get(), ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + + // Because SECURE mode does not allow any fallback, allow multiple retries as + // a quick hack to increase the timeout for these requests. + // TODO(crbug.com/1105138): Rethink the timeout logic to be less aggressive in + // cases where there is no fallback, without needing to make so many retries. + if (secure_dns_mode == net::DnsConfig::SecureDnsMode::SECURE) + overrides.doh_attempts = 3; + host_resolver_manager_->SetDnsConfigOverrides(overrides); } @@ -768,28 +794,29 @@ void NetworkService::OnBeforeURLRequest() { MaybeStartUpdateLoadInfoTimer(); } -void NetworkService::DestroyNetworkContexts() { - // Delete NetworkContexts. If there's a primary NetworkContext, it must be - // deleted after all other NetworkContexts, to avoid use-after-frees. - for (auto it = owned_network_contexts_.begin(); - it != owned_network_contexts_.end();) { - const auto last = it; - ++it; - if (!(*last)->IsPrimaryNetworkContext()) - owned_network_contexts_.erase(last); - } +void NetworkService::OnDataPipeCreated(DataPipeUser user) { + auto& entry = data_pipe_use_[user]; + ++entry.current; + entry.max = std::max(entry.max, entry.current); +} + +void NetworkService::OnDataPipeDropped(DataPipeUser user) { + auto& entry = data_pipe_use_[user]; + DCHECK_GT(entry.current, 0); + --entry.current; + entry.min = std::min(entry.min, entry.current); +} + +void NetworkService::StopMetricsTimerForTesting() { + metrics_trigger_timer_.Stop(); +} - DCHECK_LE(owned_network_contexts_.size(), 1u); +void NetworkService::DestroyNetworkContexts() { owned_network_contexts_.clear(); } void NetworkService::OnNetworkContextConnectionClosed( NetworkContext* network_context) { - if (network_context->IsPrimaryNetworkContext()) { - DestroyNetworkContexts(); - return; - } - auto it = owned_network_contexts_.find(network_context); DCHECK(it != owned_network_contexts_.end()); owned_network_contexts_.erase(it); @@ -881,6 +908,23 @@ void NetworkService::AckUpdateLoadInfo() { MaybeStartUpdateLoadInfoTimer(); } +void NetworkService::ReportMetrics() { + UMA_HISTOGRAM_COUNTS_10000("Net.DataPipeUseForUrlLoader.Min", + data_pipe_use_[DataPipeUser::kUrlLoader].min); + UMA_HISTOGRAM_COUNTS_10000("Net.DataPipeUseForUrlLoader.Max", + data_pipe_use_[DataPipeUser::kUrlLoader].max); + UMA_HISTOGRAM_COUNTS_10000("Net.DataPipeUseForWebSocket.Min", + data_pipe_use_[DataPipeUser::kWebSocket].min); + UMA_HISTOGRAM_COUNTS_10000("Net.DataPipeUseForWebSocket.Max", + data_pipe_use_[DataPipeUser::kWebSocket].max); + + for (auto& pair : data_pipe_use_) { + DataPipeUsage& entry = pair.second; + entry.max = entry.current; + entry.min = entry.current; + } +} + void NetworkService::Bind( mojo::PendingReceiver<mojom::NetworkService> receiver) { DCHECK(!receiver_.is_bound()); diff --git a/chromium/services/network/network_service.h b/chromium/services/network/network_service.h index c98aa7f5d93..50120d006a0 100644 --- a/chromium/services/network/network_service.h +++ b/chromium/services/network/network_service.h @@ -13,6 +13,7 @@ #include <vector> #include "base/component_export.h" +#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/containers/span.h" #include "base/containers/unique_ptr_adapters.h" @@ -59,8 +60,43 @@ class HttpAuthCacheCopier; class LegacyTLSConfigDistributor; class NetLogProxySink; class NetworkContext; +class NetworkService; class NetworkUsageAccumulator; +// DataPipeUseTracker tracks the mojo data pipe usage in the network +// service. +class COMPONENT_EXPORT(NETWORK_SERVICE) DataPipeUseTracker final { + public: + enum DataPipeUser { + kUrlLoader = 0, + kWebSocket = 1, + }; + // |network_service| must outlive |this|. + DataPipeUseTracker(NetworkService* network_service, DataPipeUser user); + DataPipeUseTracker(DataPipeUseTracker&&); + ~DataPipeUseTracker(); + DataPipeUseTracker(const DataPipeUseTracker&) = delete; + DataPipeUseTracker& operator=(const DataPipeUseTracker&) = delete; + + // Call this when the associated data pipe is created. + void Activate(); + // Call this when (one end of) the associated data pipe is dropped. + void Reset(); + + private: + enum State { + kInit, + kActivated, + kReset, + }; + NetworkService* const network_service_; + const DataPipeUser user_; + + State state_ = State::kInit; +}; + +using DataPipeUser = DataPipeUseTracker::DataPipeUser; + class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService : public mojom::NetworkService { public: @@ -247,6 +283,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService return trust_token_key_commitments_.get(); } + void OnDataPipeCreated(DataPipeUser user); + void OnDataPipeDropped(DataPipeUser user); + void StopMetricsTimerForTesting(); + static NetworkService* GetNetworkServiceForTesting(); private: @@ -271,6 +311,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService // Starts timer call UpdateLoadInfo() again, if needed. void AckUpdateLoadInfo(); + void ReportMetrics(); + bool initialized_ = false; net::NetLog* net_log_; @@ -351,7 +393,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService // acknowledged. bool waiting_on_load_state_ack_ = false; - // A timer that periodically calls ReportMetrics every hour. + // A timer that periodically calls ReportMetrics every 20 minutes. base::RepeatingTimer metrics_trigger_timer_; // Whether new NetworkContexts will be configured to partition their @@ -370,6 +412,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService // requests should have their initiator origin within the set stored here). std::map<int, std::set<url::Origin>> plugin_origins_; + struct DataPipeUsage final { + int current = 0; + int max = 0; + int min = 0; + }; + base::flat_map<DataPipeUser, DataPipeUsage> data_pipe_use_; + DISALLOW_COPY_AND_ASSIGN(NetworkService); }; diff --git a/chromium/services/network/network_service_network_delegate.cc b/chromium/services/network/network_service_network_delegate.cc index c0e1a2e9bf3..abf4451b269 100644 --- a/chromium/services/network/network_service_network_delegate.cc +++ b/chromium/services/network/network_service_network_delegate.cc @@ -11,6 +11,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "components/domain_reliability/monitor.h" +#include "net/base/features.h" #include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -20,7 +21,6 @@ #include "services/network/network_service.h" #include "services/network/network_service_proxy_delegate.h" #include "services/network/pending_callback_chain.h" -#include "services/network/public/cpp/features.h" #include "services/network/url_loader.h" #include "url/gurl.h" @@ -61,7 +61,7 @@ void NetworkServiceNetworkDelegate::MaybeTruncateReferrer( } if (base::FeatureList::IsEnabled( - features::kCapReferrerToOriginOnCrossOrigin)) { + net::features::kCapReferrerToOriginOnCrossOrigin)) { url::Origin destination_origin = url::Origin::Create(effective_url); url::Origin source_origin = url::Origin::Create(GURL(request->referrer())); if (!destination_origin.IsSameOriginWith(source_origin)) @@ -186,7 +186,6 @@ void NetworkServiceNetworkDelegate::OnPACScriptError( bool NetworkServiceNetworkDelegate::OnCanGetCookies( const net::URLRequest& request, - const net::CookieList& cookie_list, bool allowed_from_caller) { bool allowed = allowed_from_caller && diff --git a/chromium/services/network/network_service_network_delegate.h b/chromium/services/network/network_service_network_delegate.h index 31dd7812921..0926327d2a0 100644 --- a/chromium/services/network/network_service_network_delegate.h +++ b/chromium/services/network/network_service_network_delegate.h @@ -56,7 +56,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkServiceNetworkDelegate int net_error) override; void OnPACScriptError(int line_number, const base::string16& error) override; bool OnCanGetCookies(const net::URLRequest& request, - const net::CookieList& cookie_list, bool allowed_from_caller) override; bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, diff --git a/chromium/services/network/network_service_unittest.cc b/chromium/services/network/network_service_unittest.cc index 8c3719a41ff..bb38b299fcb 100644 --- a/chromium/services/network/network_service_unittest.cc +++ b/chromium/services/network/network_service_unittest.cc @@ -56,6 +56,7 @@ #include "services/network/public/mojom/network_change_manager.mojom.h" #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_network_context_client.h" #include "services/network/test/test_network_service_client.h" #include "services/network/test/test_url_loader_client.h" @@ -83,6 +84,10 @@ GURL AddQuery(const GURL& url, mojom::NetworkContextParamsPtr CreateContextParams() { mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); // Use a fixed proxy config, to avoid dependencies on local network // configuration. params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect(); @@ -616,31 +621,6 @@ TEST_F(NetworkServiceTest, DisableDohUpgradeProviders) { TEST_F(NetworkServiceTest, DohProbe) { mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; - mojo::Remote<mojom::NetworkContext> network_context; - service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), - std::move(context_params)); - - net::DnsConfig config; - config.nameservers.push_back(net::IPEndPoint()); - config.dns_over_https_servers.emplace_back("example.com", - true /* use_post */); - auto dns_client = std::make_unique<net::MockDnsClient>( - std::move(config), net::MockDnsClientRuleList()); - dns_client->set_ignore_system_config_changes(true); - net::MockDnsClient* dns_client_ptr = dns_client.get(); - service()->host_resolver_manager()->SetDnsClientForTesting( - std::move(dns_client)); - - EXPECT_FALSE(dns_client_ptr->factory()->doh_probes_running()); - - task_environment()->FastForwardBy(NetworkService::kInitialDohProbeTimeout); - EXPECT_TRUE(dns_client_ptr->factory()->doh_probes_running()); -} - -TEST_F(NetworkServiceTest, DohProbe_NoPrimaryContext) { - mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = false; mojo::Remote<mojom::NetworkContext> network_context; service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -663,8 +643,8 @@ TEST_F(NetworkServiceTest, DohProbe_NoPrimaryContext) { } TEST_F(NetworkServiceTest, DohProbe_MultipleContexts) { + service()->StopMetricsTimerForTesting(); mojom::NetworkContextParamsPtr context_params1 = CreateContextParams(); - context_params1->primary_network_context = true; mojo::Remote<mojom::NetworkContext> network_context1; service()->CreateNetworkContext(network_context1.BindNewPipeAndPassReceiver(), std::move(context_params1)); @@ -684,7 +664,6 @@ TEST_F(NetworkServiceTest, DohProbe_MultipleContexts) { ASSERT_TRUE(dns_client_ptr->factory()->doh_probes_running()); mojom::NetworkContextParamsPtr context_params2 = CreateContextParams(); - context_params2->primary_network_context = false; mojo::Remote<mojom::NetworkContext> network_context2; service()->CreateNetworkContext(network_context2.BindNewPipeAndPassReceiver(), std::move(context_params2)); @@ -714,7 +693,6 @@ TEST_F(NetworkServiceTest, DohProbe_ContextAddedBeforeTimeout) { EXPECT_FALSE(dns_client_ptr->factory()->doh_probes_running()); mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; mojo::Remote<mojom::NetworkContext> network_context; service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -726,6 +704,7 @@ TEST_F(NetworkServiceTest, DohProbe_ContextAddedBeforeTimeout) { } TEST_F(NetworkServiceTest, DohProbe_ContextAddedAfterTimeout) { + service()->StopMetricsTimerForTesting(); net::DnsConfig config; config.nameservers.push_back(net::IPEndPoint()); config.dns_over_https_servers.emplace_back("example.com", @@ -743,7 +722,6 @@ TEST_F(NetworkServiceTest, DohProbe_ContextAddedAfterTimeout) { EXPECT_FALSE(dns_client_ptr->factory()->doh_probes_running()); mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; mojo::Remote<mojom::NetworkContext> network_context; service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -752,8 +730,8 @@ TEST_F(NetworkServiceTest, DohProbe_ContextAddedAfterTimeout) { } TEST_F(NetworkServiceTest, DohProbe_ContextRemovedBeforeTimeout) { + service()->StopMetricsTimerForTesting(); mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; mojo::Remote<mojom::NetworkContext> network_context; service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -780,8 +758,8 @@ TEST_F(NetworkServiceTest, DohProbe_ContextRemovedBeforeTimeout) { } TEST_F(NetworkServiceTest, DohProbe_ContextRemovedAfterTimeout) { + service()->StopMetricsTimerForTesting(); mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; mojo::Remote<mojom::NetworkContext> network_context; service()->CreateNetworkContext(network_context.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -931,6 +909,10 @@ class NetworkServiceTestWithService : public testing::Test { void CreateNetworkContext() { mojom::NetworkContextParamsPtr context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); network_service_->CreateNetworkContext( network_context_.BindNewPipeAndPassReceiver(), std::move(context_params)); @@ -1257,292 +1239,6 @@ TEST_F(NetworkServiceTestWithService, SetsTrustTokenKeyCommitments) { EXPECT_TRUE(result.Equals(expectation)); } -// CRLSets are not supported on iOS and Android system verifiers. -#if !defined(OS_IOS) && !defined(OS_ANDROID) - -// Verifies CRLSets take effect if configured on the service. -TEST_F(NetworkServiceTestWithService, CRLSetIsApplied) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); - test_server.AddDefaultHandlers(base::FilePath(kServicesTestData)); - ASSERT_TRUE(test_server.Start()); - - CreateNetworkContext(); - - uint32_t options = mojom::kURLLoadOptionSendSSLInfoWithResponse | - mojom::kURLLoadOptionSendSSLInfoForCertificateError; - // Make sure the test server loads fine with no CRLSet. - LoadURL(test_server.GetURL("/echo"), options); - ASSERT_EQ(net::OK, client()->completion_status().error_code); - - // Send a CRLSet that blocks the leaf cert. - std::string crl_set_bytes; - EXPECT_TRUE(base::ReadFileToString( - net::GetTestCertsDirectory().AppendASCII("crlset_by_leaf_spki.raw"), - &crl_set_bytes)); - - { - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - } - - // Flush all connections in the context, to force a new connection. A new - // verification should be attempted, due to the configuration having - // changed, thus forcing the CRLSet to be checked. - { - base::RunLoop run_loop; - context()->CloseAllConnections(run_loop.QuitClosure()); - run_loop.Run(); - } - - // Make sure the connection fails, due to the certificate being revoked. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); -} - -// Verifies CRLSets configured before creating a new network context are -// applied to that network context. -TEST_F(NetworkServiceTestWithService, CRLSetIsPassedToNewContexts) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); - test_server.AddDefaultHandlers(base::FilePath(kServicesTestData)); - ASSERT_TRUE(test_server.Start()); - - // Send a CRLSet that blocks the leaf cert, even while no NetworkContexts - // exist. - std::string crl_set_bytes; - EXPECT_TRUE(base::ReadFileToString( - net::GetTestCertsDirectory().AppendASCII("crlset_by_leaf_spki.raw"), - &crl_set_bytes)); - - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - - // Configure a new NetworkContext. - CreateNetworkContext(); - - uint32_t options = mojom::kURLLoadOptionSendSSLInfoWithResponse | - mojom::kURLLoadOptionSendSSLInfoForCertificateError; - // Make sure the connection fails, due to the certificate being revoked. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); -} - -// Verifies newer CRLSets (by sequence number) are applied. -TEST_F(NetworkServiceTestWithService, CRLSetIsUpdatedIfNewer) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); - test_server.AddDefaultHandlers(base::FilePath(kServicesTestData)); - ASSERT_TRUE(test_server.Start()); - - // Send a CRLSet that only allows the root cert if it matches a known SPKI - // hash (that matches the test server chain) - std::string crl_set_bytes; - ASSERT_TRUE(base::ReadFileToString( - net::GetTestCertsDirectory().AppendASCII("crlset_by_root_subject.raw"), - &crl_set_bytes)); - - { - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - } - - CreateNetworkContext(); - - uint32_t options = mojom::kURLLoadOptionSendSSLInfoWithResponse | - mojom::kURLLoadOptionSendSSLInfoForCertificateError; - // Make sure the connection loads, due to the root being permitted. - LoadURL(test_server.GetURL("/echo"), options); - ASSERT_EQ(net::OK, client()->completion_status().error_code); - - // Send a new CRLSet that removes trust in the root. - ASSERT_TRUE(base::ReadFileToString(net::GetTestCertsDirectory().AppendASCII( - "crlset_by_root_subject_no_spki.raw"), - &crl_set_bytes)); - - { - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - } - - // Flush all connections in the context, to force a new connection. A new - // verification should be attempted, due to the configuration having - // changed, thus forcing the CRLSet to be checked. - { - base::RunLoop run_loop; - context()->CloseAllConnections(run_loop.QuitClosure()); - run_loop.Run(); - } - - // Make sure the connection fails, due to the certificate being revoked. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); -} - -// Verifies that attempting to send an older CRLSet (by sequence number) -// does not apply to existing or new contexts. -TEST_F(NetworkServiceTestWithService, CRLSetDoesNotDowngrade) { - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); - test_server.AddDefaultHandlers(base::FilePath(kServicesTestData)); - ASSERT_TRUE(test_server.Start()); - - // Send a CRLSet that blocks the root certificate by subject name. - std::string crl_set_bytes; - ASSERT_TRUE(base::ReadFileToString(net::GetTestCertsDirectory().AppendASCII( - "crlset_by_root_subject_no_spki.raw"), - &crl_set_bytes)); - - { - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - } - - CreateNetworkContext(); - - uint32_t options = mojom::kURLLoadOptionSendSSLInfoWithResponse | - mojom::kURLLoadOptionSendSSLInfoForCertificateError; - // Make sure the connection fails, due to the certificate being revoked. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); - - // Attempt to configure an older CRLSet that allowed trust in the root. - ASSERT_TRUE(base::ReadFileToString( - net::GetTestCertsDirectory().AppendASCII("crlset_by_root_subject.raw"), - &crl_set_bytes)); - - { - base::RunLoop run_loop; - service()->UpdateCRLSet(base::as_bytes(base::make_span(crl_set_bytes)), - run_loop.QuitClosure()); - run_loop.Run(); - } - - // Flush all connections in the context, to force a new connection. A new - // verification should be attempted, due to the configuration having - // changed, thus forcing the CRLSet to be checked. - { - base::RunLoop run_loop; - context()->CloseAllConnections(run_loop.QuitClosure()); - run_loop.Run(); - } - - // Make sure the connection still fails, due to the newer CRLSet still - // applying. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); - - // Create a new NetworkContext and ensure the latest CRLSet is still - // applied. - network_context_.reset(); - CreateNetworkContext(); - - // The newer CRLSet that blocks the connection should still apply, even to - // new NetworkContexts. - LoadURL(test_server.GetURL("/echo"), options); - EXPECT_EQ(net::ERR_INSECURE_RESPONSE, - client()->completion_status().error_code); - ASSERT_TRUE(client()->completion_status().ssl_info.has_value()); - EXPECT_TRUE(client()->completion_status().ssl_info->cert_status & - net::CERT_STATUS_REVOKED); -} - -#endif // !defined(OS_IOS) && !defined(OS_ANDROID) - -// TODO(crbug.com/860189): AIA tests fail on iOS -#if defined(OS_IOS) -#define MAYBE_AIAFetching DISABLED_AIAFetching -#else -#define MAYBE_AIAFetching AIAFetching -#endif -// Test |primary_network_context|, which is required by AIA fetching, among -// other things. -TEST_F(NetworkServiceTestWithService, MAYBE_AIAFetching) { - mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; - - network_service_->CreateNetworkContext( - network_context_.BindNewPipeAndPassReceiver(), std::move(context_params)); - - net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); - net::EmbeddedTestServer::ServerCertificateConfig cert_config; - cert_config.intermediate = net::EmbeddedTestServer::IntermediateType::kByAIA; - test_server.SetSSLConfig(cert_config); - test_server.AddDefaultHandlers(base::FilePath(kServicesTestData)); - ASSERT_TRUE(test_server.Start()); - - LoadURL(test_server.GetURL("/echo"), - mojom::kURLLoadOptionSendSSLInfoWithResponse); - EXPECT_EQ(net::OK, client()->completion_status().error_code); - ASSERT_TRUE(client()->response_head()); - EXPECT_EQ( - 0u, client()->response_head()->cert_status & net::CERT_STATUS_ALL_ERRORS); - ASSERT_TRUE(client()->ssl_info()); - ASSERT_TRUE(client()->ssl_info()->cert); - EXPECT_EQ(2u, client()->ssl_info()->cert->intermediate_buffers().size()); - ASSERT_TRUE(client()->ssl_info()->unverified_cert); - EXPECT_EQ( - 0u, client()->ssl_info()->unverified_cert->intermediate_buffers().size()); -} - -// Check that destroying a NetworkContext with |primary_network_context| set -// destroys all other NetworkContexts. -TEST_F(NetworkServiceTestWithService, - DestroyingPrimaryNetworkContextDestroysOtherContexts) { - mojom::NetworkContextParamsPtr context_params = CreateContextParams(); - context_params->primary_network_context = true; - mojo::Remote<mojom::NetworkContext> cert_validating_network_context; - network_service_->CreateNetworkContext( - cert_validating_network_context.BindNewPipeAndPassReceiver(), - std::move(context_params)); - - base::RunLoop run_loop; - mojo::Remote<mojom::NetworkContext> network_context; - network_service_->CreateNetworkContext( - network_context.BindNewPipeAndPassReceiver(), CreateContextParams()); - network_context.set_disconnect_handler(run_loop.QuitClosure()); - - // Wait until the new NetworkContext has been created, so it's not created - // after the primary NetworkContext is destroyed. - network_service_.FlushForTesting(); - - // Destroying |cert_validating_network_context| should result in destroying - // |network_context| as well. - cert_validating_network_context.reset(); - run_loop.Run(); - EXPECT_FALSE(network_context.is_connected()); -} - TEST_F(NetworkServiceTestWithService, GetDnsConfigChangeManager) { mojo::Remote<mojom::DnsConfigChangeManager> remote; ASSERT_FALSE(remote.is_bound()); @@ -1699,6 +1395,10 @@ class NetworkServiceNetworkDelegateTest : public NetworkServiceTest { void CreateNetworkContext() { mojom::NetworkContextParamsPtr context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); service()->CreateNetworkContext( network_context_.BindNewPipeAndPassReceiver(), std::move(context_params)); diff --git a/chromium/services/network/origin_policy/origin_policy_manager_unittest.cc b/chromium/services/network/origin_policy/origin_policy_manager_unittest.cc index ab2c413192a..37a759cdda9 100644 --- a/chromium/services/network/origin_policy/origin_policy_manager_unittest.cc +++ b/chromium/services/network/origin_policy/origin_policy_manager_unittest.cc @@ -12,6 +12,7 @@ #include "services/network/network_context.h" #include "services/network/network_service.h" #include "services/network/public/cpp/origin_policy.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" @@ -30,6 +31,10 @@ class OriginPolicyManagerTest : public testing::Test { network_service_ = NetworkService::CreateForTesting(); auto context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); // Use a fixed proxy config, to avoid dependencies on local network // configuration. context_params->initial_proxy_config = diff --git a/chromium/services/network/p2p/socket.h b/chromium/services/network/p2p/socket.h index 3904fb6414a..63a42ac6b29 100644 --- a/chromium/services/network/p2p/socket.h +++ b/chromium/services/network/p2p/socket.h @@ -26,6 +26,7 @@ namespace net { class NetLog; +class NetworkIsolationKey; } namespace network { @@ -79,10 +80,12 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) P2PSocket : public mojom::P2PSocket { // in the valid range. // If |local_address.port()| is nonzero and not in the valid range, // initialization will fail. + // |network_isolation_key| specifies the network stack cache shard to used. virtual void Init(const net::IPEndPoint& local_address, uint16_t min_port, uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) = 0; + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) = 0; mojo::PendingRemote<mojom::P2PSocketClient> ReleaseClientForTesting(); mojo::PendingReceiver<mojom::P2PSocket> ReleaseReceiverForTesting(); diff --git a/chromium/services/network/p2p/socket_manager.cc b/chromium/services/network/p2p/socket_manager.cc index 56dc77eddb7..8b149110739 100644 --- a/chromium/services/network/p2p/socket_manager.cc +++ b/chromium/services/network/p2p/socket_manager.cc @@ -15,6 +15,7 @@ #include "net/base/address_list.h" #include "net/base/net_errors.h" #include "net/base/network_interfaces.h" +#include "net/base/network_isolation_key.h" #include "net/base/sys_addrinfo.h" #include "net/dns/host_resolver.h" #include "net/log/net_log_source.h" @@ -79,7 +80,9 @@ class P2PSocketManager::DnsRequest { DnsRequest(net::HostResolver* host_resolver, bool enable_mdns) : resolver_(host_resolver), enable_mdns_(enable_mdns) {} - void Resolve(const std::string& host_name, DoneCallback done_callback) { + void Resolve(const std::string& host_name, + const net::NetworkIsolationKey& network_isolation_key, + DoneCallback done_callback) { DCHECK(!done_callback.is_null()); host_name_ = host_name; @@ -107,8 +110,8 @@ class P2PSocketManager::DnsRequest { parameters.source = net::HostResolverSource::MULTICAST_DNS; #endif // ENABLE_MDNS } - request_ = - resolver_->CreateRequest(host, net::NetLogWithSource(), parameters); + request_ = resolver_->CreateRequest(host, network_isolation_key, + net::NetLogWithSource(), parameters); int result = request_->Start(base::BindOnce( &P2PSocketManager::DnsRequest::OnDone, base::Unretained(this))); @@ -144,6 +147,7 @@ class P2PSocketManager::DnsRequest { }; P2PSocketManager::P2PSocketManager( + const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> trusted_socket_manager_client, mojo::PendingReceiver<mojom::P2PTrustedSocketManager> @@ -153,6 +157,7 @@ P2PSocketManager::P2PSocketManager( net::URLRequestContext* url_request_context) : delete_callback_(std::move(delete_callback)), url_request_context_(url_request_context), + network_isolation_key_(network_isolation_key), network_list_task_runner_(base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_VISIBLE})), trusted_socket_manager_client_(std::move(trusted_socket_manager_client)), @@ -286,7 +291,7 @@ void P2PSocketManager::GetHostAddress( DnsRequest* request_ptr = request.get(); dns_requests_.insert(std::move(request)); request_ptr->Resolve( - host_name, + host_name, network_isolation_key_, base::BindOnce(&P2PSocketManager::OnAddressResolved, base::Unretained(this), request_ptr, std::move(callback))); } @@ -327,7 +332,7 @@ void P2PSocketManager::CreateSocket( // Init() may call SocketManager::DestroySocket(), so it must be called after // adding the socket to |sockets_|. socket_ptr->Init(local_address, port_range.min_port, port_range.max_port, - remote_address); + remote_address, network_isolation_key_); } void P2PSocketManager::StartRtpDump(bool incoming, bool outgoing) { diff --git a/chromium/services/network/p2p/socket_manager.h b/chromium/services/network/p2p/socket_manager.h index f6e793fa556..79f6546cf68 100644 --- a/chromium/services/network/p2p/socket_manager.h +++ b/chromium/services/network/p2p/socket_manager.h @@ -26,6 +26,7 @@ #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/base/network_change_notifier.h" +#include "net/base/network_isolation_key.h" #include "services/network/p2p/socket.h" #include "services/network/p2p/socket_throttler.h" #include "services/network/public/cpp/p2p_socket_type.h" @@ -58,6 +59,7 @@ class P2PSocketManager // P2PSocketManager. The P2PSocketManager must be destroyed before the // |url_request_context|. P2PSocketManager( + const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> trusted_socket_manager_client, mojo::PendingReceiver<mojom::P2PTrustedSocketManager> @@ -121,6 +123,7 @@ class P2PSocketManager DeleteCallback delete_callback_; net::URLRequestContext* url_request_context_; + const net::NetworkIsolationKey network_isolation_key_; std::unique_ptr<ProxyResolvingClientSocketFactory> proxy_resolving_socket_factory_; diff --git a/chromium/services/network/p2p/socket_tcp.cc b/chromium/services/network/p2p/socket_tcp.cc index 385433b53d8..9d7d9fd7495 100644 --- a/chromium/services/network/p2p/socket_tcp.cc +++ b/chromium/services/network/p2p/socket_tcp.cc @@ -14,6 +14,7 @@ #include "jingle/glue/fake_ssl_client_socket.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_handle.h" #include "net/socket/ssl_client_socket.h" @@ -78,10 +79,12 @@ void P2PSocketTcpBase::InitAccepted(const net::IPEndPoint& remote_address, DoRead(); } -void P2PSocketTcpBase::Init(const net::IPEndPoint& local_address, - uint16_t min_port, - uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) { +void P2PSocketTcpBase::Init( + const net::IPEndPoint& local_address, + uint16_t min_port, + uint16_t max_port, + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) { DCHECK(!socket_); remote_address_ = remote_address; @@ -105,7 +108,7 @@ void P2PSocketTcpBase::Init(const net::IPEndPoint& local_address, // a problem on multi-homed host. socket_ = proxy_resolving_socket_factory_->CreateSocket( - GURL("https://" + dest_host_port_pair.ToString()), + GURL("https://" + dest_host_port_pair.ToString()), network_isolation_key, IsTlsClientSocket(type_)); if (IsPseudoTlsClientSocket(type_)) { diff --git a/chromium/services/network/p2p/socket_tcp.h b/chromium/services/network/p2p/socket_tcp.h index e4640f181d7..b086186a6aa 100644 --- a/chromium/services/network/p2p/socket_tcp.h +++ b/chromium/services/network/p2p/socket_tcp.h @@ -48,7 +48,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) P2PSocketTcpBase : public P2PSocket { void Init(const net::IPEndPoint& local_address, uint16_t min_port, uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) override; + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) override; // mojom::P2PSocket implementation: void Send(const std::vector<int8_t>& data, diff --git a/chromium/services/network/p2p/socket_tcp_server.cc b/chromium/services/network/p2p/socket_tcp_server.cc index d2bdc4fcd2b..f9dafef98aa 100644 --- a/chromium/services/network/p2p/socket_tcp_server.cc +++ b/chromium/services/network/p2p/socket_tcp_server.cc @@ -37,10 +37,12 @@ P2PSocketTcpServer::P2PSocketTcpServer( P2PSocketTcpServer::~P2PSocketTcpServer() = default; // TODO(guidou): Add support for port range. -void P2PSocketTcpServer::Init(const net::IPEndPoint& local_address, - uint16_t min_port, - uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) { +void P2PSocketTcpServer::Init( + const net::IPEndPoint& local_address, + uint16_t min_port, + uint16_t max_port, + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) { int result = socket_->Listen(local_address, kListenBacklog); if (result < 0) { LOG(ERROR) << "Listen() failed: " << result; diff --git a/chromium/services/network/p2p/socket_tcp_server.h b/chromium/services/network/p2p/socket_tcp_server.h index b496ba2574e..527110ea7d7 100644 --- a/chromium/services/network/p2p/socket_tcp_server.h +++ b/chromium/services/network/p2p/socket_tcp_server.h @@ -39,7 +39,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) P2PSocketTcpServer : public P2PSocket { void Init(const net::IPEndPoint& local_address, uint16_t min_port, uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) override; + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) override; // mojom::P2PSocket implementation: void Send(const std::vector<int8_t>& data, diff --git a/chromium/services/network/p2p/socket_tcp_server_unittest.cc b/chromium/services/network/p2p/socket_tcp_server_unittest.cc index 87876968539..0c0ec2f4c5d 100644 --- a/chromium/services/network/p2p/socket_tcp_server_unittest.cc +++ b/chromium/services/network/p2p/socket_tcp_server_unittest.cc @@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/test/task_environment.h" #include "net/base/completion_once_callback.h" +#include "net/base/network_isolation_key.h" #include "services/network/p2p/socket_tcp.h" #include "services/network/p2p/socket_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -105,7 +106,8 @@ class P2PSocketTcpServerTest : public testing::Test { P2PHostAndIPEndPoint dest; dest.ip_address = ParseAddress(kTestIpAddress1, kTestPort1); - p2p_socket_->Init(ParseAddress(kTestLocalIpAddress, 0), 0, 0, dest); + p2p_socket_->Init(ParseAddress(kTestLocalIpAddress, 0), 0, 0, dest, + net::NetworkIsolationKey()); EXPECT_TRUE(socket_->listening()); base::RunLoop().RunUntilIdle(); } diff --git a/chromium/services/network/p2p/socket_tcp_unittest.cc b/chromium/services/network/p2p/socket_tcp_unittest.cc index 1150cf293b8..dd1f75cb2a2 100644 --- a/chromium/services/network/p2p/socket_tcp_unittest.cc +++ b/chromium/services/network/p2p/socket_tcp_unittest.cc @@ -9,12 +9,17 @@ #include "base/run_loop.h" #include "base/stl_util.h" +#include "base/strings/stringprintf.h" #include "base/sys_byteorder.h" #include "base/test/bind_test_util.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "jingle/glue/fake_ssl_client_socket.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "net/base/features.h" +#include "net/base/network_isolation_key.h" +#include "net/dns/mock_host_resolver.h" #include "net/socket/socket_test_util.h" #include "net/socket/stream_socket.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" @@ -522,13 +527,104 @@ TEST(P2PSocketTcpWithPseudoTlsTest, Basic) { &factory); P2PHostAndIPEndPoint dest; dest.ip_address = server_addr; - host.Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0), 0, 0, dest); + host.Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0), 0, 0, dest, + net::NetworkIsolationKey()); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(data_provider.AllReadDataConsumed()); EXPECT_TRUE(data_provider.AllWriteDataConsumed()); } +// Test the case where P2PHostAndIPEndPoint::hostname is populated. Make sure +// there's a DNS lookup using the right hostname and NetworkIsolationKey. +TEST(P2PSocketTcpWithPseudoTlsTest, Hostname) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + net::features::kSplitHostCacheByNetworkIsolationKey); + + const char kHostname[] = "foo.test"; + base::test::TaskEnvironment task_environment( + base::test::TaskEnvironment::MainThreadType::IO); + + mojo::PendingRemote<mojom::P2PSocketClient> socket_client; + mojo::PendingRemote<mojom::P2PSocket> socket; + auto socket_receiver = socket.InitWithNewPipeAndPassReceiver(); + + FakeSocketClient fake_client2(std::move(socket), + socket_client.InitWithNewPipeAndPassReceiver()); + EXPECT_CALL(fake_client2, SocketCreated(_, _)).Times(1); + + net::TestURLRequestContext context(true); + net::MockClientSocketFactory mock_socket_factory; + context.set_client_socket_factory(&mock_socket_factory); + net::MockCachingHostResolver host_resolver; + host_resolver.rules()->AddRule(kHostname, "1.2.3.4"); + context.set_host_resolver(&host_resolver); + context.Init(); + ProxyResolvingClientSocketFactory factory(&context); + + base::StringPiece ssl_client_hello = + jingle_glue::FakeSSLClientSocket::GetSslClientHello(); + base::StringPiece ssl_server_hello = + jingle_glue::FakeSSLClientSocket::GetSslServerHello(); + net::MockRead reads[] = { + net::MockRead(net::ASYNC, ssl_server_hello.data(), + ssl_server_hello.size()), + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + net::MockWrite writes[] = {net::MockWrite( + net::SYNCHRONOUS, ssl_client_hello.data(), ssl_client_hello.size())}; + net::StaticSocketDataProvider data_provider(reads, writes); + net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234); + data_provider.set_connect_data( + net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr)); + mock_socket_factory.AddSocketDataProvider(&data_provider); + + FakeP2PSocketDelegate socket_delegate; + P2PSocketTcp host(&socket_delegate, std::move(socket_client), + std::move(socket_receiver), P2P_SOCKET_SSLTCP_CLIENT, + &factory); + P2PHostAndIPEndPoint dest; + dest.ip_address = server_addr; + dest.hostname = kHostname; + net::NetworkIsolationKey network_isolation_key = + net::NetworkIsolationKey::CreateTransient(); + host.Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0), 0, 0, dest, + network_isolation_key); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + + // Check that the URL in kHostname is in the HostCache, with + // |network_isolation_key|. + const net::HostPortPair kHostPortPair = net::HostPortPair(kHostname, 0); + net::HostResolver::ResolveHostParameters params; + params.source = net::HostResolverSource::LOCAL_ONLY; + std::unique_ptr<net::HostResolver::ResolveHostRequest> request1 = + context.host_resolver()->CreateRequest(kHostPortPair, + network_isolation_key, + net::NetLogWithSource(), params); + net::TestCompletionCallback callback1; + int result = request1->Start(callback1.callback()); + EXPECT_EQ(net::OK, callback1.GetResult(result)); + + // Check that the hostname is not in the DNS cache for other possible NIKs. + const url::Origin kDestinationOrigin = + url::Origin::Create(GURL(base::StringPrintf("https://%s", kHostname))); + const net::NetworkIsolationKey kOtherNiks[] = { + net::NetworkIsolationKey(), + net::NetworkIsolationKey(kDestinationOrigin /* top_frame_origin */, + kDestinationOrigin /* frame_origin */)}; + for (const auto& other_nik : kOtherNiks) { + std::unique_ptr<net::HostResolver::ResolveHostRequest> request2 = + context.host_resolver()->CreateRequest(kHostPortPair, other_nik, + net::NetLogWithSource(), params); + net::TestCompletionCallback callback2; + int result = request2->Start(callback2.callback()); + EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, callback2.GetResult(result)); + } +} + class P2PSocketTcpWithTlsTest : public testing::TestWithParam<std::tuple<net::IoMode, P2PSocketType>> {}; @@ -585,7 +681,8 @@ TEST_P(P2PSocketTcpWithTlsTest, Basic) { } P2PHostAndIPEndPoint dest; dest.ip_address = server_addr; - host->Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0), 0, 0, dest); + host->Init(net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0), 0, 0, dest, + net::NetworkIsolationKey()); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(data_provider.AllReadDataConsumed()); diff --git a/chromium/services/network/p2p/socket_udp.cc b/chromium/services/network/p2p/socket_udp.cc index b6d6392873d..bf042cfd6de 100644 --- a/chromium/services/network/p2p/socket_udp.cc +++ b/chromium/services/network/p2p/socket_udp.cc @@ -129,7 +129,8 @@ P2PSocketUdp::~P2PSocketUdp() = default; void P2PSocketUdp::Init(const net::IPEndPoint& local_address, uint16_t min_port, uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) { + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) { DCHECK(!socket_); DCHECK((min_port == 0 && max_port == 0) || min_port > 0); DCHECK_LE(min_port, max_port); diff --git a/chromium/services/network/p2p/socket_udp.h b/chromium/services/network/p2p/socket_udp.h index 7ffa5e5fead..e2e4f540845 100644 --- a/chromium/services/network/p2p/socket_udp.h +++ b/chromium/services/network/p2p/socket_udp.h @@ -58,7 +58,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) P2PSocketUdp : public P2PSocket { void Init(const net::IPEndPoint& local_address, uint16_t min_port, uint16_t max_port, - const P2PHostAndIPEndPoint& remote_address) override; + const P2PHostAndIPEndPoint& remote_address, + const net::NetworkIsolationKey& network_isolation_key) override; // mojom::P2PSocket implementation: void Send(const std::vector<int8_t>& data, diff --git a/chromium/services/network/p2p/socket_udp_unittest.cc b/chromium/services/network/p2p/socket_udp_unittest.cc index 624a3b05dac..48d9379dcf8 100644 --- a/chromium/services/network/p2p/socket_udp_unittest.cc +++ b/chromium/services/network/p2p/socket_udp_unittest.cc @@ -21,6 +21,7 @@ #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/log/net_log_with_source.h" #include "net/socket/datagram_server_socket.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" @@ -233,7 +234,8 @@ class P2PSocketUdpTest : public testing::Test { socket_impl_->Init( local_address_, 0, 0, P2PHostAndIPEndPoint(std::string(), - ParseAddress(kTestIpAddress1, kTestPort1))); + ParseAddress(kTestIpAddress1, kTestPort1)), + net::NetworkIsolationKey()); socket_ = GetSocketFromHost(socket_impl_.get()); dest1_ = ParseAddress(kTestIpAddress1, kTestPort1); @@ -577,7 +579,8 @@ TEST_F(P2PSocketUdpTest, PortRangeImplicitPort) { socket_impl->Init( local_address, min_port, max_port, P2PHostAndIPEndPoint(std::string(), - ParseAddress(kTestIpAddress1, kTestPort1))); + ParseAddress(kTestIpAddress1, kTestPort1)), + net::NetworkIsolationKey()); FakeDatagramServerSocket* socket = GetSocketFromHost(socket_impl.get()); net::IPEndPoint bound_address; @@ -601,7 +604,8 @@ TEST_F(P2PSocketUdpTest, PortRangeImplicitPort) { socket_impl_ptr->Init( local_address, min_port, max_port, P2PHostAndIPEndPoint(std::string(), - ParseAddress(kTestIpAddress1, kTestPort1))); + ParseAddress(kTestIpAddress1, kTestPort1)), + net::NetworkIsolationKey()); base::RunLoop().RunUntilIdle(); @@ -638,7 +642,8 @@ TEST_F(P2PSocketUdpTest, PortRangeExplictValidPort) { socket_host->Init( local_address, min_port, max_port, P2PHostAndIPEndPoint(std::string(), - ParseAddress(kTestIpAddress1, kTestPort1))); + ParseAddress(kTestIpAddress1, kTestPort1)), + net::NetworkIsolationKey()); FakeDatagramServerSocket* fake_socket = GetSocketFromHost(socket_host.get()); net::IPEndPoint bound_address; @@ -679,7 +684,8 @@ TEST_F(P2PSocketUdpTest, PortRangeExplictInvalidPort) { socket_impl_ptr->Init( local_address, min_port, max_port, P2PHostAndIPEndPoint(std::string(), - ParseAddress(kTestIpAddress1, kTestPort1))); + ParseAddress(kTestIpAddress1, kTestPort1)), + net::NetworkIsolationKey()); base::RunLoop().RunUntilIdle(); diff --git a/chromium/services/network/proxy_resolving_client_socket.cc b/chromium/services/network/proxy_resolving_client_socket.cc index fc6008039ba..c1fe895100c 100644 --- a/chromium/services/network/proxy_resolving_client_socket.cc +++ b/chromium/services/network/proxy_resolving_client_socket.cc @@ -38,10 +38,12 @@ ProxyResolvingClientSocket::ProxyResolvingClientSocket( net::HttpNetworkSession* network_session, const net::CommonConnectJobParams* common_connect_job_params, const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, bool use_tls) : network_session_(network_session), common_connect_job_params_(common_connect_job_params), url_(url), + network_isolation_key_(network_isolation_key), use_tls_(use_tls), net_log_(net::NetLogWithSource::Make(network_session_->net_log(), net::NetLogSourceType::SOCKET)), @@ -245,8 +247,8 @@ int ProxyResolvingClientSocket::DoProxyResolve() { // // TODO(https://crbug.com/1023439): Pass along a NetworkIsolationKey. return network_session_->proxy_resolution_service()->ResolveProxy( - url_, net::HttpRequestHeaders::kPostMethod, - net::NetworkIsolationKey::Todo(), &proxy_info_, + url_, net::HttpRequestHeaders::kPostMethod, network_isolation_key_, + &proxy_info_, base::BindOnce(&ProxyResolvingClientSocket::OnIOComplete, base::Unretained(this)), &proxy_resolve_request_, net_log_); @@ -299,7 +301,7 @@ int ProxyResolvingClientSocket::DoInitConnection() { use_tls_, net::HostPortPair::FromURL(url_), proxy_info_.proxy_server(), proxy_annotation_tag, &ssl_config, &ssl_config, true /* force_tunnel */, net::PRIVACY_MODE_DISABLED, net::OnHostResolutionCallback(), - net::MAXIMUM_PRIORITY, net::SocketTag(), net::NetworkIsolationKey(), + net::MAXIMUM_PRIORITY, net::SocketTag(), network_isolation_key_, false /* disable_secure_dns */, common_connect_job_params_, this); return connect_job_->Connect(); } diff --git a/chromium/services/network/proxy_resolving_client_socket.h b/chromium/services/network/proxy_resolving_client_socket.h index 07271b53c2b..6ce2a2bd215 100644 --- a/chromium/services/network/proxy_resolving_client_socket.h +++ b/chromium/services/network/proxy_resolving_client_socket.h @@ -17,6 +17,7 @@ #include "base/memory/weak_ptr.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/log/net_log_with_source.h" #include "net/proxy_resolution/proxy_info.h" #include "net/proxy_resolution/proxy_resolution_service.h" @@ -31,6 +32,7 @@ struct CommonConnectJobParams; class HttpAuthController; class HttpResponseInfo; class HttpNetworkSession; +class NetworkIsolationKey; class ProxyResolutionRequest; } // namespace net @@ -55,6 +57,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocket net::HttpNetworkSession* network_session, const net::CommonConnectJobParams* common_connect_job_params, const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, bool use_tls); ~ProxyResolvingClientSocket() override; @@ -132,6 +135,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocket std::unique_ptr<net::ProxyResolutionRequest> proxy_resolve_request_; net::ProxyInfo proxy_info_; const GURL url_; + const net::NetworkIsolationKey network_isolation_key_; const bool use_tls_; net::NetLogWithSource net_log_; diff --git a/chromium/services/network/proxy_resolving_client_socket_factory.cc b/chromium/services/network/proxy_resolving_client_socket_factory.cc index 0d00e6db3cc..47d01a340d3 100644 --- a/chromium/services/network/proxy_resolving_client_socket_factory.cc +++ b/chromium/services/network/proxy_resolving_client_socket_factory.cc @@ -76,6 +76,7 @@ ProxyResolvingClientSocketFactory::~ProxyResolvingClientSocketFactory() {} std::unique_ptr<ProxyResolvingClientSocket> ProxyResolvingClientSocketFactory::CreateSocket( const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, bool use_tls) { // |request_context|'s HttpAuthCache might have updates. For example, a user // might have since entered proxy credentials. Clear the http auth of @@ -96,7 +97,8 @@ ProxyResolvingClientSocketFactory::CreateSocket( ->http_auth_cache(); network_session_->http_auth_cache()->CopyProxyEntriesFrom(*other_auth_cache); return std::make_unique<ProxyResolvingClientSocket>( - network_session_.get(), common_connect_job_params_.get(), url, use_tls); + network_session_.get(), common_connect_job_params_.get(), url, + network_isolation_key, use_tls); } } // namespace network diff --git a/chromium/services/network/proxy_resolving_client_socket_factory.h b/chromium/services/network/proxy_resolving_client_socket_factory.h index 9192d57ea15..01410d980d9 100644 --- a/chromium/services/network/proxy_resolving_client_socket_factory.h +++ b/chromium/services/network/proxy_resolving_client_socket_factory.h @@ -16,6 +16,7 @@ namespace net { struct CommonConnectJobParams; class HttpNetworkSession; +class NetworkIsolationKey; class URLRequestContext; } // namespace net @@ -37,11 +38,20 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocketFactory { // doesn't need to explicitly sanitize the url, any sensitive data (like // embedded usernames and passwords), and local data (i.e. reference fragment) // will be sanitized by net::ProxyService::ResolveProxyHelper() before the url - // is disclosed to the proxy. If |use_tls|, TLS connect will be used in - // addition to TCP connect. The URLRequestContext's SSL configurations will be - // respected when establishing a TLS connection. - std::unique_ptr<ProxyResolvingClientSocket> CreateSocket(const GURL& url, - bool use_tls); + // is disclosed to the proxy. + // + // |network_isolation_key| indicates the network shard to use for storing + // shared network state (DNS cache entries, shared H2/QUIC proxy connections, + // etc). Proxy connections will only be shared with other + // ProxyResolvingClientSockets, not with standards HTTP/HTTPS requests. + // + // If |use_tls| is true, TLS connect will be used in addition to TCP connect. + // The URLRequestContext's SSL configurations will be respected when + // establishing a TLS connection. + std::unique_ptr<ProxyResolvingClientSocket> CreateSocket( + const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, + bool use_tls); const net::HttpNetworkSession* network_session() const { return network_session_.get(); diff --git a/chromium/services/network/proxy_resolving_client_socket_unittest.cc b/chromium/services/network/proxy_resolving_client_socket_unittest.cc index 84a404f336a..c9941339291 100644 --- a/chromium/services/network/proxy_resolving_client_socket_unittest.cc +++ b/chromium/services/network/proxy_resolving_client_socket_unittest.cc @@ -13,17 +13,21 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" +#include "net/base/features.h" #include "net/base/network_isolation_key.h" #include "net/base/test_completion_callback.h" #include "net/dns/mock_host_resolver.h" +#include "net/http/http_proxy_connect_job.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/proxy_resolution/mock_proxy_resolver.h" #include "net/proxy_resolution/proxy_config_service_fixed.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" #include "net/socket/client_socket_pool_manager.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_test_util_common.h" #include "net/test/gtest_util.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request_test_util.h" @@ -44,7 +48,7 @@ class TestURLRequestContextWithProxy : public net::TestURLRequestContext { net::ConfiguredProxyResolutionService::CreateFixedFromPacResult( pac_result, TRAFFIC_ANNOTATION_FOR_TESTS)); // net::MockHostResolver maps all hosts to localhost. - auto host_resolver = std::make_unique<net::MockHostResolver>(); + auto host_resolver = std::make_unique<net::MockCachingHostResolver>(); context_storage_.set_host_resolver(std::move(host_resolver)); set_client_socket_factory(client_socket_factory); Init(); @@ -61,7 +65,14 @@ class ProxyResolvingClientSocketTest ProxyResolvingClientSocketTest() : context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT", &mock_client_socket_factory_), - use_tls_(GetParam()) {} + use_tls_(GetParam()) { + feature_list_.InitWithFeatures( + // enabled_features + {net::features::kPartitionConnectionsByNetworkIsolationKey, + net::features::kSplitHostCacheByNetworkIsolationKey}, + // disabled_features + {}); + } ~ProxyResolvingClientSocketTest() override {} @@ -72,6 +83,7 @@ class ProxyResolvingClientSocketTest } base::test::TaskEnvironment task_environment_; + base::test::ScopedFeatureList feature_list_; TestURLRequestContextWithProxy context_with_proxy_; net::MockClientSocketFactory mock_client_socket_factory_; const bool use_tls_; @@ -81,6 +93,183 @@ INSTANTIATE_TEST_SUITE_P(All, ProxyResolvingClientSocketTest, ::testing::Bool()); +// Checks the correct NetworkIsolationKey is used for host resolution in the +// case no proxy is in use. +TEST_P(ProxyResolvingClientSocketTest, NetworkIsolationKeyDirect) { + // This deliberately uses a different origin than the one being connected to. + url::Origin kNetworkIsolationKeyOrigin = + url::Origin::Create(GURL("https://foopy.test")); + net::NetworkIsolationKey kNetworkIsolationKey( + kNetworkIsolationKeyOrigin /* top_frame_origin */, + kNetworkIsolationKeyOrigin /* frame_origin */); + + TestURLRequestContextWithProxy url_request_context( + "DIRECT", &mock_client_socket_factory_); + const GURL kDestination("https://dest.test/"); + net::StaticSocketDataProvider socket_data; + mock_client_socket_factory_.AddSocketDataProvider(&socket_data); + net::SSLSocketDataProvider ssl_data(net::ASYNC, net::OK); + mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_data); + + ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( + &url_request_context); + std::unique_ptr<ProxyResolvingClientSocket> socket = + proxy_resolving_socket_factory.CreateSocket( + kDestination, kNetworkIsolationKey, use_tls_); + net::TestCompletionCallback callback; + int status = socket->Connect(callback.callback()); + EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); + + // Check that the URL in kDestination is in the HostCache, with + // kNetworkIsolationInfo. + const net::HostPortPair kDestinationHostPortPair = + net::HostPortPair::FromURL(kDestination); + net::HostResolver::ResolveHostParameters params; + params.source = net::HostResolverSource::LOCAL_ONLY; + std::unique_ptr<net::HostResolver::ResolveHostRequest> request1 = + url_request_context.host_resolver()->CreateRequest( + kDestinationHostPortPair, kNetworkIsolationKey, + net::NetLogWithSource(), params); + net::TestCompletionCallback callback2; + int result = request1->Start(callback2.callback()); + EXPECT_EQ(net::OK, callback2.GetResult(result)); + + // Check that the hostname is not in the DNS cache for other possible NIKs. + const url::Origin kDestinationOrigin = url::Origin::Create(kDestination); + const net::NetworkIsolationKey kOtherNiks[] = { + net::NetworkIsolationKey(), + net::NetworkIsolationKey(kDestinationOrigin /* top_frame_origin */, + kDestinationOrigin /* frame_origin */)}; + for (const auto& other_nik : kOtherNiks) { + std::unique_ptr<net::HostResolver::ResolveHostRequest> request2 = + url_request_context.host_resolver()->CreateRequest( + kDestinationHostPortPair, other_nik, net::NetLogWithSource(), + params); + net::TestCompletionCallback callback3; + int result = request2->Start(callback3.callback()); + EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, callback3.GetResult(result)); + } +} + +// Checks the correct NetworkIsolationKey is used for host resolution in the +// case an H2 proxy is in use. In the non-H2 proxy case, the NetworkIsolationKey +// makes little difference, but in the H2 case, it affects which requests use +// the same session. Unlike other tests, this test creates a +// ProxyResolvingClientSocket instead of using the factory class, because it +// uses SpdySessionDependencies to create a NetworkSession configured to test +// H2. +TEST_P(ProxyResolvingClientSocketTest, NetworkIsolationKeyWithH2Proxy) { + // Don't bother running this test in the SSL case - it's complicated enough + // without it, and testing HTTPS on top of H2 provides minimal value, since + // SSL is mocked out anyways and there are other tests that cover it on top of + // HTTP/1.x tunnels. + if (GetParam() == true) + return; + net::SpdySessionDependencies session_deps; + session_deps.proxy_resolution_service = + net::ConfiguredProxyResolutionService::CreateFixedFromPacResult( + "HTTPS proxy.test:80", TRAFFIC_ANNOTATION_FOR_TESTS); + std::unique_ptr<net::HttpNetworkSession> http_network_session = + net::SpdySessionDependencies::SpdyCreateSession(&session_deps); + + net::NetworkIsolationKey kNetworkIsolationKey1 = + net::NetworkIsolationKey::CreateTransient(); + net::NetworkIsolationKey kNetworkIsolationKey2 = + net::NetworkIsolationKey::CreateTransient(); + + const GURL kDestination1("https://dest1.test/"); + const GURL kDestination2("https://dest2.test/"); + const GURL kDestination3("https://dest3.test/"); + + // A tunnel to kDestination1 and kDestination3 is requested using + // kNetworkIsolationKey1, so they should use the same H2 session, and a tunnel + // to kDestination2 is requested using kNetworkIsolationKey2, which should use + // a different session. + net::SpdyTestUtil spdy_util1; + spdy::SpdySerializedFrame connect_dest1(spdy_util1.ConstructSpdyConnect( + nullptr, 0, 1, net::HttpProxyConnectJob::kH2QuicTunnelPriority, + net::HostPortPair::FromURL(kDestination1))); + spdy::SpdySerializedFrame connect_dest1_resp( + spdy_util1.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame connect_dest3(spdy_util1.ConstructSpdyConnect( + nullptr, 0, 3, net::HttpProxyConnectJob::kH2QuicTunnelPriority, + net::HostPortPair::FromURL(kDestination3))); + spdy::SpdySerializedFrame connect_dest3_resp( + spdy_util1.ConstructSpdyGetReply(nullptr, 0, 3)); + + net::MockWrite spdy_writes[] = { + net::CreateMockWrite(connect_dest1, 0), + net::CreateMockWrite(connect_dest3, 2), + }; + + net::MockRead spdy_reads[] = { + net::CreateMockRead(connect_dest1_resp, 1, net::ASYNC), + net::CreateMockRead(connect_dest3_resp, 3, net::ASYNC), + net::MockRead(net::SYNCHRONOUS, 0, 4), + }; + + net::SequencedSocketData socket_data(spdy_reads, spdy_writes); + session_deps.socket_factory->AddSocketDataProvider(&socket_data); + net::SSLSocketDataProvider ssl_data(net::ASYNC, net::OK); + ssl_data.next_proto = net::kProtoHTTP2; + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_data); + + net::SpdyTestUtil spdy_util2; + spdy::SpdySerializedFrame connect_dest2(spdy_util2.ConstructSpdyConnect( + nullptr, 0, 1, net::HttpProxyConnectJob::kH2QuicTunnelPriority, + net::HostPortPair::FromURL(kDestination2))); + spdy::SpdySerializedFrame connect_dest2_resp( + spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1)); + + net::MockWrite spdy_writes2[] = { + net::CreateMockWrite(connect_dest2, 0), + }; + + net::MockRead spdy_reads2[] = { + net::CreateMockRead(connect_dest2_resp, 1, net::ASYNC), + net::MockRead(net::SYNCHRONOUS, 0, 2), + }; + + net::SequencedSocketData socket_data2(spdy_reads2, spdy_writes2); + session_deps.socket_factory->AddSocketDataProvider(&socket_data2); + net::SSLSocketDataProvider ssl_data2(net::ASYNC, net::OK); + ssl_data2.next_proto = net::kProtoHTTP2; + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_data2); + + // Connect to kDestination1 using kNetworkIsolationKey1. It should use a new + // H2 session. + net::CommonConnectJobParams common_connect_job_params = + http_network_session->CreateCommonConnectJobParams(); + ProxyResolvingClientSocket socket1( + http_network_session.get(), &common_connect_job_params, kDestination1, + kNetworkIsolationKey1, false /* use_tls */); + net::TestCompletionCallback callback1; + int result = socket1.Connect(callback1.callback()); + EXPECT_THAT(callback1.GetResult(result), net::test::IsOk()); + + // Connect to kDestination2 using kNetworkIsolationKey2. It should use a new + // H2 session. + ProxyResolvingClientSocket socket2( + http_network_session.get(), &common_connect_job_params, kDestination2, + kNetworkIsolationKey2, false /* use_tls */); + net::TestCompletionCallback callback2; + result = socket2.Connect(callback2.callback()); + EXPECT_THAT(callback2.GetResult(result), net::test::IsOk()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + + // Connect to kDestination3 using kNetworkIsolationKey1. It should reuse the + // first H2 session. + ProxyResolvingClientSocket socket3( + http_network_session.get(), &common_connect_job_params, kDestination3, + kNetworkIsolationKey1, false /* use_tls */); + net::TestCompletionCallback callback3; + result = socket3.Connect(callback3.callback()); + EXPECT_THAT(callback3.GetResult(result), net::test::IsOk()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); +} + // Tests that the global socket pool limit // (ClientSocketPoolManager::max_sockets_per_group) doesn't apply to this // type of sockets. @@ -112,7 +301,8 @@ TEST_P(ProxyResolvingClientSocketTest, SocketLimitNotApply) { std::vector<std::unique_ptr<ProxyResolvingClientSocket>> sockets; for (int i = 0; i < kNumSockets; ++i) { std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); @@ -153,7 +343,8 @@ TEST_P(ProxyResolvingClientSocketTest, ConnectError) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -203,7 +394,8 @@ TEST_P(ProxyResolvingClientSocketTest, ConnectToProxy) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -238,7 +430,8 @@ TEST_P(ProxyResolvingClientSocketTest, SocketDestroyedBeforeConnectComplete) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -305,7 +498,8 @@ TEST_P(ProxyResolvingClientSocketTest, ReadWriteErrors) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -369,7 +563,8 @@ TEST_P(ProxyResolvingClientSocketTest, ReportsBadProxies) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -405,7 +600,8 @@ TEST_P(ProxyResolvingClientSocketTest, ResetSocketAfterTunnelAuth) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_THAT(callback.GetResult(status), @@ -483,7 +679,8 @@ TEST_P(ProxyResolvingClientSocketTest, MultiroundAuth) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); @@ -540,7 +737,8 @@ TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); @@ -598,7 +796,8 @@ TEST_P(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) { mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); @@ -638,7 +837,8 @@ TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); @@ -666,7 +866,8 @@ TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); @@ -698,7 +899,8 @@ TEST_P(ProxyResolvingClientSocketTest, URLSanitized) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(url, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + url, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); @@ -742,7 +944,8 @@ TEST_P(ProxyResolvingClientSocketTest, ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(url, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + url, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; EXPECT_EQ(net::ERR_IO_PENDING, socket->Connect(callback.callback())); socket.reset(); @@ -776,7 +979,8 @@ TEST_P(ProxyResolvingClientSocketTest, NoSupportedProxies) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( context.get()); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); status = callback.GetResult(status); @@ -868,7 +1072,8 @@ TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) { ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( &context_with_proxy_); std::unique_ptr<ProxyResolvingClientSocket> socket = - proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); + proxy_resolving_socket_factory.CreateSocket( + kDestination, net::NetworkIsolationKey(), use_tls_); net::TestCompletionCallback callback; int status = socket->Connect(callback.callback()); EXPECT_EQ(net::ERR_IO_PENDING, status); diff --git a/chromium/services/network/proxy_resolving_socket_factory_mojo.cc b/chromium/services/network/proxy_resolving_socket_factory_mojo.cc index 2d673a7115d..059a4886374 100644 --- a/chromium/services/network/proxy_resolving_socket_factory_mojo.cc +++ b/chromium/services/network/proxy_resolving_socket_factory_mojo.cc @@ -25,13 +25,14 @@ ProxyResolvingSocketFactoryMojo::~ProxyResolvingSocketFactoryMojo() {} void ProxyResolvingSocketFactoryMojo::CreateProxyResolvingSocket( const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, mojom::ProxyResolvingSocketOptionsPtr options, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver, mojo::PendingRemote<mojom::SocketObserver> observer, CreateProxyResolvingSocketCallback callback) { - std::unique_ptr<net::StreamSocket> net_socket = - factory_impl_.CreateSocket(url, options && options->use_tls); + std::unique_ptr<net::StreamSocket> net_socket = factory_impl_.CreateSocket( + url, network_isolation_key, options && options->use_tls); if (options && options->fake_tls_handshake) { DCHECK(!options->use_tls); net_socket = std::make_unique<jingle_glue::FakeSSLClientSocket>( diff --git a/chromium/services/network/proxy_resolving_socket_factory_mojo.h b/chromium/services/network/proxy_resolving_socket_factory_mojo.h index b32c0824cb1..f7c8d5b3db5 100644 --- a/chromium/services/network/proxy_resolving_socket_factory_mojo.h +++ b/chromium/services/network/proxy_resolving_socket_factory_mojo.h @@ -19,6 +19,7 @@ #include "services/network/tls_socket_factory.h" namespace net { +class NetworkIsolationKey; class URLRequestContext; } // namespace net @@ -33,6 +34,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingSocketFactoryMojo // mojom::ProxyResolvingSocketFactory implementation. void CreateProxyResolvingSocket( const GURL& url, + const net::NetworkIsolationKey& network_isolation_key, mojom::ProxyResolvingSocketOptionsPtr options, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, mojo::PendingReceiver<mojom::ProxyResolvingSocket> receiver, diff --git a/chromium/services/network/proxy_resolving_socket_mojo_unittest.cc b/chromium/services/network/proxy_resolving_socket_mojo_unittest.cc index 1a2b199963a..f840843a062 100644 --- a/chromium/services/network/proxy_resolving_socket_mojo_unittest.cc +++ b/chromium/services/network/proxy_resolving_socket_mojo_unittest.cc @@ -18,6 +18,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe_utils.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/base/test_completion_callback.h" #include "net/dns/mock_host_resolver.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" @@ -122,7 +123,7 @@ class ProxyResolvingSocketTestBase { options->use_tls = use_tls_; options->fake_tls_handshake = fake_tls_handshake_; factory_remote_->CreateProxyResolvingSocket( - url, std::move(options), + url, net::NetworkIsolationKey(), std::move(options), net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS), std::move(receiver), std::move(socket_observer), base::BindLambdaForTesting( @@ -401,7 +402,7 @@ TEST_F(ProxyResolvingSocketMojoTest, SocketDestroyedBeforeConnectCompletes) { base::RunLoop run_loop; int net_error = net::OK; factory()->CreateProxyResolvingSocket( - kDestination, nullptr, + kDestination, net::NetworkIsolationKey(), nullptr, net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS), socket.InitWithNewPipeAndPassReceiver(), mojo::NullRemote() /* observer */, diff --git a/chromium/services/network/public/cpp/BUILD.gn b/chromium/services/network/public/cpp/BUILD.gn index 6d0de4d137f..8447695610e 100644 --- a/chromium/services/network/public/cpp/BUILD.gn +++ b/chromium/services/network/public/cpp/BUILD.gn @@ -13,6 +13,15 @@ buildflag_header("buildflags") { flags = [ "IS_CT_SUPPORTED=$is_ct_supported" ] } +jumbo_component("crash_keys") { + sources = [ + "crash_keys.cc", + "crash_keys.h", + ] + deps = [ "//base" ] + defines = [ "IS_NETWORK_CPP_CRASH_KEYS_IMPL" ] +} + jumbo_component("cpp") { output_name = "network_cpp" @@ -49,6 +58,8 @@ jumbo_component("cpp") { "cross_thread_pending_shared_url_loader_factory.h", "data_pipe_to_source_stream.cc", "data_pipe_to_source_stream.h", + "empty_url_loader_client.cc", + "empty_url_loader_client.h", "features.cc", "features.h", "header_util.cc", @@ -67,6 +78,8 @@ jumbo_component("cpp") { "network_quality_tracker.h", "network_switches.cc", "network_switches.h", + "origin_isolation_parser.cc", + "origin_isolation_parser.h", "parsed_headers.cc", "parsed_headers.h", "request_destination.cc", @@ -82,6 +95,9 @@ jumbo_component("cpp") { "simple_url_loader_stream_consumer.h", "source_stream_to_data_pipe.cc", "source_stream_to_data_pipe.h", + "spki_hash_set.cc", + "spki_hash_set.h", + "trust_token_operation_authorization.h", "weak_wrapper_shared_url_loader_factory.cc", "weak_wrapper_shared_url_loader_factory.h", "web_sandbox_flags.cc", @@ -151,6 +167,7 @@ component("cookies_mojom_support") { "site_for_cookies_mojom_traits.h", ] deps = [ + ":crash_keys", "//net", "//services/network/public/mojom:cookies_mojom_shared", ] @@ -202,10 +219,13 @@ jumbo_component("cpp_base") { "proxy_config_mojom_traits.h", "proxy_config_with_annotation_mojom_traits.cc", "proxy_config_with_annotation_mojom_traits.h", + "quic_transport_error_mojom_traits.cc", + "quic_transport_error_mojom_traits.h", "resource_request.cc", "resource_request.h", "resource_request_body.cc", "resource_request_body.h", + "trust_token_parameterization.h", "url_loader_completion_status.cc", "url_loader_completion_status.h", "url_request_mojom_traits.cc", @@ -225,6 +245,7 @@ jumbo_component("cpp_base") { public_deps = [ ":cookies_mojom_support", + ":crash_keys", ":ip_address_mojom_support", "//services/network/public/mojom:data_pipe_interfaces", "//services/network/public/mojom:mutable_network_traffic_annotation_interface", @@ -258,6 +279,7 @@ source_set("tests") { "content_security_policy/content_security_policy_unittest.cc", "content_security_policy/csp_context_unittest.cc", "content_security_policy/csp_source_list_unittest.cc", + "content_security_policy/csp_source_unittest.cc", "cookie_manager_mojom_traits_unittest.cc", "cors/cors_unittest.cc", "cors/origin_access_entry_unittest.cc", @@ -285,6 +307,7 @@ source_set("tests") { "network_mojom_traits_unittest.cc", "network_quality_tracker_unittest.cc", "optional_trust_token_params_unittest.cc", + "origin_isolation_parser_unittest.cc", "proxy_config_mojom_traits_unittest.cc", "simple_url_loader_unittest.cc", "site_for_cookies_mojom_traits_unittest.cc", diff --git a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.cc b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.cc index c7ff782ff6e..8c82aad1bd6 100644 --- a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.cc +++ b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.cc @@ -34,8 +34,12 @@ bool StructTraits<cert_verifier::mojom::CertVerifierConfigDataView, net::CertVerifier::Config* config) { mojo_base::BigBuffer crl_set_buffer; std::vector<scoped_refptr<net::X509Certificate>> additional_trust_anchors; + std::vector<scoped_refptr<net::X509Certificate>> + additional_untrusted_authorities; if (!data.ReadCrlSet(&crl_set_buffer) || - !data.ReadAdditionalTrustAnchors(&additional_trust_anchors)) + !data.ReadAdditionalTrustAnchors(&additional_trust_anchors) || + !data.ReadAdditionalUntrustedAuthorities( + &additional_untrusted_authorities)) return false; scoped_refptr<net::CRLSet> crl_set; @@ -54,6 +58,8 @@ bool StructTraits<cert_verifier::mojom::CertVerifierConfigDataView, config->disable_symantec_enforcement = data.disable_symantec_enforcement(); config->crl_set = std::move(crl_set); config->additional_trust_anchors = std::move(additional_trust_anchors); + config->additional_untrusted_authorities = + std::move(additional_untrusted_authorities); return true; } diff --git a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.h b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.h index 9615febd651..a5102ce9f4b 100644 --- a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.h +++ b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits.h @@ -71,6 +71,10 @@ struct StructTraits<cert_verifier::mojom::CertVerifierConfigDataView, additional_trust_anchors(const net::CertVerifier::Config& config) { return config.additional_trust_anchors; } + static const std::vector<scoped_refptr<net::X509Certificate>>& + additional_untrusted_authorities(const net::CertVerifier::Config& config) { + return config.additional_untrusted_authorities; + } static bool Read(cert_verifier::mojom::CertVerifierConfigDataView data, net::CertVerifier::Config* config); diff --git a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits_unittest.cc index 99c0a5490a0..3388e84eb7a 100644 --- a/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/cert_verifier/cert_verifier_mojom_traits_unittest.cc @@ -101,6 +101,10 @@ bool ConfigsEqual(const net::CertVerifier::Config& config1, config2.additional_trust_anchors)) return false; + if (!CertificateListsEqual(config1.additional_untrusted_authorities, + config2.additional_untrusted_authorities)) + return false; + return true; } } // namespace @@ -128,7 +132,7 @@ TEST(CertVerifierMojomTraitsTest, ConfigTrue) { ASSERT_TRUE(ConfigsEqual(config, out_config)); } -TEST(CertVerifierMojomTraitsTest, ConfigCRLAndAnchors) { +TEST(CertVerifierMojomTraitsTest, ConfigCRLAndAdditionalCerts) { std::string crl_set; { @@ -147,6 +151,9 @@ TEST(CertVerifierMojomTraitsTest, ConfigCRLAndAnchors) { config.additional_trust_anchors.push_back( net::ImportCertFromFile(certs_dir, "aia-root.pem")); + config.additional_untrusted_authorities.push_back( + net::ImportCertFromFile(certs_dir, "aia-intermediate.der")); + net::CertVerifier::Config out_config; ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CertVerifierConfig>( diff --git a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.cc b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.cc index d9f14efddb6..767d136b405 100644 --- a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.cc +++ b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "services/network/public/cpp/cert_verifier/mojo_cert_verifier.h" + +#include <memory> + #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "net/base/net_errors.h" @@ -62,9 +65,44 @@ class CertVerifierRequestImpl : public mojom::CertVerifierRequest, }; } // namespace +class MojoCertVerifier::MojoReconnector + : public mojom::URLLoaderFactoryConnector { + public: + MojoReconnector( + mojo::PendingReceiver<mojom::URLLoaderFactoryConnector> receiver, + base::RepeatingCallback<void( + mojo::PendingReceiver<network::mojom::URLLoaderFactory>)> reconnector) + : receiver_(this, std::move(receiver)), + reconnector_(std::move(reconnector)) {} + + // mojom::URLLoaderFactoryConnector implementation: + void CreateURLLoaderFactory( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> + url_loader_factory) override { + reconnector_.Run(std::move(url_loader_factory)); + } + + private: + mojo::Receiver<mojom::URLLoaderFactoryConnector> receiver_; + base::RepeatingCallback<void( + mojo::PendingReceiver<network::mojom::URLLoaderFactory>)> + reconnector_; +}; + MojoCertVerifier::MojoCertVerifier( - mojo::PendingRemote<mojom::CertVerifierService> mojo_cert_verifier) - : mojo_cert_verifier_(std::move(mojo_cert_verifier)) {} + mojo::PendingRemote<mojom::CertVerifierService> mojo_cert_verifier, + mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory, + base::RepeatingCallback<void( + mojo::PendingReceiver<network::mojom::URLLoaderFactory>)> reconnector) + : mojo_cert_verifier_(std::move(mojo_cert_verifier)) { + mojo::PendingRemote<mojom::URLLoaderFactoryConnector> reconnector_remote; + + reconnector_ = std::make_unique<MojoReconnector>( + reconnector_remote.InitWithNewPipeAndPassReceiver(), + std::move(reconnector)); + mojo_cert_verifier_->EnableNetworkAccess(std::move(url_loader_factory), + std::move(reconnector_remote)); +} MojoCertVerifier::~MojoCertVerifier() = default; @@ -90,4 +128,8 @@ int MojoCertVerifier::Verify( void MojoCertVerifier::SetConfig(const net::CertVerifier::Config& config) { mojo_cert_verifier_->SetConfig(config); } + +void MojoCertVerifier::FlushForTesting() { + mojo_cert_verifier_.FlushForTesting(); +} } // namespace cert_verifier diff --git a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.h b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.h index d61b9d04063..b51f2f6558a 100644 --- a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.h +++ b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier.h @@ -5,6 +5,8 @@ #ifndef SERVICES_NETWORK_PUBLIC_CPP_CERT_VERIFIER_MOJO_CERT_VERIFIER_H_ #define SERVICES_NETWORK_PUBLIC_CPP_CERT_VERIFIER_MOJO_CERT_VERIFIER_H_ +#include "base/callback_forward.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/completion_once_callback.h" #include "net/base/net_export.h" @@ -12,6 +14,7 @@ #include "net/cert/cert_verify_result.h" #include "net/log/net_log_with_source.h" #include "services/network/public/mojom/cert_verifier_service.mojom.h" +#include "services/network/public/mojom/network_context.mojom.h" namespace cert_verifier { @@ -19,8 +22,16 @@ namespace cert_verifier { // verify certificates. class MojoCertVerifier : public net::CertVerifier { public: - explicit MojoCertVerifier( - mojo::PendingRemote<mojom::CertVerifierService> mojo_cert_verifier); + using ReconnectURLLoaderFactory = base::RepeatingCallback<void( + mojo::PendingReceiver<network::mojom::URLLoaderFactory>)>; + + // The remote CertNetFetcher will use |url_loader_factory| for fetches. If + // |url_loader_factory| disconnects it will use |reconnector| to try to + // connect a new URLLoaderFactory. + MojoCertVerifier( + mojo::PendingRemote<mojom::CertVerifierService> mojo_cert_verifier, + mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory, + ReconnectURLLoaderFactory reconnector); ~MojoCertVerifier() override; // net::CertVerifier implementation: @@ -31,8 +42,14 @@ class MojoCertVerifier : public net::CertVerifier { const net::NetLogWithSource& net_log) override; void SetConfig(const net::CertVerifier::Config& config) override; + // Flushes the underlying Mojo pipe. + void FlushForTesting(); + private: + class MojoReconnector; + mojo::Remote<mojom::CertVerifierService> mojo_cert_verifier_; + std::unique_ptr<MojoReconnector> reconnector_; }; } // namespace cert_verifier diff --git a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier_unittest.cc b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier_unittest.cc index a6b00cb61a6..d43349ff7bb 100644 --- a/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier_unittest.cc +++ b/chromium/services/network/public/cpp/cert_verifier/mojo_cert_verifier_unittest.cc @@ -7,9 +7,11 @@ #include <map> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/run_loop.h" #include "base/test/bind_test_util.h" #include "base/test/task_environment.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "net/base/test_completion_callback.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" @@ -36,12 +38,27 @@ net::CertVerifier::RequestParams GetDummyParams() { /*sct_list=*/std::string()); } +mojo::PendingRemote<network::mojom::URLLoaderFactory> +CreateUnconnectedURLLoaderFactory() { + mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory; + // Bind the factory, but don't bother connecting it. + ignore_result(url_loader_factory.InitWithNewPipeAndPassReceiver()); + return url_loader_factory; +} + class MojoCertVerifierTest : public PlatformTest { public: MojoCertVerifierTest() : dummy_cv_service_(this), cv_service_receiver_(&dummy_cv_service_), - mojo_cert_verifier_(cv_service_receiver_.BindNewPipeAndPassRemote()) {} + mojo_cert_verifier_( + cv_service_receiver_.BindNewPipeAndPassRemote(), + CreateUnconnectedURLLoaderFactory(), + base::BindRepeating(&MojoCertVerifierTest::ReconnectCb, + base::Unretained(this))) { + // Any Mojo requests in the MojoCertVerifier constructor should run here. + mojo_cert_verifier_.FlushForTesting(); + } class DummyCVService final : public mojom::CertVerifierService { public: @@ -57,12 +74,24 @@ class MojoCertVerifierTest : public PlatformTest { config_ = config; } + void EnableNetworkAccess( + mojo::PendingRemote<network::mojom::URLLoaderFactory> + url_loader_factory, + mojo::PendingRemote<mojom::URLLoaderFactoryConnector> reconnector) + override { + reconnector_.Bind(std::move(reconnector)); + } + const net::CertVerifier::Config* config() const { return &config_; } + mojom::URLLoaderFactoryConnector* reconnector() { + return reconnector_.get(); + } private: MojoCertVerifierTest* test_; net::CertVerifier::Config config_; + mojo::Remote<mojom::URLLoaderFactoryConnector> reconnector_; }; base::test::TaskEnvironment* task_environment() { return &task_environment_; } @@ -97,6 +126,16 @@ class MojoCertVerifierTest : public PlatformTest { void SimulateCVServiceDisconnect() { cv_service_receiver_.reset(); } + void ReconnectCb(mojo::PendingReceiver<network::mojom::URLLoaderFactory> + mojo_cert_verifier) { + if (reconnect_cb_) + reconnect_cb_.Run(std::move(mojo_cert_verifier)); + } + + void SetReconnectCb(MojoCertVerifier::ReconnectURLLoaderFactory cb) { + reconnect_cb_ = std::move(cb); + } + private: std::map<net::CertVerifier::RequestParams, mojo::Remote<mojom::CertVerifierRequest>> @@ -109,6 +148,8 @@ class MojoCertVerifierTest : public PlatformTest { MojoCertVerifier mojo_cert_verifier_; + MojoCertVerifier::ReconnectURLLoaderFactory reconnect_cb_; + net::NetLogWithSource empty_net_log_with_source_; }; } // namespace @@ -244,4 +285,22 @@ TEST_F(MojoCertVerifierTest, SendsConfig) { ASSERT_TRUE(dummy_cv_service()->config()->disable_symantec_enforcement); } +TEST_F(MojoCertVerifierTest, ReconnectorCallsCb) { + base::RunLoop run_loop; + SetReconnectCb(base::BindLambdaForTesting( + [&](mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { + run_loop.Quit(); + })); + + mojo::PendingRemote<network::mojom::URLLoaderFactory> + dummy_url_loader_factory_remote; + // Simulate a remote CertVerifierService trying to reconnect after + // disconnection. This should call the callback given to the MojoCertVerifier + // on construction. + dummy_cv_service()->reconnector()->CreateURLLoaderFactory( + dummy_url_loader_factory_remote.InitWithNewPipeAndPassReceiver()); + + run_loop.Run(); +} + } // namespace cert_verifier diff --git a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc index bd427654cbf..6ae235de565 100644 --- a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc +++ b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc @@ -35,6 +35,13 @@ static CSPDirectiveName CSPFallback(CSPDirectiveName directive) { case CSPDirectiveName::FormAction: case CSPDirectiveName::NavigateTo: case CSPDirectiveName::FrameAncestors: + case CSPDirectiveName::ImgSrc: + case CSPDirectiveName::MediaSrc: + case CSPDirectiveName::ObjectSrc: + case CSPDirectiveName::ScriptSrc: + case CSPDirectiveName::StyleSrc: + case CSPDirectiveName::WorkerSrc: + case CSPDirectiveName::ConnectSrc: return CSPDirectiveName::Unknown; case CSPDirectiveName::FrameSrc: @@ -78,6 +85,13 @@ const char* ErrorMessage(CSPDirectiveName directive) { case CSPDirectiveName::ChildSrc: case CSPDirectiveName::DefaultSrc: case CSPDirectiveName::Unknown: + case CSPDirectiveName::ImgSrc: + case CSPDirectiveName::MediaSrc: + case CSPDirectiveName::ObjectSrc: + case CSPDirectiveName::ScriptSrc: + case CSPDirectiveName::StyleSrc: + case CSPDirectiveName::WorkerSrc: + case CSPDirectiveName::ConnectSrc: NOTREACHED(); return nullptr; }; @@ -614,6 +628,20 @@ CSPDirectiveName ToCSPDirectiveName(const std::string& name) { return CSPDirectiveName::NavigateTo; if (name == "frame-ancestors") return CSPDirectiveName::FrameAncestors; + if (name == "img-src") + return CSPDirectiveName::ImgSrc; + if (name == "media-src") + return CSPDirectiveName::MediaSrc; + if (name == "object-src") + return CSPDirectiveName::ObjectSrc; + if (name == "script-src") + return CSPDirectiveName::ScriptSrc; + if (name == "style-src") + return CSPDirectiveName::StyleSrc; + if (name == "worker-src") + return CSPDirectiveName::WorkerSrc; + if (name == "connect-src") + return CSPDirectiveName::ConnectSrc; return CSPDirectiveName::Unknown; } @@ -631,6 +659,20 @@ std::string ToString(CSPDirectiveName name) { return "navigate-to"; case CSPDirectiveName::FrameAncestors: return "frame-ancestors"; + case CSPDirectiveName::ImgSrc: + return "img-src"; + case CSPDirectiveName::MediaSrc: + return "media-src"; + case CSPDirectiveName::ObjectSrc: + return "object-src"; + case CSPDirectiveName::ScriptSrc: + return "script-src"; + case CSPDirectiveName::StyleSrc: + return "style-src"; + case CSPDirectiveName::WorkerSrc: + return "worker-src"; + case CSPDirectiveName::ConnectSrc: + return "connect-src"; case CSPDirectiveName::Unknown: return ""; } diff --git a/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc b/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc index 693f7952bcc..903aa5a2a25 100644 --- a/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc +++ b/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/network/content_security_policy/csp_source.h" -#include "services/network/content_security_policy/csp_context.h" +#include "services/network/public/cpp/content_security_policy/csp_source.h" +#include "services/network/public/cpp/content_security_policy/csp_context.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/origin.h" -namespace content { +namespace network { namespace { diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc index d973efffce1..d4897d169e5 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc @@ -81,6 +81,48 @@ bool EnumTraits<network::mojom::CookieSameSite, net::CookieSameSite>::FromMojom( return false; } +network::mojom::CookieEffectiveSameSite EnumTraits< + network::mojom::CookieEffectiveSameSite, + net::CookieEffectiveSameSite>::ToMojom(net::CookieEffectiveSameSite input) { + switch (input) { + case net::CookieEffectiveSameSite::NO_RESTRICTION: + return network::mojom::CookieEffectiveSameSite::kNoRestriction; + case net::CookieEffectiveSameSite::LAX_MODE: + return network::mojom::CookieEffectiveSameSite::kLaxMode; + case net::CookieEffectiveSameSite::STRICT_MODE: + return network::mojom::CookieEffectiveSameSite::kStrictMode; + case net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE: + return network::mojom::CookieEffectiveSameSite::kLaxModeAllowUnsafe; + default: + break; + } + NOTREACHED(); + return static_cast<network::mojom::CookieEffectiveSameSite>(input); +} + +bool EnumTraits<network::mojom::CookieEffectiveSameSite, + net::CookieEffectiveSameSite>:: + FromMojom(network::mojom::CookieEffectiveSameSite input, + net::CookieEffectiveSameSite* output) { + switch (input) { + case network::mojom::CookieEffectiveSameSite::kNoRestriction: + *output = net::CookieEffectiveSameSite::NO_RESTRICTION; + return true; + case network::mojom::CookieEffectiveSameSite::kLaxMode: + *output = net::CookieEffectiveSameSite::LAX_MODE; + return true; + case network::mojom::CookieEffectiveSameSite::kStrictMode: + *output = net::CookieEffectiveSameSite::STRICT_MODE; + return true; + case network::mojom::CookieEffectiveSameSite::kLaxModeAllowUnsafe: + *output = net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE; + return true; + default: + break; + } + return false; +} + network::mojom::CookieSourceScheme EnumTraits<network::mojom::CookieSourceScheme, net::CookieSourceScheme>::ToMojom(net::CookieSourceScheme input) { @@ -351,10 +393,10 @@ bool StructTraits< } bool StructTraits<network::mojom::CookieInclusionStatusDataView, - net::CanonicalCookie::CookieInclusionStatus>:: + net::CookieInclusionStatus>:: Read(network::mojom::CookieInclusionStatusDataView status, - net::CanonicalCookie::CookieInclusionStatus* out) { - *out = net::CanonicalCookie::CookieInclusionStatus(); + net::CookieInclusionStatus* out) { + *out = net::CookieInclusionStatus(); out->set_exclusion_reasons(status.exclusion_reasons()); out->set_warning_reasons(status.warning_reasons()); @@ -366,7 +408,7 @@ bool StructTraits< net::CookieWithStatus>::Read(network::mojom::CookieWithStatusDataView c, net::CookieWithStatus* out) { net::CanonicalCookie cookie; - net::CanonicalCookie::CookieInclusionStatus status; + net::CookieInclusionStatus status; if (!c.ReadCookie(&cookie)) return false; if (!c.ReadStatus(&status)) @@ -383,7 +425,7 @@ bool StructTraits<network::mojom::CookieAndLineWithStatusDataView, net::CookieAndLineWithStatus* out) { base::Optional<net::CanonicalCookie> cookie; std::string cookie_string; - net::CanonicalCookie::CookieInclusionStatus status; + net::CookieInclusionStatus status; if (!c.ReadCookie(&cookie)) return false; if (!c.ReadCookieString(&cookie_string)) @@ -397,6 +439,39 @@ bool StructTraits<network::mojom::CookieAndLineWithStatusDataView, } bool StructTraits< + network::mojom::CookieAccessResultDataView, + net::CookieAccessResult>::Read(network::mojom::CookieAccessResultDataView c, + net::CookieAccessResult* out) { + net::CookieEffectiveSameSite effective_same_site; + net::CookieInclusionStatus status; + + if (!c.ReadEffectiveSameSite(&effective_same_site)) + return false; + if (!c.ReadStatus(&status)) + return false; + + *out = {effective_same_site, status}; + + return true; +} + +bool StructTraits<network::mojom::CookieWithAccessResultDataView, + net::CookieWithAccessResult>:: + Read(network::mojom::CookieWithAccessResultDataView c, + net::CookieWithAccessResult* out) { + net::CanonicalCookie cookie; + net::CookieAccessResult access_result; + if (!c.ReadCookie(&cookie)) + return false; + if (!c.ReadAccessResult(&access_result)) + return false; + + *out = {cookie, access_result}; + + return true; +} + +bool StructTraits< network::mojom::CookieChangeInfoDataView, net::CookieChangeInfo>::Read(network::mojom::CookieChangeInfoDataView info, net::CookieChangeInfo* out) { diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h index b9b4eec5df5..8b5243cafcf 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h @@ -8,8 +8,10 @@ #include "ipc/ipc_message_utils.h" #include "mojo/public/cpp/bindings/enum_traits.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_change_dispatcher.h" #include "net/cookies/cookie_constants.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_options.h" #include "services/network/public/mojom/cookie_manager.mojom.h" @@ -30,6 +32,15 @@ struct EnumTraits<network::mojom::CookieSameSite, net::CookieSameSite> { }; template <> +struct EnumTraits<network::mojom::CookieEffectiveSameSite, + net::CookieEffectiveSameSite> { + static network::mojom::CookieEffectiveSameSite ToMojom( + net::CookieEffectiveSameSite input); + static bool FromMojom(network::mojom::CookieEffectiveSameSite input, + net::CookieEffectiveSameSite* output); +}; + +template <> struct EnumTraits<network::mojom::CookieAccessSemantics, net::CookieAccessSemantics> { static network::mojom::CookieAccessSemantics ToMojom( @@ -145,17 +156,15 @@ struct StructTraits<network::mojom::CanonicalCookieDataView, template <> struct StructTraits<network::mojom::CookieInclusionStatusDataView, - net::CanonicalCookie::CookieInclusionStatus> { - static uint32_t exclusion_reasons( - const net::CanonicalCookie::CookieInclusionStatus& s) { + net::CookieInclusionStatus> { + static uint32_t exclusion_reasons(const net::CookieInclusionStatus& s) { return s.exclusion_reasons(); } - static uint32_t warning_reasons( - const net::CanonicalCookie::CookieInclusionStatus& s) { + static uint32_t warning_reasons(const net::CookieInclusionStatus& s) { return s.warning_reasons(); } static bool Read(network::mojom::CookieInclusionStatusDataView status, - net::CanonicalCookie::CookieInclusionStatus* out); + net::CookieInclusionStatus* out); }; template <> @@ -164,7 +173,7 @@ struct StructTraits<network::mojom::CookieWithStatusDataView, static const net::CanonicalCookie& cookie(const net::CookieWithStatus& c) { return c.cookie; } - static const net::CanonicalCookie::CookieInclusionStatus& status( + static const net::CookieInclusionStatus& status( const net::CookieWithStatus& c) { return c.status; } @@ -183,7 +192,7 @@ struct StructTraits<network::mojom::CookieAndLineWithStatusDataView, const net::CookieAndLineWithStatus& c) { return c.cookie_string; } - static const net::CanonicalCookie::CookieInclusionStatus& status( + static const net::CookieInclusionStatus& status( const net::CookieAndLineWithStatus& c) { return c.status; } @@ -192,6 +201,36 @@ struct StructTraits<network::mojom::CookieAndLineWithStatusDataView, }; template <> +struct StructTraits<network::mojom::CookieAccessResultDataView, + net::CookieAccessResult> { + static const net::CookieEffectiveSameSite& effective_same_site( + const net::CookieAccessResult& c) { + return c.effective_same_site; + } + static const net::CookieInclusionStatus& status( + const net::CookieAccessResult& c) { + return c.status; + } + static bool Read(network::mojom::CookieAccessResultDataView access_result, + net::CookieAccessResult* out); +}; + +template <> +struct StructTraits<network::mojom::CookieWithAccessResultDataView, + net::CookieWithAccessResult> { + static const net::CanonicalCookie& cookie( + const net::CookieWithAccessResult& c) { + return c.cookie; + } + static const net::CookieAccessResult& access_result( + const net::CookieWithAccessResult& c) { + return c.access_result; + } + static bool Read(network::mojom::CookieWithAccessResultDataView cookie, + net::CookieWithAccessResult* out); +}; + +template <> struct StructTraits<network::mojom::CookieChangeInfoDataView, net::CookieChangeInfo> { static const net::CanonicalCookie& cookie(const net::CookieChangeInfo& c) { diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc index 6bdb434d7a7..d3be1aee9de 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc @@ -59,34 +59,32 @@ TEST(CookieManagerTraitsTest, Roundtrips_CanonicalCookie) { TEST(CookieManagerTraitsTest, Roundtrips_CookieInclusionStatus) { // This status + warning combo doesn't really make sense. It's just an // arbitrary selection of values to test the serialization/deserialization. - net::CanonicalCookie::CookieInclusionStatus original = - net::CanonicalCookie::CookieInclusionStatus::MakeFromReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX, - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX, - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY}, - {net::CanonicalCookie::CookieInclusionStatus:: + net::CookieInclusionStatus original = + net::CookieInclusionStatus::MakeFromReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX, + net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX, + net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY}, + {net::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT, - net::CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_NONE_INSECURE, - net::CanonicalCookie::CookieInclusionStatus:: + net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE, + net::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE}); - net::CanonicalCookie::CookieInclusionStatus copied; + net::CookieInclusionStatus copied; EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieInclusionStatus>( &original, &copied)); EXPECT_TRUE(copied.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX, - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX, - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX, + net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX, + net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); EXPECT_TRUE(copied.HasExactlyWarningReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT, - net::CanonicalCookie::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE, - net::CanonicalCookie::CookieInclusionStatus:: + {net::CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT, + net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE, + net::CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE})); - net::CanonicalCookie::CookieInclusionStatus invalid; + net::CookieInclusionStatus invalid; invalid.set_exclusion_reasons(~0u); EXPECT_FALSE( @@ -100,8 +98,8 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieWithStatus) { /* secure = */ true, /* http_only = */ false, net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW); - net::CookieWithStatus original = { - original_cookie, net::CanonicalCookie::CookieInclusionStatus()}; + net::CookieWithStatus original = {original_cookie, + net::CookieInclusionStatus()}; net::CookieWithStatus copied; diff --git a/chromium/services/network/public/cpp/cors/cors.cc b/chromium/services/network/public/cpp/cors/cors.cc index 3c95e68e7ff..6e12d8f2970 100644 --- a/chromium/services/network/public/cpp/cors/cors.cc +++ b/chromium/services/network/public/cpp/cors/cors.cc @@ -616,6 +616,7 @@ bool CalculateCredentialsFlag(mojom::CredentialsMode credentials_mode, // is true, and unset otherwise. switch (credentials_mode) { case network::mojom::CredentialsMode::kOmit: + case network::mojom::CredentialsMode::kOmitBug_775438_Workaround: return false; case network::mojom::CredentialsMode::kSameOrigin: return response_tainting == network::mojom::FetchResponseType::kBasic; diff --git a/chromium/services/network/public/cpp/cors/preflight_cache.cc b/chromium/services/network/public/cpp/cors/preflight_cache.cc index 3e89dfb3da6..0049fa1bc3d 100644 --- a/chromium/services/network/public/cpp/cors/preflight_cache.cc +++ b/chromium/services/network/public/cpp/cors/preflight_cache.cc @@ -53,10 +53,6 @@ void PreflightCache::AppendEntry( if (url_spec.length() >= kMaxKeyLength) return; - DCHECK( - !network_isolation_key.GetFrameOrigin().has_value() || - (origin.opaque() && network_isolation_key.GetFrameOrigin()->opaque()) || - network_isolation_key.GetFrameOrigin()->IsSameOriginWith(origin)); auto key = std::make_tuple(origin, url_spec, network_isolation_key); const auto existing_entry = cache_.find(key); if (existing_entry == cache_.end()) { @@ -80,10 +76,6 @@ bool PreflightCache::CheckIfRequestCanSkipPreflight( const net::HttpRequestHeaders& request_headers, bool is_revalidating) { // Check if the entry exists in the cache. - DCHECK( - !network_isolation_key.GetFrameOrigin().has_value() || - (origin.opaque() && network_isolation_key.GetFrameOrigin()->opaque()) || - network_isolation_key.GetFrameOrigin()->IsSameOriginWith(origin)); auto key = std::make_tuple(origin, url.spec(), network_isolation_key); auto cache_entry = cache_.find(key); if (cache_entry == cache_.end()) { diff --git a/chromium/services/network/public/cpp/crash_keys.cc b/chromium/services/network/public/cpp/crash_keys.cc new file mode 100644 index 00000000000..45a4120bf81 --- /dev/null +++ b/chromium/services/network/public/cpp/crash_keys.cc @@ -0,0 +1,31 @@ +// Copyright 2020 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 "services/network/public/cpp/crash_keys.h" + +#include <utility> + +#include "base/debug/crash_logging.h" + +namespace network { +namespace debug { + +namespace { +base::debug::CrashKeyString* GetCrashKey() { + static auto* crash_key = base::debug::AllocateCrashKeyString( + "network_deserialization", base::debug::CrashKeySize::Size32); + return crash_key; +} +} // namespace + +void SetDeserializationCrashKeyString(base::StringPiece str) { + base::debug::SetCrashKeyString(GetCrashKey(), std::move(str)); +} + +void ClearDeserializationCrashKeyString() { + base::debug::ClearCrashKeyString(GetCrashKey()); +} + +} // namespace debug +} // namespace network diff --git a/chromium/services/network/public/cpp/crash_keys.h b/chromium/services/network/public/cpp/crash_keys.h new file mode 100644 index 00000000000..16e6932bbd6 --- /dev/null +++ b/chromium/services/network/public/cpp/crash_keys.h @@ -0,0 +1,23 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_CRASH_KEYS_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_CRASH_KEYS_H_ + +#include "base/component_export.h" +#include "base/strings/string_piece_forward.h" + +namespace network { +namespace debug { + +COMPONENT_EXPORT(NETWORK_CPP_CRASH_KEYS) +void SetDeserializationCrashKeyString(base::StringPiece str); + +COMPONENT_EXPORT(NETWORK_CPP_CRASH_KEYS) +void ClearDeserializationCrashKeyString(); + +} // namespace debug +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_CRASH_KEYS_H_ diff --git a/chromium/services/network/public/cpp/data_element.cc b/chromium/services/network/public/cpp/data_element.cc index a75bb59b4e0..9a959842e9d 100644 --- a/chromium/services/network/public/cpp/data_element.cc +++ b/chromium/services/network/public/cpp/data_element.cc @@ -20,7 +20,7 @@ const uint64_t DataElement::kUnknownSize; DataElement::DataElement() : type_(mojom::DataElementType::kUnknown), - bytes_(NULL), + bytes_(nullptr), offset_(0), length_(std::numeric_limits<uint64_t>::max()) {} diff --git a/chromium/services/network/public/cpp/data_element.h b/chromium/services/network/public/cpp/data_element.h index c3bd96eb10d..26d0f4fc4fe 100644 --- a/chromium/services/network/public/cpp/data_element.h +++ b/chromium/services/network/public/cpp/data_element.h @@ -14,11 +14,11 @@ #include <string> #include <vector> +#include "base/check_op.h" #include "base/component_export.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/time/time.h" #include "mojo/public/cpp/bindings/enum_traits.h" #include "mojo/public/cpp/bindings/pending_remote.h" diff --git a/chromium/services/network/empty_url_loader_client.cc b/chromium/services/network/public/cpp/empty_url_loader_client.cc index e19b1ba3723..6d869d05675 100644 --- a/chromium/services/network/empty_url_loader_client.cc +++ b/chromium/services/network/public/cpp/empty_url_loader_client.cc @@ -2,38 +2,56 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/network/empty_url_loader_client.h" +#include "services/network/public/cpp/empty_url_loader_client.h" #include <utility> #include "base/bind.h" +#include "base/callback.h" #include "base/threading/sequenced_task_runner_handle.h" namespace network { // static -void EmptyURLLoaderClient::DrainURLRequest( +void EmptyURLLoaderClientWrapper::DrainURLRequest( mojo::PendingReceiver<mojom::URLLoaderClient> client_receiver, mojo::PendingRemote<mojom::URLLoader> url_loader) { - // Raw |new| is okay, because the newly constructed EmptyURLLoaderClient will - // delete itself after consuming all the data/callbacks. - new EmptyURLLoaderClient(std::move(client_receiver), std::move(url_loader)); + // Raw |new| is okay, because the object will delete itself. + new EmptyURLLoaderClientWrapper(std::move(client_receiver), + std::move(url_loader)); } -EmptyURLLoaderClient::EmptyURLLoaderClient( +EmptyURLLoaderClientWrapper::EmptyURLLoaderClientWrapper( mojo::PendingReceiver<mojom::URLLoaderClient> receiver, mojo::PendingRemote<mojom::URLLoader> url_loader) - : receiver_(this, std::move(receiver)), url_loader_(std::move(url_loader)) { + : receiver_(&client_, std::move(receiver)), + url_loader_(std::move(url_loader)) { + client_.Drain(base::BindOnce(&EmptyURLLoaderClientWrapper::DeleteSelf, + base::Unretained(this))); receiver_.set_disconnect_handler(base::BindOnce( - &EmptyURLLoaderClient::DeleteSelf, base::Unretained(this))); + &EmptyURLLoaderClientWrapper::DeleteSelf, base::Unretained(this))); } -EmptyURLLoaderClient::~EmptyURLLoaderClient() {} +EmptyURLLoaderClientWrapper::~EmptyURLLoaderClientWrapper() = default; -void EmptyURLLoaderClient::DeleteSelf() { +void EmptyURLLoaderClientWrapper::DeleteSelf() { delete this; } +EmptyURLLoaderClient::EmptyURLLoaderClient() = default; +EmptyURLLoaderClient::~EmptyURLLoaderClient() = default; + +void EmptyURLLoaderClient::Drain(base::OnceClosure callback) { + DCHECK(!callback_); + callback_ = std::move(callback); + MaybeDone(); +} + +void EmptyURLLoaderClient::MaybeDone() { + if (done_ && callback_) + std::move(callback_).Run(); +} + void EmptyURLLoaderClient::OnReceiveResponse( const mojom::URLResponseHeadPtr head) {} @@ -59,7 +77,8 @@ void EmptyURLLoaderClient::OnStartLoadingResponseBody( } void EmptyURLLoaderClient::OnComplete(const URLLoaderCompletionStatus& status) { - DeleteSelf(); + done_ = true; + MaybeDone(); } void EmptyURLLoaderClient::OnDataAvailable(const void* data, size_t num_bytes) { diff --git a/chromium/services/network/empty_url_loader_client.h b/chromium/services/network/public/cpp/empty_url_loader_client.h index e9320b69bed..bcff77f086d 100644 --- a/chromium/services/network/empty_url_loader_client.h +++ b/chromium/services/network/public/cpp/empty_url_loader_client.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_NETWORK_EMPTY_URL_LOADER_CLIENT_H_ -#define SERVICES_NETWORK_EMPTY_URL_LOADER_CLIENT_H_ +#ifndef SERVICES_NETWORK_PUBLIC_CPP_EMPTY_URL_LOADER_CLIENT_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_EMPTY_URL_LOADER_CLIENT_H_ #include <memory> +#include "base/callback_forward.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" @@ -18,24 +19,18 @@ namespace network { // Helper for draining/discarding data and callbacks that go to URLLoaderClient. -class EmptyURLLoaderClient : public mojom::URLLoaderClient, - public mojo::DataPipeDrainer::Client { +class COMPONENT_EXPORT(NETWORK_CPP) EmptyURLLoaderClient + : public mojom::URLLoaderClient, + public mojo::DataPipeDrainer::Client { public: - // Binds |client_receiver| to a newly constructed EmptyURLLoaderClient which - // will drain/discard all callbacks/data. Takes ownership of |url_loader| and - // discards it (together with EmptyURLLoaderClient) when the URL request has - // been completed. - static void DrainURLRequest( - mojo::PendingReceiver<mojom::URLLoaderClient> client_receiver, - mojo::PendingRemote<mojom::URLLoader> url_loader); + EmptyURLLoaderClient(); + ~EmptyURLLoaderClient() override; - private: - EmptyURLLoaderClient( - mojo::PendingReceiver<mojom::URLLoaderClient> client_receiver, - mojo::PendingRemote<mojom::URLLoader> url_loader); + // Calls |callback| when the request is done. + void Drain(base::OnceClosure callback); - ~EmptyURLLoaderClient() override; - void DeleteSelf(); + private: + void MaybeDone(); // mojom::URLLoaderClient overrides: void OnReceiveResponse(mojom::URLResponseHeadPtr head) override; @@ -54,15 +49,39 @@ class EmptyURLLoaderClient : public mojom::URLLoaderClient, void OnDataAvailable(const void* data, size_t num_bytes) override; void OnDataComplete() override; - mojo::Receiver<mojom::URLLoaderClient> receiver_; - std::unique_ptr<mojo::DataPipeDrainer> response_body_drainer_; - mojo::Remote<mojom::URLLoader> url_loader_; + bool done_ = false; + base::OnceClosure callback_; DISALLOW_COPY_AND_ASSIGN(EmptyURLLoaderClient); }; +// Self-owned helper class for using EmptyURLLoaderClient. +class COMPONENT_EXPORT(NETWORK_CPP) EmptyURLLoaderClientWrapper { + public: + // Binds |client_receiver| to a newly constructed EmptyURLLoaderClient which + // will drain/discard all callbacks/data. Takes ownership of |url_loader| and + // discards it (together with EmptyURLLoaderClient) when the URL request has + // been completed. + static void DrainURLRequest( + mojo::PendingReceiver<mojom::URLLoaderClient> client_receiver, + mojo::PendingRemote<mojom::URLLoader> url_loader); + + ~EmptyURLLoaderClientWrapper(); + + private: + EmptyURLLoaderClientWrapper( + mojo::PendingReceiver<mojom::URLLoaderClient> receiver, + mojo::PendingRemote<mojom::URLLoader> url_loader); + + void DeleteSelf(); + + EmptyURLLoaderClient client_; + mojo::Receiver<mojom::URLLoaderClient> receiver_; + mojo::Remote<mojom::URLLoader> url_loader_; +}; + } // namespace network -#endif // SERVICES_NETWORK_EMPTY_URL_LOADER_CLIENT_H_ +#endif // SERVICES_NETWORK_PUBLIC_CPP_EMPTY_URL_LOADER_CLIENT_H_ diff --git a/chromium/services/network/public/cpp/features.cc b/chromium/services/network/public/cpp/features.cc index 970b405187b..7ea2ac326c8 100644 --- a/chromium/services/network/public/cpp/features.cc +++ b/chromium/services/network/public/cpp/features.cc @@ -9,11 +9,6 @@ namespace network { namespace features { -// When kCapReferrerToOriginOnCrossOrigin is enabled, HTTP referrers on cross- -// origin requests are restricted to contain at most the source origin. -const base::Feature kCapReferrerToOriginOnCrossOrigin{ - "CapReferrerToOriginOnCrossOrigin", base::FEATURE_DISABLED_BY_DEFAULT}; - // Enables Expect CT reporting, which sends reports for opted-in sites // that don't serve sufficient Certificate Transparency information. const base::Feature kExpectCTReporting{"ExpectCTReporting", @@ -106,6 +101,12 @@ const base::Feature kCrossOriginOpenerPolicy { const base::Feature kCrossOriginOpenerPolicyReporting{ "CrossOriginOpenerPolicyReporting", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables Cross-Origin Opener Policy (COOP) access reporting. +// https://github.com/camillelamy/explainers/blob/master/coop_reporting.md#report-blocked-accesses-to-other-windows +const base::Feature kCrossOriginOpenerPolicyAccessReporting{ + "CrossOriginOpenerPolicyAccessReporting", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables Cross-Origin Embedder Policy (COEP). // https://github.com/mikewest/corpp // Currently this feature is enabled for all platforms except WebView. @@ -156,12 +157,6 @@ const base::FeatureParam<std::string> const base::Feature kDisableKeepaliveFetch{"DisableKeepaliveFetch", base::FEATURE_DISABLED_BY_DEFAULT}; -// When kOutOfBlinkFrameAncestors is enabled, the frame-ancestors -// directive is parsed from the Content-Security-Policy header in the network -// service and enforced in the browser. -const base::Feature kOutOfBlinkFrameAncestors{"OutOfBlinkFrameAncestors", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Attach the origin of the destination URL to the "origin" header const base::Feature kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess{ @@ -187,12 +182,29 @@ const base::Feature kCorbAllowlistAlsoAppliesToOorCors = { const char kCorbAllowlistAlsoAppliesToOorCorsParamName[] = "AllowlistForCorbAndCors"; +// Controls whether a |request_initiator| that mismatches +// |request_initiator_site_lock| leads to 1) failing the HTTP request and 2) +// calling mojo::ReportBadMessage (on desktop platforms, where NetworkService +// is hosted outside of the Browser process, this leads to DumpWithoutCrashing +// and does *not* lead to a renderer kill). +// +// See also https://crbug.com/920634 +const base::Feature kRequestInitiatorSiteLockEnfocement = { + "RequestInitiatorSiteLockEnfocement", + base::FEATURE_DISABLED_BY_DEFAULT}; + // The preflight parser should reject Access-Control-Allow-* headers which do // not conform to ABNF. But if the strict check is applied directly, some // existing sites might fail to load. The feature flag controls whether a strict // check will be used or not. const base::Feature kStrictAccessControlAllowListCheck = { - "StrictAccessControlAllowListCheck", base::FEATURE_DISABLED_BY_DEFAULT}; + "StrictAccessControlAllowListCheck", base::FEATURE_ENABLED_BY_DEFAULT}; + +// When the CertVerifierService is enabled, certificate verification will not be +// performed in the network service, but will instead be brokered to a separate +// cert verification service potentially running in a different process. +const base::Feature kCertVerifierService{"CertVerifierService", + base::FEATURE_DISABLED_BY_DEFAULT}; // Enables preprocessing requests with the Trust Tokens API Fetch flags set, // and handling their responses, according to the protocol. @@ -230,9 +242,16 @@ const base::FeatureParam<TrustTokenOriginTrialSpec> TrustTokenOriginTrialSpec::kOriginTrialNotRequired, &kTrustTokenOriginTrialParamOptions}; +// Enables the Content Security Policy Embedded Enforcement check out of blink +const base::Feature kOutOfBlinkCSPEE{"OutOfBlinkCSPEE", + base::FEATURE_DISABLED_BY_DEFAULT}; + bool ShouldEnableOutOfBlinkCorsForTesting() { return base::FeatureList::IsEnabled(features::kOutOfBlinkCors); } +const base::Feature kWebSocketReassembleShortMessages{ + "WebSocketReassembleShortMessages", base::FEATURE_ENABLED_BY_DEFAULT}; + } // namespace features } // namespace network diff --git a/chromium/services/network/public/cpp/features.h b/chromium/services/network/public/cpp/features.h index 69eb7a29dea..213184d4cb4 100644 --- a/chromium/services/network/public/cpp/features.h +++ b/chromium/services/network/public/cpp/features.h @@ -13,8 +13,6 @@ namespace network { namespace features { COMPONENT_EXPORT(NETWORK_CPP) -extern const base::Feature kCapReferrerToOriginOnCrossOrigin; -COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kExpectCTReporting; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kNetworkErrorLogging; @@ -41,6 +39,8 @@ extern const base::Feature kCrossOriginOpenerPolicy; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginOpenerPolicyReporting; COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kCrossOriginOpenerPolicyAccessReporting; +COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginEmbedderPolicy; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kBlockNonSecureExternalRequests; @@ -56,8 +56,6 @@ extern const base::FeatureParam<std::string> COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kDisableKeepaliveFetch; COMPONENT_EXPORT(NETWORK_CPP) -extern const base::Feature kOutOfBlinkFrameAncestors; -COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess; COMPONENT_EXPORT(NETWORK_CPP) @@ -72,7 +70,11 @@ extern const base::Feature kCorbAllowlistAlsoAppliesToOorCors; COMPONENT_EXPORT(NETWORK_CPP) extern const char kCorbAllowlistAlsoAppliesToOorCorsParamName[]; COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kRequestInitiatorSiteLockEnfocement; +COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kStrictAccessControlAllowListCheck; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kCertVerifierService; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kTrustTokens; @@ -88,8 +90,14 @@ extern const base::FeatureParam<TrustTokenOriginTrialSpec> kTrustTokenOperationsRequiringOriginTrial; COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kOutOfBlinkCSPEE; + +COMPONENT_EXPORT(NETWORK_CPP) bool ShouldEnableOutOfBlinkCorsForTesting(); +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kWebSocketReassembleShortMessages; + } // namespace features } // namespace network diff --git a/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc b/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc index 8cd78a620b7..9832b21aacd 100644 --- a/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc +++ b/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc @@ -283,7 +283,7 @@ DnsQueryType EnumTraits<DnsQueryType, net::DnsQueryType>::ToMojom( return DnsQueryType::PTR; case net::DnsQueryType::SRV: return DnsQueryType::SRV; - case net::DnsQueryType::ESNI: + case net::DnsQueryType::INTEGRITY: NOTIMPLEMENTED(); return DnsQueryType::UNSPECIFIED; } diff --git a/chromium/services/network/public/cpp/http_request_headers_mojom_traits.cc b/chromium/services/network/public/cpp/http_request_headers_mojom_traits.cc index d6b2987bcc2..66bedc18330 100644 --- a/chromium/services/network/public/cpp/http_request_headers_mojom_traits.cc +++ b/chromium/services/network/public/cpp/http_request_headers_mojom_traits.cc @@ -5,6 +5,7 @@ #include "services/network/public/cpp/http_request_headers_mojom_traits.h" #include "net/http/http_util.h" +#include "services/network/public/cpp/crash_keys.h" namespace mojo { @@ -15,13 +16,17 @@ bool StructTraits<network::mojom::HttpRequestHeaderKeyValuePairDataView, net::HttpRequestHeaders::HeaderKeyValuePair* item) { if (!data.ReadKey(&item->key)) return false; - if (!net::HttpUtil::IsValidHeaderName(item->key)) + if (!net::HttpUtil::IsValidHeaderName(item->key)) { + network::debug::SetDeserializationCrashKeyString("header_key"); return false; + } if (!data.ReadValue(&item->value)) return false; item->value = std::string(net::HttpUtil::TrimLWS(item->value)); - if (!net::HttpUtil::IsValidHeaderValue(item->value)) + if (!net::HttpUtil::IsValidHeaderValue(item->value)) { + network::debug::SetDeserializationCrashKeyString("header_value"); return false; + } return true; } diff --git a/chromium/services/network/public/cpp/isolation_info_mojom_traits.cc b/chromium/services/network/public/cpp/isolation_info_mojom_traits.cc index 07d848dbde8..8f797b5bd00 100644 --- a/chromium/services/network/public/cpp/isolation_info_mojom_traits.cc +++ b/chromium/services/network/public/cpp/isolation_info_mojom_traits.cc @@ -5,6 +5,7 @@ #include "services/network/public/cpp/isolation_info_mojom_traits.h" #include "base/notreached.h" +#include "services/network/public/cpp/crash_keys.h" #include "services/network/public/cpp/site_for_cookies_mojom_traits.h" namespace mojo { @@ -51,9 +52,15 @@ bool StructTraits<network::mojom::IsolationInfoDataView, net::IsolationInfo>:: net::SiteForCookies site_for_cookies; net::IsolationInfo::RedirectMode redirect_mode; - if (!data.ReadTopFrameOrigin(&top_frame_origin) || - !data.ReadFrameOrigin(&frame_origin) || - !data.ReadSiteForCookies(&site_for_cookies) || + if (!data.ReadTopFrameOrigin(&top_frame_origin)) { + network::debug::SetDeserializationCrashKeyString("isolation_top_origin"); + return false; + } + if (!data.ReadFrameOrigin(&frame_origin)) { + network::debug::SetDeserializationCrashKeyString("isolation_frame_origin"); + return false; + } + if (!data.ReadSiteForCookies(&site_for_cookies) || !data.ReadRedirectMode(&redirect_mode)) { return false; } @@ -62,8 +69,10 @@ bool StructTraits<network::mojom::IsolationInfoDataView, net::IsolationInfo>:: net::IsolationInfo::CreateIfConsistent(redirect_mode, top_frame_origin, frame_origin, site_for_cookies, data.opaque_and_non_transient()); - if (!isolation_info) + if (!isolation_info) { + network::debug::SetDeserializationCrashKeyString("isolation_inconsistent"); return false; + } *out = std::move(*isolation_info); return true; diff --git a/chromium/services/network/public/cpp/load_timing_info_mojom_traits.cc b/chromium/services/network/public/cpp/load_timing_info_mojom_traits.cc index d66f2647790..9039014d7f8 100644 --- a/chromium/services/network/public/cpp/load_timing_info_mojom_traits.cc +++ b/chromium/services/network/public/cpp/load_timing_info_mojom_traits.cc @@ -34,10 +34,14 @@ bool StructTraits<network::mojom::LoadTimingInfoDataView, net::LoadTimingInfo>:: data.ReadSendEnd(&out->send_end) && data.ReadReceiveHeadersStart(&out->receive_headers_start) && data.ReadReceiveHeadersEnd(&out->receive_headers_end) && + data.ReadFirstEarlyHintsTime(&out->first_early_hints_time) && data.ReadPushStart(&out->push_start) && data.ReadPushEnd(&out->push_end) && data.ReadServiceWorkerStartTime(&out->service_worker_start_time) && - data.ReadServiceWorkerReadyTime(&out->service_worker_ready_time); + data.ReadServiceWorkerReadyTime(&out->service_worker_ready_time) && + data.ReadServiceWorkerFetchStart(&out->service_worker_fetch_start) && + data.ReadServiceWorkerRespondWithSettled( + &out->service_worker_respond_with_settled); } } // namespace mojo diff --git a/chromium/services/network/public/cpp/load_timing_info_mojom_traits.h b/chromium/services/network/public/cpp/load_timing_info_mojom_traits.h index c118264b586..0bec1dbc1b5 100644 --- a/chromium/services/network/public/cpp/load_timing_info_mojom_traits.h +++ b/chromium/services/network/public/cpp/load_timing_info_mojom_traits.h @@ -96,6 +96,11 @@ struct StructTraits<network::mojom::LoadTimingInfoDataView, return obj.receive_headers_end; } + static base::TimeTicks first_early_hints_time( + const net::LoadTimingInfo& obj) { + return obj.first_early_hints_time; + } + static base::TimeTicks push_start(const net::LoadTimingInfo& obj) { return obj.push_start; } @@ -114,6 +119,16 @@ struct StructTraits<network::mojom::LoadTimingInfoDataView, return obj.service_worker_ready_time; } + static base::TimeTicks service_worker_fetch_start( + const net::LoadTimingInfo& obj) { + return obj.service_worker_fetch_start; + } + + static base::TimeTicks service_worker_respond_with_settled( + const net::LoadTimingInfo& obj) { + return obj.service_worker_respond_with_settled; + } + static bool Read(network::mojom::LoadTimingInfoDataView obj, net::LoadTimingInfo* output); }; diff --git a/chromium/services/network/public/cpp/net_ipc_param_traits.cc b/chromium/services/network/public/cpp/net_ipc_param_traits.cc index 8f6d970ee13..bb2f1be0aa0 100644 --- a/chromium/services/network/public/cpp/net_ipc_param_traits.cc +++ b/chromium/services/network/public/cpp/net_ipc_param_traits.cc @@ -489,6 +489,7 @@ void ParamTraits<net::LoadTimingInfo>::Write(base::Pickle* m, WriteParam(m, p.send_end); WriteParam(m, p.receive_headers_start); WriteParam(m, p.receive_headers_end); + WriteParam(m, p.first_early_hints_time); WriteParam(m, p.push_start); WriteParam(m, p.push_end); } @@ -519,6 +520,7 @@ bool ParamTraits<net::LoadTimingInfo>::Read(const base::Pickle* m, ReadParam(m, iter, &r->send_end) && ReadParam(m, iter, &r->receive_headers_start) && ReadParam(m, iter, &r->receive_headers_end) && + ReadParam(m, iter, &r->first_early_hints_time) && ReadParam(m, iter, &r->push_start) && ReadParam(m, iter, &r->push_end); } @@ -557,6 +559,8 @@ void ParamTraits<net::LoadTimingInfo>::Log(const param_type& p, l->append(", "); LogParam(p.receive_headers_end, l); l->append(", "); + LogParam(p.first_early_hints_time, l); + l->append(", "); LogParam(p.push_start, l); l->append(", "); LogParam(p.push_end, l); diff --git a/chromium/services/network/public/cpp/network_ipc_param_traits.h b/chromium/services/network/public/cpp/network_ipc_param_traits.h index 3ddf58f64d2..90fb4bf7d95 100644 --- a/chromium/services/network/public/cpp/network_ipc_param_traits.h +++ b/chromium/services/network/public/cpp/network_ipc_param_traits.h @@ -49,8 +49,8 @@ namespace IPC { -// TODO(Richard): Remove this traits after usage of FrameHostMsg_OpenURL_Params -// disappears. +// TODO(Richard): Remove this traits after usage of +// content::mojom::OpenURLParams disappears. template <> struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<network::DataElement> { typedef network::DataElement param_type; @@ -61,7 +61,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<network::DataElement> { static void Log(const param_type& p, std::string* l); }; -// TODO(Richard): Remove this traits after usage of FrameHostMsg_OpenURL_Params +// TODO(Richard): Remove this traits after usage of OpenURLParams struct // disappears. template <> struct COMPONENT_EXPORT(NETWORK_CPP_BASE) diff --git a/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc b/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc index 6eca5d7f68c..7722e574bb2 100644 --- a/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc +++ b/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc @@ -28,7 +28,8 @@ OptionalTrustTokenParams NonemptyTrustTokenParams() { mojom::TrustTokenSignRequestData::kInclude, /*include_timestamp_header=*/true, url::Origin::Create(GURL("https://issuer.com")), - std::vector<std::string>{"some_header", "another_header"}); + std::vector<std::string>{"some_header", "another_header"}, + "some additional signing data"); } } // namespace diff --git a/chromium/services/network/public/cpp/origin_isolation_parser.cc b/chromium/services/network/public/cpp/origin_isolation_parser.cc new file mode 100644 index 00000000000..b324c3d01e8 --- /dev/null +++ b/chromium/services/network/public/cpp/origin_isolation_parser.cc @@ -0,0 +1,15 @@ +// Copyright 2020 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 "services/network/public/cpp/origin_isolation_parser.h" +#include "net/http/structured_headers.h" + +namespace network { + +bool ParseOriginIsolation(const std::string& header_value) { + const auto item = net::structured_headers::ParseItem(header_value); + return item && item->item.is_boolean() && item->item.GetBoolean(); +} + +} // namespace network diff --git a/chromium/services/network/public/cpp/origin_isolation_parser.h b/chromium/services/network/public/cpp/origin_isolation_parser.h new file mode 100644 index 00000000000..dd4f7d7fc81 --- /dev/null +++ b/chromium/services/network/public/cpp/origin_isolation_parser.h @@ -0,0 +1,23 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_ORIGIN_ISOLATION_PARSER_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_ORIGIN_ISOLATION_PARSER_H_ + +#include <string> +#include "base/component_export.h" + +namespace network { + +// Parsing is done following the Origin-Isolation spec draft: +// https://github.com/whatwg/html/pull/5545 +// +// See the comment in network::PopulateParsedHeaders for restrictions on this +// function. +COMPONENT_EXPORT(NETWORK_CPP) +bool ParseOriginIsolation(const std::string&); + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_ORIGIN_ISOLATION_PARSER_H_ diff --git a/chromium/services/network/public/cpp/origin_isolation_parser_unittest.cc b/chromium/services/network/public/cpp/origin_isolation_parser_unittest.cc new file mode 100644 index 00000000000..0b53d3a0901 --- /dev/null +++ b/chromium/services/network/public/cpp/origin_isolation_parser_unittest.cc @@ -0,0 +1,31 @@ +// Copyright 2020 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 "services/network/public/cpp/origin_isolation_parser.h" + +#include <string> +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace network { + +TEST(OriginIsolationHeaderTest, Parse) { + EXPECT_EQ(ParseOriginIsolation(""), false); + + EXPECT_EQ(ParseOriginIsolation("?1"), true); + EXPECT_EQ(ParseOriginIsolation("?0"), false); + + EXPECT_EQ(ParseOriginIsolation("?1;param"), true); + EXPECT_EQ(ParseOriginIsolation("?1;param=value"), true); + EXPECT_EQ(ParseOriginIsolation("?1;param=value;param2=value2"), true); + + EXPECT_EQ(ParseOriginIsolation("true"), false); + EXPECT_EQ(ParseOriginIsolation("\"?1\""), false); + EXPECT_EQ(ParseOriginIsolation("1"), false); + EXPECT_EQ(ParseOriginIsolation("?2"), false); + EXPECT_EQ(ParseOriginIsolation("(?1)"), false); +} + +} // namespace network diff --git a/chromium/services/network/public/cpp/parsed_headers.cc b/chromium/services/network/public/cpp/parsed_headers.cc index f4c4e3f09c1..dc7b9ae7adc 100644 --- a/chromium/services/network/public/cpp/parsed_headers.cc +++ b/chromium/services/network/public/cpp/parsed_headers.cc @@ -10,6 +10,7 @@ #include "services/network/public/cpp/cross_origin_embedder_policy_parser.h" #include "services/network/public/cpp/cross_origin_opener_policy_parser.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/origin_isolation_parser.h" namespace network { @@ -20,16 +21,18 @@ mojom::ParsedHeadersPtr PopulateParsedHeaders( if (!headers) return parsed_headers; - if (base::FeatureList::IsEnabled(features::kOutOfBlinkFrameAncestors)) { - AddContentSecurityPolicyFromHeaders( - *headers, url, &parsed_headers->content_security_policy); - } + AddContentSecurityPolicyFromHeaders(*headers, url, + &parsed_headers->content_security_policy); parsed_headers->cross_origin_embedder_policy = ParseCrossOriginEmbedderPolicy(*headers); parsed_headers->cross_origin_opener_policy = ParseCrossOriginOpenerPolicy(*headers); + std::string origin_isolation; + if (headers->GetNormalizedHeader("Origin-Isolation", &origin_isolation)) + parsed_headers->origin_isolation = ParseOriginIsolation(origin_isolation); + std::string accept_ch; if (headers->GetNormalizedHeader("Accept-CH", &accept_ch)) parsed_headers->accept_ch = ParseAcceptCH(accept_ch); diff --git a/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.cc b/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.cc new file mode 100644 index 00000000000..5d0619259b9 --- /dev/null +++ b/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.cc @@ -0,0 +1,30 @@ +// Copyright 2020 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 "services/network/public/cpp/quic_transport_error_mojom_traits.h" + +namespace mojo { + +bool StructTraits<network::mojom::QuicTransportErrorDataView, + net::QuicTransportError>:: + Read(network::mojom::QuicTransportErrorDataView in, + net::QuicTransportError* out) { + if (in.net_error() > 0) { + return false; + } + if (in.quic_error() < 0 || in.quic_error() >= quic::QUIC_LAST_ERROR) { + return false; + } + std::string details; + if (!in.ReadDetails(&details)) { + return false; + } + + *out = net::QuicTransportError( + in.net_error(), static_cast<quic::QuicErrorCode>(in.quic_error()), + std::move(details), in.safe_to_report_details()); + return true; +} + +} // namespace mojo diff --git a/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.h b/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.h new file mode 100644 index 00000000000..d86e513c4b4 --- /dev/null +++ b/chromium/services/network/public/cpp/quic_transport_error_mojom_traits.h @@ -0,0 +1,36 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_QUIC_TRANSPORT_ERROR_MOJOM_TRAITS_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_QUIC_TRANSPORT_ERROR_MOJOM_TRAITS_H_ + +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "net/quic/quic_transport_error.h" +#include "services/network/public/mojom/quic_transport.mojom-shared.h" + +namespace mojo { + +template <> +struct COMPONENT_EXPORT(NETWORK_CPP_BASE) + StructTraits<network::mojom::QuicTransportErrorDataView, + net::QuicTransportError> { + static int32_t net_error(const net::QuicTransportError& e) { + return e.net_error; + } + static int32_t quic_error(const net::QuicTransportError& e) { + return static_cast<int32_t>(e.quic_error); + } + static const std::string& details(const net::QuicTransportError& e) { + return e.details; + } + static bool safe_to_report_details(const net::QuicTransportError& e) { + return e.safe_to_report_details; + } + static bool Read(network::mojom::QuicTransportErrorDataView in, + net::QuicTransportError* out); +}; + +} // namespace mojo + +#endif // SERVICES_NETWORK_PUBLIC_CPP_QUIC_TRANSPORT_ERROR_MOJOM_TRAITS_H_ diff --git a/chromium/services/network/public/cpp/resource_request.cc b/chromium/services/network/public/cpp/resource_request.cc index 2b2692f98d7..7d32256054a 100644 --- a/chromium/services/network/public/cpp/resource_request.cc +++ b/chromium/services/network/public/cpp/resource_request.cc @@ -154,6 +154,7 @@ net::URLRequest::ReferrerPolicy ReferrerPolicyForUrlRequest( return net::URLRequest:: REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; } + NOTREACHED(); return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; } diff --git a/chromium/services/network/public/cpp/resource_request_body.h b/chromium/services/network/public/cpp/resource_request_body.h index 6d4920d3d95..2634327fdac 100644 --- a/chromium/services/network/public/cpp/resource_request_body.h +++ b/chromium/services/network/public/cpp/resource_request_body.h @@ -80,6 +80,12 @@ class COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequestBody // support chunked uploads. void SetToChunkedDataPipe(mojo::PendingRemote<mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter); + void SetAllowHTTP1ForStreamingUpload(bool allow) { + allow_http1_for_streaming_upload_ = allow; + } + bool AllowHTTP1ForStreamingUpload() const { + return allow_http1_for_streaming_upload_; + } const std::vector<DataElement>* elements() const { return &elements_; } std::vector<DataElement>* elements_mutable() { return &elements_; } @@ -117,6 +123,8 @@ class COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequestBody bool contains_sensitive_info_; + bool allow_http1_for_streaming_upload_ = true; + DISALLOW_COPY_AND_ASSIGN(ResourceRequestBody); }; diff --git a/chromium/services/network/public/cpp/simple_url_loader_unittest.cc b/chromium/services/network/public/cpp/simple_url_loader_unittest.cc index 18dc5d0e976..beea0a11e91 100644 --- a/chromium/services/network/public/cpp/simple_url_loader_unittest.cc +++ b/chromium/services/network/public/cpp/simple_url_loader_unittest.cc @@ -54,6 +54,7 @@ #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_network_context_client.h" #include "services/network/test/test_network_service_client.h" #include "testing/gtest/include/gtest/gtest.h" @@ -592,6 +593,12 @@ class SimpleURLLoaderTestBase { network_service_remote.BindNewPipeAndPassReceiver()); network::mojom::NetworkContextParamsPtr context_params = network::mojom::NetworkContextParams::New(); + + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); + network_service_remote->CreateNetworkContext( network_context_.BindNewPipeAndPassReceiver(), std::move(context_params)); diff --git a/chromium/services/network/public/cpp/site_for_cookies_mojom_traits.cc b/chromium/services/network/public/cpp/site_for_cookies_mojom_traits.cc index 74df0098fa7..282d0ef1e08 100644 --- a/chromium/services/network/public/cpp/site_for_cookies_mojom_traits.cc +++ b/chromium/services/network/public/cpp/site_for_cookies_mojom_traits.cc @@ -4,6 +4,7 @@ #include "services/network/public/cpp/site_for_cookies_mojom_traits.h" #include "net/base/features.h" +#include "services/network/public/cpp/crash_keys.h" namespace mojo { @@ -11,13 +12,19 @@ bool StructTraits<network::mojom::SiteForCookiesDataView, net::SiteForCookies>:: Read(network::mojom::SiteForCookiesDataView data, net::SiteForCookies* out) { std::string scheme, registrable_domain; - if (!data.ReadScheme(&scheme)) + if (!data.ReadScheme(&scheme)) { return false; - if (!data.ReadRegistrableDomain(®istrable_domain)) + } + if (!data.ReadRegistrableDomain(®istrable_domain)) { return false; + } - return net::SiteForCookies::FromWire(scheme, registrable_domain, - data.schemefully_same(), out); + bool result = net::SiteForCookies::FromWire(scheme, registrable_domain, + data.schemefully_same(), out); + if (!result) { + network::debug::SetDeserializationCrashKeyString("site_for_cookie"); + } + return result; } } // namespace mojo diff --git a/chromium/services/network/public/cpp/spki_hash_set.cc b/chromium/services/network/public/cpp/spki_hash_set.cc new file mode 100644 index 00000000000..e8f2e3521c2 --- /dev/null +++ b/chromium/services/network/public/cpp/spki_hash_set.cc @@ -0,0 +1,30 @@ +// Copyright 2020 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 "services/network/public/cpp/spki_hash_set.h" + +#include "base/check_op.h" +#include "base/logging.h" +#include "net/base/hash_value.h" + +namespace network { + +// static +SPKIHashSet CreateSPKIHashSet(const std::vector<std::string>& fingerprints) { + SPKIHashSet spki_hash_list; + for (const std::string& fingerprint : fingerprints) { + net::HashValue hash; + if (!hash.FromString("sha256/" + fingerprint)) { + LOG(ERROR) << "Invalid SPKI: " << fingerprint; + continue; + } + net::SHA256HashValue sha256; + DCHECK_EQ(hash.size(), sizeof(sha256)); + memcpy(&sha256, hash.data(), sizeof(sha256)); + spki_hash_list.insert(sha256); + } + return spki_hash_list; +} + +} // namespace network diff --git a/chromium/services/network/public/cpp/spki_hash_set.h b/chromium/services/network/public/cpp/spki_hash_set.h new file mode 100644 index 00000000000..b8c304bcc72 --- /dev/null +++ b/chromium/services/network/public/cpp/spki_hash_set.h @@ -0,0 +1,28 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_SPKI_HASH_SET_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_SPKI_HASH_SET_H_ + +#include "base/component_export.h" +#include "base/containers/flat_set.h" + +namespace net { +struct SHA256HashValue; +} // namespace net + +namespace network { + +// SPKIHashSet is a set of SHA-256 SPKI fingerprints (RFC 7469, Section 2.4). +using SPKIHashSet = base::flat_set<net::SHA256HashValue>; + +// CreateSPKIHashSet converts a vector of Base64-encoded SHA-256 SPKI +// fingerprints into an SPKIHashSet. Invalid fingerprints are logged and +// skipped. +COMPONENT_EXPORT(NETWORK_CPP) +SPKIHashSet CreateSPKIHashSet(const std::vector<std::string>& fingerprints); + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_SPKI_HASH_SET_H_ diff --git a/chromium/services/network/public/cpp/trust_token_operation_authorization.h b/chromium/services/network/public/cpp/trust_token_operation_authorization.h new file mode 100644 index 00000000000..6996e1f7f9f --- /dev/null +++ b/chromium/services/network/public/cpp/trust_token_operation_authorization.h @@ -0,0 +1,41 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_OPERATION_AUTHORIZATION_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_OPERATION_AUTHORIZATION_H_ + +#include "services/network/public/mojom/trust_tokens.mojom-shared.h" + +namespace network { + +// A Trust Tokens operation requires the 'trust-token-redemption' Feature Policy +// feature if it's of type "redemption" or "signing" (as opposed to "issuance"). +// +// Needing the top-level frame's authorization to execute a redemption operation +// has two motivations: security and privacy. +// - Security: Executing a redemption operation counts against rate limits, +// currently indelible short of a user-initiated browsing data clear, that are +// scoped to the redemption-time top frame origin. Using Feature Policy stops +// subframes from denying service to ancestor frames by exhausting these rate +// limits. +// - Privacy: The results of a redemption operation ("signed redemption +// records") are persistent first-party identifiers in the context of the top +// frame at redemption time. Since embedded third-party frames are on the other +// side of a privacy boundary, we’d like to prohibit these third-party frames +// from having access to the redeeming-context SRRs without the top-level +// frame's explicit consent. +constexpr bool DoesTrustTokenOperationRequireFeaturePolicy( + mojom::TrustTokenOperationType type) { + switch (type) { + case mojom::TrustTokenOperationType::kRedemption: + case mojom::TrustTokenOperationType::kSigning: + return true; + case mojom::TrustTokenOperationType::kIssuance: + return false; + } +} + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_OPERATION_AUTHORIZATION_H_ diff --git a/chromium/services/network/public/cpp/trust_token_parameterization.h b/chromium/services/network/public/cpp/trust_token_parameterization.h new file mode 100644 index 00000000000..4bb09ab64ae --- /dev/null +++ b/chromium/services/network/public/cpp/trust_token_parameterization.h @@ -0,0 +1,29 @@ +// Copyright 2020 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 SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_PARAMETERIZATION_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_PARAMETERIZATION_H_ + +namespace network { + +// Note: This file contains Trust Tokens constants that are part of the network +// service public API. Network service-internal Trust Tokens-related constants +// live in a corresponding file in the network service-internal Trust Tokens +// directory. + +// The maximum number of trust token issuers allowed to be associated with a +// given top-level origin. +// +// This value is quite low because registering additional issuers with an origin +// has a number of privacy risks (for instance, whether or not a user has any +// tokens issued by a given issuer reveals one bit of identifying information). +constexpr int kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers = 2; + +// When the client provides custom signing data alongside a Trust Tokens signed +// request, this is the data's maximum length in bytes. +constexpr size_t kTrustTokenAdditionalSigningDataMaxSizeBytes = 1 << 11; + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_TRUST_TOKEN_PARAMETERIZATION_H_ diff --git a/chromium/services/network/public/cpp/url_request_mojom_traits.cc b/chromium/services/network/public/cpp/url_request_mojom_traits.cc index 5c94c3cd869..ce1478f6df6 100644 --- a/chromium/services/network/public/cpp/url_request_mojom_traits.cc +++ b/chromium/services/network/public/cpp/url_request_mojom_traits.cc @@ -12,6 +12,7 @@ #include "mojo/public/cpp/base/file_path_mojom_traits.h" #include "mojo/public/cpp/base/time_mojom_traits.h" #include "mojo/public/cpp/base/unguessable_token_mojom_traits.h" +#include "services/network/public/cpp/crash_keys.h" #include "services/network/public/cpp/http_request_headers_mojom_traits.h" #include "services/network/public/cpp/isolation_info_mojom_traits.h" #include "services/network/public/cpp/network_ipc_param_traits.h" @@ -171,13 +172,30 @@ bool StructTraits< network::mojom::URLRequestDataView, network::ResourceRequest>::Read(network::mojom::URLRequestDataView data, network::ResourceRequest* out) { - if (!data.ReadMethod(&out->method) || !data.ReadUrl(&out->url) || - !data.ReadSiteForCookies(&out->site_for_cookies) || - !data.ReadTrustedParams(&out->trusted_params) || - !data.ReadRequestInitiator(&out->request_initiator) || - !data.ReadIsolatedWorldOrigin(&out->isolated_world_origin) || - !data.ReadReferrer(&out->referrer) || - !data.ReadReferrerPolicy(&out->referrer_policy) || + if (!data.ReadMethod(&out->method)) { + return false; + } + if (!data.ReadUrl(&out->url)) { + network::debug::SetDeserializationCrashKeyString("url"); + return false; + } + if (!data.ReadSiteForCookies(&out->site_for_cookies) || + !data.ReadTrustedParams(&out->trusted_params)) { + return false; + } + if (!data.ReadRequestInitiator(&out->request_initiator)) { + network::debug::SetDeserializationCrashKeyString("request_initiator"); + return false; + } + if (!data.ReadIsolatedWorldOrigin(&out->isolated_world_origin)) { + network::debug::SetDeserializationCrashKeyString("isolated_world_origin"); + return false; + } + if (!data.ReadReferrer(&out->referrer)) { + network::debug::SetDeserializationCrashKeyString("referrer"); + return false; + } + if (!data.ReadReferrerPolicy(&out->referrer_policy) || !data.ReadHeaders(&out->headers) || !data.ReadCorsExemptHeaders(&out->cors_exempt_headers) || !data.ReadPriority(&out->priority) || @@ -242,15 +260,27 @@ bool StructTraits<network::mojom::URLRequestBodyDataView, return false; body->set_identifier(data.identifier()); body->set_contains_sensitive_info(data.contains_sensitive_info()); + body->SetAllowHTTP1ForStreamingUpload( + data.allow_http1_for_streaming_upload()); *out = std::move(body); return true; } bool StructTraits<network::mojom::DataElementDataView, network::DataElement>:: Read(network::mojom::DataElementDataView data, network::DataElement* out) { - if (!data.ReadPath(&out->path_) || !data.ReadFile(&out->file_) || - !data.ReadBlobUuid(&out->blob_uuid_) || - !data.ReadExpectedModificationTime(&out->expected_modification_time_)) { + if (!data.ReadPath(&out->path_)) { + network::debug::SetDeserializationCrashKeyString("data_element_path"); + return false; + } + if (!data.ReadFile(&out->file_)) { + network::debug::SetDeserializationCrashKeyString("data_element_file"); + return false; + } + if (!data.ReadBlobUuid(&out->blob_uuid_)) { + network::debug::SetDeserializationCrashKeyString("data_element_blob_uuid"); + return false; + } + if (!data.ReadExpectedModificationTime(&out->expected_modification_time_)) { return false; } if (data.type() == network::mojom::DataElementType::kBytes) { diff --git a/chromium/services/network/public/cpp/url_request_mojom_traits.h b/chromium/services/network/public/cpp/url_request_mojom_traits.h index 498c7bb6a78..9e1dd066185 100644 --- a/chromium/services/network/public/cpp/url_request_mojom_traits.h +++ b/chromium/services/network/public/cpp/url_request_mojom_traits.h @@ -288,6 +288,11 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) return r->contains_sensitive_info_; } + static bool allow_http1_for_streaming_upload( + const scoped_refptr<network::ResourceRequestBody>& r) { + return r->allow_http1_for_streaming_upload_; + } + static bool Read(network::mojom::URLRequestBodyDataView data, scoped_refptr<network::ResourceRequestBody>* out); }; diff --git a/chromium/services/network/public/mojom/BUILD.gn b/chromium/services/network/public/mojom/BUILD.gn index a79516b579e..4d5bef25d3c 100644 --- a/chromium/services/network/public/mojom/BUILD.gn +++ b/chromium/services/network/public/mojom/BUILD.gn @@ -49,6 +49,22 @@ mojom("mojom_ip_address") { "//services/network/public/cpp:cpp_base", ] }, + { + types = [ + { + mojom = "network.mojom.ConnectionInfo" + cpp = "::net::HttpResponseInfo::ConnectionInfo" + }, + ] + traits_headers = [ + "//net/http/http_response_info.h", + "//services/network/public/cpp/network_ipc_param_traits.h", + ] + traits_public_deps = [ + "//net", + "//services/network/public/cpp:cpp_base", + ] + }, ] cpp_typemaps = [ @@ -76,6 +92,21 @@ mojom("mojom_ip_address") { [ "//services/network/public/cpp/address_list_mojom_traits.h" ] traits_public_deps = [ "//net" ] }, + { + types = [ + { + mojom = "network.mojom.QuicTransportError" + cpp = "::net::QuicTransportError" + }, + ] + traits_headers = [ + "//services/network/public/cpp/quic_transport_error_mojom_traits.h", + ] + traits_public_deps = [ + "//net", + "//services/network/public/cpp:cpp_base", + ] + }, ] cpp_typemaps += shared_cpp_typemaps @@ -325,6 +356,10 @@ mojom("cookies_mojom") { cpp = "::net::CookieSameSite" }, { + mojom = "network.mojom.CookieEffectiveSameSite" + cpp = "::net::CookieEffectiveSameSite" + }, + { mojom = "network.mojom.CookieSameSiteContext" cpp = "::net::CookieOptions::SameSiteCookieContext" }, @@ -342,11 +377,11 @@ mojom("cookies_mojom") { }, { mojom = "network.mojom.CookieInclusionStatusWarningReason" - cpp = "::net::CanonicalCookie::CookieInclusionStatus::WarningReason" + cpp = "::net::CookieInclusionStatus::WarningReason" }, { mojom = "network.mojom.CookieInclusionStatus" - cpp = "::net::CanonicalCookie::CookieInclusionStatus" + cpp = "::net::CookieInclusionStatus" move_only = true }, { @@ -354,6 +389,10 @@ mojom("cookies_mojom") { cpp = "::net::CookieWithStatus" }, { + mojom = "network.mojom.CookieWithAccessResult" + cpp = "::net::CookieWithAccessResult" + }, + { mojom = "network.mojom.CookieAndLineWithStatus" cpp = "::net::CookieAndLineWithStatus" }, @@ -646,10 +685,6 @@ mojom("mojom") { cpp = "::net::ct::CTPolicyCompliance" }, { - mojom = "network.mojom.ConnectionInfo" - cpp = "::net::HttpResponseInfo::ConnectionInfo" - }, - { mojom = "network.mojom.CorsErrorStatus" cpp = "::network::CorsErrorStatus" }, diff --git a/chromium/services/network/public/mojom/cert_verifier_service.mojom b/chromium/services/network/public/mojom/cert_verifier_service.mojom index 063549cae19..a6181fa2c5d 100644 --- a/chromium/services/network/public/mojom/cert_verifier_service.mojom +++ b/chromium/services/network/public/mojom/cert_verifier_service.mojom @@ -6,6 +6,7 @@ module cert_verifier.mojom; import "mojo/public/mojom/base/big_buffer.mojom"; import "services/network/public/mojom/network_param.mojom"; +import "services/network/public/mojom/url_loader_factory.mojom"; // Mojo version of net::CertVerifier::RequestParams. struct RequestParams { @@ -25,12 +26,30 @@ struct CertVerifierConfig { bool disable_symantec_enforcement; mojo_base.mojom.BigBuffer crl_set; array<network.mojom.X509Certificate> additional_trust_anchors; + array<network.mojom.X509Certificate> additional_untrusted_authorities; +}; + +// Allows the CertVerifierService to connect a new URLLoaderFactory if its +// existing URLLoaderFactory is disconnected. The CertVerifierService uses the +// URLLoaderFactory for AIA and OCSP fetching. +interface URLLoaderFactoryConnector { + // Binds a URLLoaderFactory. + CreateURLLoaderFactory( + pending_receiver<network.mojom.URLLoaderFactory> url_loader_factory); }; // An interface that verifies a certificate based on the |params|, and calls the // |Complete| method on the returned CertVerifierRequest when the result is // available. interface CertVerifierService { + // |url_loader_factory| allows the CertVerifierService to connect to the + // network for things like AIA or OCSP. |reconnector| allows the CertVerifier + // to reconnect its URLLoaderFactory in case the network service disconnects + // its URLLoaderFactories without crashing. Must be called before Verify() to + // have an effect. + EnableNetworkAccess( + pending_remote<network.mojom.URLLoaderFactory> url_loader_factory, + pending_remote<URLLoaderFactoryConnector>? reconnector); // Mojo IPC used to verify a certificate. Sends results to the // |cert_verifier_request| interface when verification is complete. Verify(RequestParams params, diff --git a/chromium/services/network/public/mojom/content_security_policy.mojom b/chromium/services/network/public/mojom/content_security_policy.mojom index 114377a2fe1..ca7d5062a8f 100644 --- a/chromium/services/network/public/mojom/content_security_policy.mojom +++ b/chromium/services/network/public/mojom/content_security_policy.mojom @@ -81,6 +81,13 @@ enum CSPDirectiveName { FormAction, NavigateTo, FrameAncestors, + ImgSrc, + MediaSrc, + ObjectSrc, + ScriptSrc, + StyleSrc, + WorkerSrc, + ConnectSrc }; struct ContentSecurityPolicy { @@ -98,7 +105,8 @@ struct ContentSecurityPolicy { bool treat_as_public_address = false; // https://www.w3.org/TR/CSP3/#directive-sandbox - WebSandboxFlags sandbox; // WebSandboxFlags::kNone. + // This uses the convention: kAll means "everything is allowed". + WebSandboxFlags sandbox = WebSandboxFlags.kAll; ContentSecurityPolicyHeader header; diff --git a/chromium/services/network/public/mojom/cookie_manager.mojom b/chromium/services/network/public/mojom/cookie_manager.mojom index 045bcc71c67..07aaee6a595 100644 --- a/chromium/services/network/public/mojom/cookie_manager.mojom +++ b/chromium/services/network/public/mojom/cookie_manager.mojom @@ -78,6 +78,13 @@ enum CookieSameSite { // Reserved 3 (was EXTENDED_MODE), next number is 4. }; +enum CookieEffectiveSameSite { + kNoRestriction = 0, + kLaxMode = 1, + kStrictMode = 2, + kLaxModeAllowUnsafe = 3, +}; + enum ContextType { CROSS_SITE, SAME_SITE_LAX_METHOD_UNSAFE, @@ -127,10 +134,10 @@ struct CanonicalCookie { struct CookieInclusionStatus { // Bitfield. Is defined in - // net::CanonicalCookie::CookieInclusionStatus::ExclusionReason. + // net::CookieInclusionStatus::ExclusionReason. + // net::CookieInclusionStatus::WarningReason. uint32 exclusion_reasons; // Bitfield. Is defined in - // net::CanonicalCookie::CookieInclusionStatus::WarningReason. uint32 warning_reasons; }; @@ -145,6 +152,16 @@ struct CookieAndLineWithStatus { CookieInclusionStatus status; }; +struct CookieAccessResult { + CookieEffectiveSameSite effective_same_site; + CookieInclusionStatus status; +}; + +struct CookieWithAccessResult { + CanonicalCookie cookie; + CookieAccessResult access_result; +}; + // Keep values here in sync with net::CookieChangeCause. enum CookieChangeCause { // The cookie was inserted. @@ -276,8 +293,8 @@ interface CookieManager { // blocked from being sent along with the reason each cookie was blocked. By // default, that option is not set and |excluded_cookies| is an empty list. GetCookieList(url.mojom.Url url, CookieOptions cookie_options) - => (array<CookieWithStatus> cookies, - array<CookieWithStatus> excluded_cookies); + => (array<CookieWithAccessResult> cookies, + array<CookieWithAccessResult> excluded_cookies); // Set a cookie. |source_url| is used to check whether existing secure // cookies can be overwritten (secure cookies may be created from a diff --git a/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom b/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom index 887426512d2..ac96351bc7a 100644 --- a/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom +++ b/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom @@ -4,6 +4,29 @@ module network.mojom; +import "url/mojom/url.mojom"; + +// Reports potential COOP violations. Implemented in the browser process. +// TODO(ahemery, pmeuleman): Add extra coop breakage cases as listed in +// https://docs.google.com/document/d/1zWqwI8PFrezwQpBSejIMUfdtsIYl9-h8epasdrDXVIM/edit +interface CrossOriginOpenerPolicyReporter { + + // When COOP triggers a browsing context group swap during a navigation, we + // lose the existing opener, which can create page breakage. We report such + // cases using this function. + // |is_reported_from_document| is true if the report is coming from the + // document begin navigated from. It is false if the report originates from + // the document we are navigating to. + // |is_report_only| is true if we are reporting a breakage that would have + // occurred if we enforced the reporting only values of COOP. + QueueOpenerBreakageReport(url.mojom.Url other_url, + bool is_reported_from_document, + bool is_report_only); + + // Connects a new pipe to this instance. + Clone(pending_receiver<CrossOriginOpenerPolicyReporter> receiver); +}; + // Cross-Origin-Opener-Policy enum representing parsed values. enum CrossOriginOpenerPolicyValue { // Severs the opener relationship with openers/opened documents that are not diff --git a/chromium/services/network/public/mojom/fetch_api.mojom b/chromium/services/network/public/mojom/fetch_api.mojom index b6d2b5ea4fd..8b3f2f16df1 100644 --- a/chromium/services/network/public/mojom/fetch_api.mojom +++ b/chromium/services/network/public/mojom/fetch_api.mojom @@ -62,9 +62,21 @@ enum RedirectMode { // Corresponds to Fetch request's "credentials mode": // https://fetch.spec.whatwg.org/#concept-request-credentials-mode enum CredentialsMode { + // TODO(https://crbug.com/775438): Due to a bug, this does not properly + // correspond to Fetch's "credentials mode", in that client certificates will + // be sent is available, or the handshake will be aborted in order to allow + // selecting a client cert. The correct behavior is to omit all client certs + // and continue the handshake without sending one if requested. kOmit, + kSameOrigin, kInclude, + + // TODO(https://crbug.com/775438): This works around kOmit not doing the + // spec-defined behavior. This is a temporary workaround that explicitly + // indicates the caller wants the spec-defined behavior. It's named as such + // because this should be only temporary, until kOmit is fixed. + kOmitBug_775438_Workaround }; // Corresponds to response types from the Fetch spec: diff --git a/chromium/services/network/public/mojom/load_timing_info.mojom b/chromium/services/network/public/mojom/load_timing_info.mojom index 2be0ef5a089..c8a6db8697e 100644 --- a/chromium/services/network/public/mojom/load_timing_info.mojom +++ b/chromium/services/network/public/mojom/load_timing_info.mojom @@ -29,6 +29,7 @@ struct LoadTimingInfo { mojo_base.mojom.TimeTicks send_end; mojo_base.mojom.TimeTicks receive_headers_start; mojo_base.mojom.TimeTicks receive_headers_end; + mojo_base.mojom.TimeTicks first_early_hints_time; mojo_base.mojom.TimeTicks push_start; mojo_base.mojom.TimeTicks push_end; @@ -39,4 +40,14 @@ struct LoadTimingInfo { // The time immediately before dispatching fetch event in ServiceWorker. // If the response is not provided by the ServiceWorker, kept empty. mojo_base.mojom.TimeTicks service_worker_ready_time; + + // The time immediately before the service worker fetch event handler runs. + // If the response is not provided by the service worker, kept empty. + // If respondWith() was not called, kept empty. + mojo_base.mojom.TimeTicks service_worker_fetch_start; + + // The time when a service worker respondWith promise gets settled. + // If the response is not provided by the service worker, kept empty. + // If respondWith() was not called, kept empty. + mojo_base.mojom.TimeTicks service_worker_respond_with_settled; }; diff --git a/chromium/services/network/public/mojom/network_context.mojom b/chromium/services/network/public/mojom/network_context.mojom index 93a2baf855d..6d5110add0e 100644 --- a/chromium/services/network/public/mojom/network_context.mojom +++ b/chromium/services/network/public/mojom/network_context.mojom @@ -11,7 +11,7 @@ import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/unguessable_token.mojom"; import "mojo/public/mojom/base/values.mojom"; import "services/network/public/mojom/address_list.mojom"; -import "services/network/public/mojom/parsed_headers.mojom"; +import "services/network/public/mojom/cert_verifier_service.mojom"; import "services/network/public/mojom/cookie_access_observer.mojom"; import "services/network/public/mojom/cookie_manager.mojom"; import "services/network/public/mojom/default_credentials.mojom"; @@ -29,6 +29,7 @@ import "services/network/public/mojom/network_isolation_key.mojom"; import "services/network/public/mojom/isolation_info.mojom"; import "services/network/public/mojom/network_param.mojom"; import "services/network/public/mojom/origin_policy_manager.mojom"; +import "services/network/public/mojom/parsed_headers.mojom"; import "services/network/public/mojom/p2p.mojom"; import "services/network/public/mojom/p2p_trusted.mojom"; import "services/network/public/mojom/proxy_config.mojom"; @@ -123,6 +124,21 @@ struct CertVerifierCreationParams { TrialComparisonCertVerifierParams? trial_comparison_cert_verifier_params; }; +// Includes a pipe to a CertVerifierService for usage by the +// NetworkContext. +struct CertVerifierServiceRemoteParams { + // A pipe to the CertVerifierService. + pending_remote<cert_verifier.mojom.CertVerifierService>? + cert_verifier_service; +}; + +// Contains the parameters necessary to either connect to a CertVerifierService +// or create a net::CertVerifier in the network service itself. +union CertVerifierParams { + CertVerifierServiceRemoteParams remote_params; + CertVerifierCreationParams creation_params; +}; + // Client to update the custom proxy config. interface CustomProxyConfigClient { OnCustomProxyConfigUpdated(CustomProxyConfig proxy_config); @@ -373,8 +389,9 @@ struct NetworkContextParams { [EnableIf=is_ct_supported] mojo_base.mojom.Time ct_log_update_time; - // Contains the parameters used to instantiate a net::CertVerifier in-process. - CertVerifierCreationParams? cert_verifier_creation_params; + // Contains either a pipe to a CertVerifierService, or parameters used to + // instantiate a net::CertVerifier in-process. + CertVerifierParams? cert_verifier_params; // Initial additional certificates that will be used for certificate // validation. @@ -407,24 +424,6 @@ struct NetworkContextParams { // decide. This should only be used by tests. bool skip_reporting_send_permission_check = false; - // Sets whether the NetworkContext should be used for globally scoped tasks - // that need to make network requests. Currently this includes DNS over HTTPS - // requests and certain cert validation requests (OCSP, AIA, etc) on some - // platforms. May only be set to true the first NetworkContext created using - // the NetworkService. Destroying a NetworkContext created with this set to - // true will destroy all other NetworkContexts. - // - // A failure to mark a NetworkContext as being used by certificate - // verification may result in incorrect certificate validation behaviors, - // such as the inability to verify EV certificates, to build paths when - // intermediates are missing, and to enforce revocation checking when it was - // requested via policy. - // - // TODO(mmenke): Once NSS is not used on any platform for certificate - // verification, we should consider using each URLRequestContext to do its own - // validation. - bool primary_network_context = false; - // Specifies the initial set of allowed and blocked origins for the // URLLoaderFactory consumers to access beyond the same-origin-policy. array<CorsOriginAccessPatterns> cors_origin_access_list; @@ -587,6 +586,13 @@ struct ClientSecurityState { IPAddressSpace ip_address_space = IPAddressSpace.kUnknown; }; +// Whether to forbid all Trust Tokens redemption and signing operations +// (https://github.com/wicg/trust-token-api). +enum TrustTokenRedemptionPolicy { + kForbid, + kPotentiallyPermit, +}; + struct URLLoaderFactoryParams { // Process requesting the URLLoaderFactory. // Set to kBrowserProcessId to indicate the browser process. @@ -598,10 +604,6 @@ struct URLLoaderFactoryParams { // 2) Make this an *origin* lock (rather than a *site* lock). url.mojom.Origin? request_initiator_site_lock; - // For subresource requests from frames, |top_frame_origin| is the request's - // top frame's origin. - url.mojom.Origin? top_frame_origin; - // Cross-origin read blocking (CORB) configuration. bool is_corb_enabled = true; @@ -667,10 +669,33 @@ struct URLLoaderFactoryParams { ClientSecurityState? client_security_state; // Used to report CORP violations caused by COEP. - CrossOriginEmbedderPolicyReporter? coep_reporter; + pending_remote<CrossOriginEmbedderPolicyReporter>? coep_reporter; // Used to notify clients about cookie reads or writes. - CookieAccessObserver? cookie_observer; + pending_remote<CookieAccessObserver>? cookie_observer; + + // If this equals kForbid, the context to which this loader is bound does not + // allow any Trust Tokens (https://github.com/wicg/trust-token-api) + // redemption or signing operations. + // + // This is set to kForbid for requests from subframes with the + // `trust-token-redemption` Feature Policy feature disabled by (1) policies + // inherited from their parents, or (2) the frames' iframe tag attributes. + // + // Note: Response headers can also disable this Feature Policy feature. + // This will not necessarily be reflected in the the enum's value. The + // response headers' value is only available from an untrusted source. + // - If the renderer behaves correctly, renderer-side checks guarantee that + // these operations will not be executed in frames with the policy disabled + // via response headers. + // - If the renderer doesn't behave correctly, there's no point in + // propagating the renderer's untrusted report about the response headers' + // value. + // + // TODO(crbug.com/1071848): Once Feature Policy headers are available from a + // trusted source, it would be good to set this depending on the headers' + // values, too. + TrustTokenRedemptionPolicy trust_token_redemption_policy = kPotentiallyPermit; }; // The |credentials| output parameter is given to URLRequest::SetAuth() @@ -729,6 +754,8 @@ interface SSLPrivateKey { // Callback interface for NetworkContext when routing identifiers aren't // available. Otherwise generally callbacks from the network service go on // NetworkServiceClient. +// +// Implemented by the browser process. interface NetworkContextClient { // Called when we receive an authentication failure. // The |auth_challenge_responder| will respond to auth challenge with @@ -821,6 +848,10 @@ interface NetworkContextClient { // Represents a distinct context for making network requests, with its own // storage (e.g. cookies and cache). +// +// NetworkContext is implemented by the network service. It is a trusted +// interface that must not be sent to an untrusted process like a renderer +// process. Only the browser process should have access to it. interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote<NetworkContextClient> client); @@ -1026,7 +1057,8 @@ interface NetworkContext { // header. [EnableIf=is_ct_supported] AddExpectCT(string host, mojo_base.mojom.Time expiry, - bool enforce, url.mojom.Url report_uri) => (bool success); + bool enforce, url.mojom.Url report_uri, + NetworkIsolationKey network_isolation_key) => (bool success); // Send a test CT report with dummy data for test purposes. [EnableIf=is_ct_supported] @@ -1035,7 +1067,9 @@ interface NetworkContext { // Retrieves the expect CT state from the associated network context // transport security state. [EnableIf=is_ct_supported] - GetExpectCTState(string domain) => (mojo_base.mojom.DictionaryValue state); + GetExpectCTState(string domain, + NetworkIsolationKey network_isolation_key) + => (mojo_base.mojom.DictionaryValue state); // Creates a UDP socket. Caller can supply a |listener| interface pointer // to listen for incoming datagrams. A null |listener| is acceptable if caller @@ -1172,12 +1206,15 @@ interface NetworkContext { // Creates a QuicTransport connection to |url|. |origin| is used for the // client indication - see // https://tools.ietf.org/html/draft-vvv-webtransport-quic-01#section-3.2 . + // When |fingerprints| is not empty, it is used to verify the certificate. + // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints // // It is recommended to detect mojo connection errors on |handshake_client|. CreateQuicTransport( url.mojom.Url url, url.mojom.Origin origin, NetworkIsolationKey network_isolation_key, + array<QuicTransportCertificateFingerprint> fingerprints, pending_remote<QuicTransportHandshakeClient> handshake_client); // Create a NetLogExporter, which helps export NetLog to an existing file. @@ -1200,7 +1237,11 @@ interface NetworkContext { NetworkIsolationKey network_isolation_key); // Creates a P2PSocketManager instance, used for WebRTC. + // |network_isolation_key| is the network cache shard to associate with the + // P2PSocketManager instance, and affects DNS cache shard used and proxy + // connection sharing. CreateP2PSocketManager( + NetworkIsolationKey network_isolation_key, pending_remote<P2PTrustedSocketManagerClient> client, pending_receiver<P2PTrustedSocketManager> trusted_socket_manager, pending_receiver<P2PSocketManager> socket_manager); @@ -1281,7 +1322,8 @@ interface NetworkContext { url.mojom.Origin source_origin, array<CorsOriginPattern> allow_patterns, array<CorsOriginPattern> block_patterns) => (); - // Sets extra CORS safelisted request headers names dynamically. + // Sets extra CORS safelisted request headers names dynamically. Headers need + // to be in lower case. SetCorsExtraSafelistedRequestHeaderNames( array<string> cors_extra_safelisted_request_header_names); diff --git a/chromium/services/network/public/mojom/network_service.mojom b/chromium/services/network/public/mojom/network_service.mojom index 565d98ad87c..6a016cc0eb0 100644 --- a/chromium/services/network/public/mojom/network_service.mojom +++ b/chromium/services/network/public/mojom/network_service.mojom @@ -42,7 +42,8 @@ struct LoadInfo { uint64 upload_size; }; -// Network service interface to the browser. +// Implemented by the browser process. The network service sends messages to +// the browser process over this interface. interface NetworkServiceClient { // Called periodically to update the client about progress of the current // loads. To avoid flooding the client, it has to ack the update before it can @@ -63,7 +64,7 @@ interface NetworkServiceClient { int32 process_id, int32 routing_id, string devtool_request_id, - array<CookieWithStatus> cookies_with_status, + array<CookieWithAccessResult> cookies_with_access_result, array<HttpRawHeaderPair> headers); // Called to send information about the cookies blocked from storage from a @@ -149,10 +150,10 @@ struct HttpAuthDynamicParams { // True if Negotiate SPNs (service principal names) should include ports // when the port isn't a standard port (80 or 443). - bool enable_negotiate_port = true; + bool enable_negotiate_port = false; // Whether NTLM V2 is enabled on POSIX platforms. No effect elsewhere. - bool ntlm_v2_enabled = false; + bool ntlm_v2_enabled = true; // The AccountManager AccountManagerget.AccountsByTypeAndFeatures on Android // when using Negotiate authentication. @@ -203,7 +204,11 @@ struct LoggingSettings { handle<platform> log_file_descriptor; }; -// Browser interface to the network service. +// Implemented by the network service. The browser process sends messages to +// the network service over this interface. +// +// This is a trusted interface that only the browser process should have access +// to. It must not be sent to any untrusted process like a renderer process. interface NetworkService { // Sets client used by all |NetworkContext|s creating by |NetworkService|. // Pending requests may hang if the |client| pipe is closed before they diff --git a/chromium/services/network/public/mojom/parsed_headers.mojom b/chromium/services/network/public/mojom/parsed_headers.mojom index c2e5624a931..01f1bf852cb 100644 --- a/chromium/services/network/public/mojom/parsed_headers.mojom +++ b/chromium/services/network/public/mojom/parsed_headers.mojom @@ -23,6 +23,9 @@ struct ParsedHeaders { // Cross-Origin-opener-Policy-Report-Only headers. CrossOriginOpenerPolicy cross_origin_opener_policy; + // The parsed value of the Origin-Isolation header. + bool origin_isolation = false; + // The parsed Accept-CH from response headers. // // If this is missing, there is no valid accept-ch header, so client hints diff --git a/chromium/services/network/public/mojom/proxy_resolving_socket.mojom b/chromium/services/network/public/mojom/proxy_resolving_socket.mojom index 2fb1a981e9d..3c5d7906820 100644 --- a/chromium/services/network/public/mojom/proxy_resolving_socket.mojom +++ b/chromium/services/network/public/mojom/proxy_resolving_socket.mojom @@ -6,6 +6,7 @@ module network.mojom; import "services/network/public/mojom/ip_endpoint.mojom"; import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom"; +import "services/network/public/mojom/network_isolation_key.mojom"; import "services/network/public/mojom/network_param.mojom"; import "services/network/public/mojom/ssl_config.mojom"; import "services/network/public/mojom/tcp_socket.mojom"; @@ -52,19 +53,25 @@ struct ProxyResolvingSocketOptions { // factory instance. interface ProxyResolvingSocketFactory { // Creates a socket connected to |url|. This connection might be done through - // proxies if any is set in system's proxy settings. On success, |result| is - // net::OK. Caller is to use |send_stream| to send data and - // |receive_stream| to receive data over the connection. On failure, |result| - // is a network error code. |local_addr| contains the local address of the - // socket. |peer_addr| contains the peer address. If socket is connected to a - // proxy, |peer_addr| will be null. + // proxies if any is set in system's proxy settings. + // + // |network_isolation_key| indicates the network storage shard to use for + // shared resources, such as the DNS cache and shared proxy connections. + // + // On success, |result| is net::OK. Caller is to use |send_stream| to send + // data and |receive_stream| to receive data over the connection. On failure, + // |result| is a network error code. |local_addr| contains the local address + // of the socket. |peer_addr| contains the peer address. If socket is + // connected to a proxy, |peer_addr| will be null. // // If socket is closed before the callback can be completed, the callback will // be invoked with net::ERR_ABORTED. // // Any sockets that are created but are yet to be destroyed will be destroyed // when the implementation of this factory goes away. - CreateProxyResolvingSocket(url.mojom.Url url, + CreateProxyResolvingSocket( + url.mojom.Url url, + NetworkIsolationKey network_isolation_key, ProxyResolvingSocketOptions? options, MutableNetworkTrafficAnnotationTag traffic_annotation, pending_receiver<ProxyResolvingSocket> socket, diff --git a/chromium/services/network/public/mojom/quic_transport.mojom b/chromium/services/network/public/mojom/quic_transport.mojom index 4ae510ce7b4..e8b4de15421 100644 --- a/chromium/services/network/public/mojom/quic_transport.mojom +++ b/chromium/services/network/public/mojom/quic_transport.mojom @@ -26,6 +26,14 @@ struct QuicTransportError { bool safe_to_report_details = false; }; +// The fingerprint of a certificate accompanied with the hash algorithm. +// https://wicg.github.io/web-transport/#quic-transport-configuration +// https://www.w3.org/TR/webrtc/#dom-rtcdtlsfingerprint +struct QuicTransportCertificateFingerprint { + string algorithm; + string fingerprint; +}; + // A mojo interface for https://wicg.github.io/web-transport/#quic-transport. interface QuicTransport { // A datagram message is sent from the client. The response message represents @@ -54,6 +62,9 @@ interface QuicTransport { // be able to write any data to the stream, but it may be able to use other // functions such as reading data from the stream. SendFin(uint32 stream_id); + + // Aborts the stream for |stream_id|. + AbortStream(uint32 stream_id, uint64 code); }; // A mojo interface for the client of QuicTransport. diff --git a/chromium/services/network/public/mojom/referrer_policy.mojom b/chromium/services/network/public/mojom/referrer_policy.mojom index 4960fafb31d..b159d7cba5e 100644 --- a/chromium/services/network/public/mojom/referrer_policy.mojom +++ b/chromium/services/network/public/mojom/referrer_policy.mojom @@ -4,10 +4,7 @@ module network.mojom; -// Don't make backwards-incompatible changes to this definition! -// It's used in PageState serialization, so backwards incompatible changes -// would cause stored PageState objects to be un-parseable. Please contact the -// page state serialization owners before making such a change. +[Stable] enum ReferrerPolicy { kAlways, kDefault, diff --git a/chromium/services/network/public/mojom/restricted_cookie_manager.mojom b/chromium/services/network/public/mojom/restricted_cookie_manager.mojom index ed16a743b5f..8e3f1982686 100644 --- a/chromium/services/network/public/mojom/restricted_cookie_manager.mojom +++ b/chromium/services/network/public/mojom/restricted_cookie_manager.mojom @@ -62,11 +62,16 @@ interface RestrictedCookieManager { // level frame or the script url for service workers. |site_for_cookies| is // used to determine if a cookie is accessed in a third-party context. // |top_frame_origin| is used to check for content settings. - // |options| filters the returned list of cookies. + // |options| filters the returned list of cookies. The returned cookies + // include CookieAccessResult information of the cookie. + // Any information contained in the CookieInclusionStatus::WarningReasons + // as part of CookieAccessResult in the included cookies may be passed to an + // untrusted renderer. GetAllForUrl( url.mojom.Url url, SiteForCookies site_for_cookies, url.mojom.Origin top_frame_origin, - CookieManagerGetOptions options) => (array<CanonicalCookie> cookies); + CookieManagerGetOptions options) => ( + array<CookieWithAccessResult> cookies); SetCanonicalCookie(CanonicalCookie cookie, url.mojom.Url url, diff --git a/chromium/services/network/public/mojom/ssl_config.mojom b/chromium/services/network/public/mojom/ssl_config.mojom index bab0033425a..f95093c16ae 100644 --- a/chromium/services/network/public/mojom/ssl_config.mojom +++ b/chromium/services/network/public/mojom/ssl_config.mojom @@ -22,7 +22,7 @@ struct SSLConfig { // If true, enables TLS 1.3 downgrade hardening for connections using local // trust anchors. (Hardening for known roots is always enabled.) - bool tls13_hardening_for_local_anchors_enabled = false; + bool tls13_hardening_for_local_anchors_enabled = true; // SSL 2.0 and 3.0 are not supported. Note these lines must be kept in sync // with net/ssl/ssl_config.cc. diff --git a/chromium/services/network/public/mojom/trust_tokens.mojom b/chromium/services/network/public/mojom/trust_tokens.mojom index bb6a209f2cd..8f599953b9f 100644 --- a/chromium/services/network/public/mojom/trust_tokens.mojom +++ b/chromium/services/network/public/mojom/trust_tokens.mojom @@ -102,14 +102,21 @@ struct TrustTokenParams { // or redeem a new token, evicting the SRR currently stored. TrustTokenRefreshPolicy refresh_policy = kUseCached; - // "sign_request_data", "include_timestamp_header", "issuer", and - // "additional_signed_headers" are used only when "type" is "kSigning": - // these parameters specify the manner in which the outgoing request should - // be signed. + // The remaining members are used only when "type" is "kSigning": these + // parameters specify the manner in which the outgoing request should be + // signed, including optionally specifying additional data to add in + // browser-provided request headers (for instance, a timestamp or custom + // client-provided data). TrustTokenSignRequestData sign_request_data = kOmit; bool include_timestamp_header = false; url.mojom.Origin? issuer; array<string> additional_signed_headers; + + // "possibly_unsafe_additional_signing_data", which stores the request's + // optional additionalSigningData Trust Tokens parameter, might not be a valid + // HTTP header value; it's the user's responsibility to ensure that it is safe + // to attach as a header prior to adding it to an outgoing request's headers. + string? possibly_unsafe_additional_signing_data; }; // Result struct for a HasTrustTokens (see below) call: diff --git a/chromium/services/network/public/mojom/url_loader.mojom b/chromium/services/network/public/mojom/url_loader.mojom index 4f2a1e56c83..b3b921ae7a5 100644 --- a/chromium/services/network/public/mojom/url_loader.mojom +++ b/chromium/services/network/public/mojom/url_loader.mojom @@ -90,7 +90,7 @@ struct TrustedUrlRequestParams { // Observer which should be notified when this URLRequest reads or writes // a cookie. If this is set to non-null, the observer passed to // URLLoaderFactory will be ignored. - CookieAccessObserver? cookie_observer; + pending_remote<CookieAccessObserver>? cookie_observer; }; // Typemapped to network::ResourceRequest. @@ -355,7 +355,7 @@ struct URLRequest { // practical terms, it's empty for requests that didn't go through a service // worker, or if the original requestor is not a window. When the request // goes through a service worker, the id is - // ServiceWorkerProviderHost::fetch_request_window_id. + // ServiceWorkerContainerHost::fetch_request_window_id. mojo_base.mojom.UnguessableToken? fetch_window_id; // The ID that DevTools uses to track network requests. It is generated in the @@ -402,6 +402,10 @@ struct URLRequestBody { // Indicates whether the post data contains sensitive information like // passwords. bool contains_sensitive_info; + + // Indicates whether fetch upload streaming is allowed/rejected over H/1. + // Even if this is false but there is a QUIC/H2 stream, the upload is allowed. + bool allow_http1_for_streaming_upload; }; // Represents part of an upload body. This could be either one of bytes, file or @@ -429,7 +433,13 @@ struct DataElement { mojo_base.mojom.Time expected_modification_time; }; +// URLLoader is an interface for performing a single request to a URL. +// // Destroying a URLLoader will cancel the associated request. +// +// A URLLoader is normally created and started using +// URLLoaderFactory::CreateLoaderAndStart(). The result of the load is +// communicated to the URLLoaderClient provided to that function. interface URLLoader { // If a disconnection is initiated by the client side, it may send the // following disconnection reason, along with an application-defined string @@ -480,6 +490,7 @@ interface URLLoader { ResumeReadingBodyFromNet(); }; +// Receives messages from a single URLLoader. interface URLLoaderClient { // Called when the response head is received. OnReceiveResponse(URLResponseHead head); diff --git a/chromium/services/network/public/mojom/url_loader_factory.mojom b/chromium/services/network/public/mojom/url_loader_factory.mojom index 3035dcf148d..8fecf02d4ff 100644 --- a/chromium/services/network/public/mojom/url_loader_factory.mojom +++ b/chromium/services/network/public/mojom/url_loader_factory.mojom @@ -29,16 +29,23 @@ const uint32 kURLLoadOptionBlockThirdPartyCookies = 64; // service must set this. const uint32 kURLLoadOptionAsCorsPreflight = 128; +// URLLoaderFactory is an interface for requesting URLs. It creates URLLoader +// instances. One URLLoader instance can load one URL. interface URLLoaderFactory { // Creates a URLLoader and starts loading with the given |request|. |client|'s - // method will be called when certain events related to that loading + // methods will be called when certain events related to that loading // (e.g., response arrival) happen. + // // |routing_id| is the routing_id for subframe requests, and is the // frame_tree_node_id for frame requests. // TODO: once MojoLoading is only codepath and we have one factory per frame, // remove this. - // |request_id| is for compatibility with the existing Chrome IPC. - // A pair (routing_id, request_id) is assumed to be unique within one factory. + // + // |request_id| is an arbitrary id for the request. The (routing_id, + // request_id) pair must be unique over all calls to CreateLoaderAndStart() + // on this factory. + // + // |options| is a bitfield of the options defined above. CreateLoaderAndStart(pending_receiver<URLLoader> loader, int32 routing_id, int32 request_id, diff --git a/chromium/services/network/public/mojom/url_response_head.mojom b/chromium/services/network/public/mojom/url_response_head.mojom index b92cd66790e..0e4afd3eb8d 100644 --- a/chromium/services/network/public/mojom/url_response_head.mojom +++ b/chromium/services/network/public/mojom/url_response_head.mojom @@ -53,9 +53,11 @@ struct URLResponseHead { // True if the request accessed the network in the process of retrieving data. bool network_accessed = false; - // The appcache this response was loaded from, or kAppCacheNoCacheId. + // The appcache this response was loaded from, or + // kAppCacheNoCacheId (hard-coded as 0 because services/network + // can't take a dependency on Blink). // TODO(rdsmith): Remove conceptual dependence on appcache. - int64 appcache_id; + int64 appcache_id = 0; // The manifest url of the appcache this response was loaded from. // Note: this value is only populated for main resource requests. @@ -74,7 +76,7 @@ struct URLResponseHead { bool was_fetched_via_spdy = false; // True if the response was delivered after NPN is negotiated. - bool was_alpn_negotiated; + bool was_alpn_negotiated = false; // True if response could use alternate protocol. However, browser will // ignore the alternate protocol when spdy is not enabled on browser side. @@ -103,11 +105,21 @@ struct URLResponseHead { // reissued by the renderer). bool was_fetched_via_service_worker = false; + // Source of a serviceworker response - CacheStorage, HttpCache, Network or + // Unspecified (default). + // The value can be Unspecified in some cases including: + // 1. |was_fetched_via_service_worker| is false. + // 2. The service worker responded with an error (response status code is 0). + // 3. The service worker responded with a generated response, i.e., + // `respondWith(new Response(...))`. + FetchResponseSource service_worker_response_source = + FetchResponseSource.kUnspecified; + // True when a request whose mode is |CORS| or |CORS-with-forced-preflight| // is sent to a ServiceWorker but FetchEvent.respondWith is not called. So the // renderer has to resend the request with skip service worker flag // considering the CORS preflight logic. - bool was_fallback_required_by_service_worker; + bool was_fallback_required_by_service_worker = false; // The URL list of the Response object the service worker passed to // respondWith() to create this response. For example, if the service worker @@ -120,11 +132,13 @@ struct URLResponseHead { array<url.mojom.Url> url_list_via_service_worker; // https://fetch.spec.whatwg.org/#concept-response-type - FetchResponseType response_type; - - // True when the response is served from the CacheStorage via the - // ServiceWorker. - bool is_in_cache_storage = false; + // + // TODO(falken): Initialize to kDefault per spec, if that doesn't + // break things. Previously this field was not being initialized + // explicitly, so it was zero-initializing which happens to be the + // kBasic type. So changing it to kDefault could potentially change + // behavior. + FetchResponseType response_type = FetchResponseType.kBasic; // The cache name of the CacheStorage from where the response is served via // the ServiceWorker. Empty if the response isn't from the CacheStorage. diff --git a/chromium/services/network/public/mojom/websocket.mojom b/chromium/services/network/public/mojom/websocket.mojom index 6972175e969..a9f92f3fb49 100644 --- a/chromium/services/network/public/mojom/websocket.mojom +++ b/chromium/services/network/public/mojom/websocket.mojom @@ -94,12 +94,6 @@ interface WebSocketClient { WebSocketMessageType type, uint64 data_length); - // Add |quota| bytes of send quota. |quota| must be positive. Initial quota - // is 0. The renderer will wait for an AddSendFlowControlQuota() message - // before forwarding any messages to the browser. Total quota must never - // exceed 0x7FFFFFFFFFFFFFFF bytes. - AddSendFlowControlQuota(int64 quota); - // Drop the channel. // // When sent by the renderer, this will cause a Close message will be sent and @@ -129,19 +123,6 @@ interface WebSocket { const uint32 kInsufficientResources = 1; const uint32 kInternalFailure = 2; - // Send a non-control frame to the remote server. - // - |fin| indicates that this frame is the last in the current message. - // - |type| is the type of the message. On the first frame of a message, it - // must be set to either WebSocketMessageType.TEXT or - // WebSocketMessageType.BINARY. On subsequent frames, it must be set to - // WebSocketMessageType.CONTINUATION, and the type is the same as that of - // the first message. If |type| is WebSocketMessageType.TEXT, then the - // concatenation of the |data| from every frame in the message must be valid - // UTF-8. If |fin| is not set, |data| must be non-empty. - SendFrame(bool fin, - WebSocketMessageType type, - mojo_base.mojom.ReadOnlyBuffer data); - // Sends a message via mojo datapipe to the remote server. // - |type| is the type of the message. It must be set to either // WebSocketMessageType.TEXT or WebSocketMessageType.BINARY. diff --git a/chromium/services/network/quic_transport.cc b/chromium/services/network/quic_transport.cc index 2a5e6d2561e..0ef85a5b839 100644 --- a/chromium/services/network/quic_transport.cc +++ b/chromium/services/network/quic_transport.cc @@ -19,6 +19,23 @@ namespace network { +namespace { + +net::QuicTransportClient::Parameters CreateParameters( + const std::vector<mojom::QuicTransportCertificateFingerprintPtr>& + fingerprints) { + net::QuicTransportClient::Parameters params; + + for (const auto& fingerprint : fingerprints) { + params.server_certificate_fingerprints.push_back( + quic::CertificateFingerprint{.algorithm = fingerprint->algorithm, + .fingerprint = fingerprint->fingerprint}); + } + return params; +} + +} // namespace + class QuicTransport::Stream final { public: class StreamVisitor final : public quic::QuicTransportStream::Visitor { @@ -118,6 +135,19 @@ class QuicTransport::Stream final { MaySendFin(); } + void Abort(quic::QuicRstStreamErrorCode code) { + auto* stream = incoming_ ? incoming_ : outgoing_; + if (!stream) { + return; + } + stream->Reset(code); + incoming_ = nullptr; + outgoing_ = nullptr; + readable_watcher_.Cancel(); + readable_.reset(); + MayDisposeLater(); + } + ~Stream() { transport_->transport_->session()->CloseStream(id_); } private: @@ -278,12 +308,14 @@ class QuicTransport::Stream final { // This must be the last member. base::WeakPtrFactory<Stream> weak_factory_{this}; -}; +}; // namespace network QuicTransport::QuicTransport( const GURL& url, const url::Origin& origin, const net::NetworkIsolationKey& key, + const std::vector<mojom::QuicTransportCertificateFingerprintPtr>& + fingerprints, NetworkContext* context, mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client) : transport_(std::make_unique<net::QuicTransportClient>( @@ -291,7 +323,8 @@ QuicTransport::QuicTransport( origin, this, key, - context->url_request_context())), + context->url_request_context(), + CreateParameters(fingerprints))), context_(context), receiver_(this), handshake_client_(std::move(handshake_client)) { @@ -390,6 +423,18 @@ void QuicTransport::SendFin(uint32_t stream) { it->second->NotifyFinFromClient(); } +void QuicTransport::AbortStream(uint32_t stream, uint64_t code) { + auto it = streams_.find(stream); + if (it == streams_.end()) { + return; + } + auto code_to_pass = quic::QuicRstStreamErrorCode::QUIC_STREAM_NO_ERROR; + if (code < quic::QuicRstStreamErrorCode::QUIC_STREAM_LAST_ERROR) { + code_to_pass = static_cast<quic::QuicRstStreamErrorCode>(code); + } + it->second->Abort(code_to_pass); +} + void QuicTransport::OnConnected() { if (torn_down_) { return; @@ -413,12 +458,9 @@ void QuicTransport::OnConnectionFailed() { DCHECK(handshake_client_); - const net::QuicTransportError& error = transport_->error(); // Here we assume that the error is not going to handed to the // initiator renderer. - handshake_client_->OnHandshakeFailed(mojom::QuicTransportError::New( - error.net_error, static_cast<int>(error.quic_error), error.details, - error.safe_to_report_details)); + handshake_client_->OnHandshakeFailed(transport_->error()); TearDown(); } diff --git a/chromium/services/network/quic_transport.h b/chromium/services/network/quic_transport.h index 4dc8c6e5e2b..62031970fd1 100644 --- a/chromium/services/network/quic_transport.h +++ b/chromium/services/network/quic_transport.h @@ -43,12 +43,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) QuicTransport final mojo::ScopedDataPipeProducerHandle)>; using UnidirectionalStreamAcceptanceCallback = base::OnceCallback<void(uint32_t, mojo::ScopedDataPipeConsumerHandle)>; - QuicTransport(const GURL& url, - const url::Origin& origin, - const net::NetworkIsolationKey& key, - NetworkContext* context, - mojo::PendingRemote<mojom::QuicTransportHandshakeClient> - handshake_client); + QuicTransport( + const GURL& url, + const url::Origin& origin, + const net::NetworkIsolationKey& key, + const std::vector<mojom::QuicTransportCertificateFingerprintPtr>& + fingerprints, + NetworkContext* context, + mojo::PendingRemote<mojom::QuicTransportHandshakeClient> + handshake_client); ~QuicTransport() override; // mojom::QuicTransport implementation: @@ -62,6 +65,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) QuicTransport final void AcceptUnidirectionalStream( UnidirectionalStreamAcceptanceCallback callback) override; void SendFin(uint32_t stream_id) override; + void AbortStream(uint32_t stream_id, uint64_t code) override; // net::QuicTransportClient::Visitor implementation: void OnConnected() override; diff --git a/chromium/services/network/quic_transport_unittest.cc b/chromium/services/network/quic_transport_unittest.cc index 4bfedb17689..78cb99af781 100644 --- a/chromium/services/network/quic_transport_unittest.cc +++ b/chromium/services/network/quic_transport_unittest.cc @@ -13,16 +13,64 @@ #include "base/test/task_environment.h" #include "net/cert/mock_cert_verifier.h" #include "net/dns/mock_host_resolver.h" +#include "net/quic/crypto/proof_source_chromium.h" +#include "net/test/test_data_directory.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" #include "net/tools/quic/quic_transport_simple_server.h" #include "net/url_request/url_request_context.h" #include "services/network/network_context.h" #include "services/network/network_service.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { namespace { +// A clock that only mocks out WallNow(), but uses real Now() and +// ApproximateNow(). Useful for certificate verification. +class TestWallClock : public quic::QuicClock { + public: + quic::QuicTime Now() const override { + return quic::QuicChromiumClock::GetInstance()->Now(); + } + quic::QuicTime ApproximateNow() const override { + return quic::QuicChromiumClock::GetInstance()->ApproximateNow(); + } + quic::QuicWallTime WallNow() const override { return wall_now_; } + + void set_wall_now(quic::QuicWallTime now) { wall_now_ = now; } + + private: + quic::QuicWallTime wall_now_ = quic::QuicWallTime::Zero(); +}; + +class TestConnectionHelper : public quic::QuicConnectionHelperInterface { + public: + const quic::QuicClock* GetClock() const override { return &clock_; } + quic::QuicRandom* GetRandomGenerator() override { + return quic::QuicRandom::GetInstance(); + } + quic::QuicBufferAllocator* GetStreamSendBufferAllocator() override { + return &allocator_; + } + + TestWallClock& clock() { return clock_; } + + private: + TestWallClock clock_; + quic::SimpleBufferAllocator allocator_; +}; + +mojom::NetworkContextParamsPtr CreateNetworkContextParams() { + auto context_params = mojom::NetworkContextParams::New(); + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test CertVerifier behavior. + context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); + return context_params; +} + // We don't use mojo::BlockingCopyToString because it leads to deadlocks. std::string Read(mojo::ScopedDataPipeConsumerHandle readable) { std::string output; @@ -69,7 +117,8 @@ class TestHandshakeClient final : public mojom::QuicTransportHandshakeClient { std::move(callback_).Run(); } - void OnHandshakeFailed(mojom::QuicTransportErrorPtr error) override { + void OnHandshakeFailed( + const base::Optional<net::QuicTransportError>& error) override { has_seen_handshake_failure_ = true; receiver_.reset(); std::move(callback_).Run(); @@ -186,6 +235,9 @@ quic::ParsedQuicVersion GetTestVersion() { class QuicTransportTest : public testing::Test { public: QuicTransportTest() + : QuicTransportTest( + quic::test::crypto_test_utils::ProofSourceForTesting()) {} + explicit QuicTransportTest(std::unique_ptr<quic::ProofSource> proof_source) : version_(GetTestVersion()), origin_(url::Origin::Create(GURL("https://example.org/"))), task_environment_(base::test::TaskEnvironment::MainThreadType::IO), @@ -193,10 +245,8 @@ class QuicTransportTest : public testing::Test { network_context_remote_(mojo::NullRemote()), network_context_(network_service_.get(), network_context_remote_.BindNewPipeAndPassReceiver(), - mojom::NetworkContextParams::New()), - server_(/* port= */ 0, - {origin_}, - quic::test::crypto_test_utils::ProofSourceForTesting()) { + CreateNetworkContextParams()), + server_(/* port= */ 0, {origin_}, std::move(proof_source)) { EXPECT_EQ(EXIT_SUCCESS, server_.Start()); cert_verifier_.set_default_result(net::OK); @@ -215,20 +265,31 @@ class QuicTransportTest : public testing::Test { const GURL& url, const url::Origin& origin, const net::NetworkIsolationKey& key, + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints, mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client) { - network_context_.CreateQuicTransport(url, origin, key, - std::move(handshake_client)); + network_context_.CreateQuicTransport( + url, origin, key, std::move(fingerprints), std::move(handshake_client)); } void CreateQuicTransport( const GURL& url, const url::Origin& origin, mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client) { - CreateQuicTransport(url, origin, net::NetworkIsolationKey(), + CreateQuicTransport(url, origin, net::NetworkIsolationKey(), {}, std::move(handshake_client)); } + void CreateQuicTransport( + const GURL& url, + const url::Origin& origin, + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints, + mojo::PendingRemote<mojom::QuicTransportHandshakeClient> + handshake_client) { + CreateQuicTransport(url, origin, net::NetworkIsolationKey(), + std::move(fingerprints), std::move(handshake_client)); + } + GURL GetURL(base::StringPiece suffix) { return GURL(quiche::QuicheStrCat("quic-transport://test.example.com:", server_.server_address().port(), suffix)); @@ -236,6 +297,7 @@ class QuicTransportTest : public testing::Test { const url::Origin& origin() const { return origin_; } const NetworkContext& network_context() const { return network_context_; } + NetworkContext& mutable_network_context() { return network_context_; } void RunPendingTasks() { base::RunLoop run_loop; @@ -509,5 +571,83 @@ TEST_F(QuicTransportTest, EchoOnBidirectionalStream) { EXPECT_TRUE(client.stream_is_closed_as_incoming_stream(stream_id)); } +class QuicTransportWithCustomCertificateTest : public QuicTransportTest { + public: + QuicTransportWithCustomCertificateTest() + : QuicTransportTest(CreateProofSource()) { + auto helper = std::make_unique<TestConnectionHelper>(); + // Set clock to a time in which quic-short-lived.pem is valid + // (2020-06-05T20:35:00.000Z). + helper->clock().set_wall_now( + quic::QuicWallTime::FromUNIXSeconds(1591389300)); + mutable_network_context() + .url_request_context() + ->quic_context() + ->SetHelperForTesting(std::move(helper)); + } + ~QuicTransportWithCustomCertificateTest() override = default; + + static std::unique_ptr<quic::ProofSource> CreateProofSource() { + auto proof_source = std::make_unique<net::ProofSourceChromium>(); + base::FilePath certs_dir = net::GetTestCertsDirectory(); + EXPECT_TRUE(proof_source->Initialize( + certs_dir.AppendASCII("quic-short-lived.pem"), + certs_dir.AppendASCII("quic-leaf-cert.key"), + certs_dir.AppendASCII("quic-leaf-cert.key.sct"))); + return proof_source; + } +}; + +TEST_F(QuicTransportWithCustomCertificateTest, WithValidFingerprint) { + base::RunLoop run_loop_for_handshake; + mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client; + TestHandshakeClient test_handshake_client( + handshake_client.InitWithNewPipeAndPassReceiver(), + run_loop_for_handshake.QuitClosure()); + + auto fingerprint = mojom::QuicTransportCertificateFingerprint::New( + "sha-256", + "ED:3D:D7:C3:67:10:94:68:D1:DC:D1:26:5C:B2:74:D7:1C:" + "A2:63:3E:94:94:C0:84:39:D6:64:FA:08:B9:77:37"); + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints; + fingerprints.push_back(std::move(fingerprint)); + + CreateQuicTransport(GetURL("/discard"), origin(), std::move(fingerprints), + std::move(handshake_client)); + + run_loop_for_handshake.Run(); + + EXPECT_TRUE(test_handshake_client.has_seen_connection_establishment()); + EXPECT_FALSE(test_handshake_client.has_seen_handshake_failure()); + EXPECT_FALSE(test_handshake_client.has_seen_mojo_connection_error()); + EXPECT_EQ(1u, network_context().NumOpenQuicTransports()); +} + +TEST_F(QuicTransportWithCustomCertificateTest, WithInvalidFingerprint) { + base::RunLoop run_loop_for_handshake; + mojo::PendingRemote<mojom::QuicTransportHandshakeClient> handshake_client; + TestHandshakeClient test_handshake_client( + handshake_client.InitWithNewPipeAndPassReceiver(), + run_loop_for_handshake.QuitClosure()); + + auto fingerprint = network::mojom::QuicTransportCertificateFingerprint::New( + "sha-256", + "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:" + "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"); + + std::vector<mojom::QuicTransportCertificateFingerprintPtr> fingerprints; + fingerprints.push_back(std::move(fingerprint)); + + CreateQuicTransport(GetURL("/discard"), origin(), std::move(fingerprints), + std::move(handshake_client)); + + run_loop_for_handshake.Run(); + + EXPECT_FALSE(test_handshake_client.has_seen_connection_establishment()); + EXPECT_TRUE(test_handshake_client.has_seen_handshake_failure()); + EXPECT_FALSE(test_handshake_client.has_seen_mojo_connection_error()); + EXPECT_EQ(0u, network_context().NumOpenQuicTransports()); +} + } // namespace } // namespace network diff --git a/chromium/services/network/restricted_cookie_manager.cc b/chromium/services/network/restricted_cookie_manager.cc index 80c2d2c6e12..888cc78e436 100644 --- a/chromium/services/network/restricted_cookie_manager.cc +++ b/chromium/services/network/restricted_cookie_manager.cc @@ -84,9 +84,32 @@ net::CookieOptions MakeOptionsForGet( return options; } -} // namespace +void MarkSameSiteCompatPairs(std::vector<net::CookieWithStatus>& cookie_list, + const net::CookieOptions& options) { + // If the context is same-site then there cannot be any SameSite-by-default + // warnings, so the compat pair warning is irrelevant. + if (options.same_site_cookie_context().GetContextForCookieInclusion() > + net::CookieOptions::SameSiteCookieContext::ContextType:: + SAME_SITE_LAX_METHOD_UNSAFE) { + return; + } + if (cookie_list.size() < 2) + return; + for (size_t i = 0; i < cookie_list.size() - 1; ++i) { + const net::CanonicalCookie& c1 = cookie_list[i].cookie; + for (size_t j = i + 1; j < cookie_list.size(); ++j) { + const net::CanonicalCookie& c2 = cookie_list[j].cookie; + if (net::cookie_util::IsSameSiteCompatPair(c1, c2, options)) { + cookie_list[i].status.AddWarningReason( + net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR); + cookie_list[j].status.AddWarningReason( + net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR); + } + } + } +} -using CookieInclusionStatus = net::CanonicalCookie::CookieInclusionStatus; +} // namespace class RestrictedCookieManager::Listener : public base::LinkNode<Listener> { public: @@ -129,7 +152,7 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!change.cookie .IncludeForRequestURL(url_, options_, change.access_semantics) - .IsInclude()) { + .status.IsInclude()) { return; } @@ -238,22 +261,28 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( const net::CookieOptions& net_options, mojom::CookieManagerGetOptionsPtr options, GetAllForUrlCallback callback, - const net::CookieStatusList& cookie_list, - const net::CookieStatusList& excluded_cookies) { + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bool blocked = !cookie_settings_->IsCookieAccessAllowed( url, site_for_cookies.RepresentativeUrl(), top_frame_origin); - std::vector<net::CanonicalCookie> result; + std::vector<net::CookieWithAccessResult> result; std::vector<net::CookieWithStatus> result_with_status; // TODO(https://crbug.com/977040): Remove once samesite tightening up is // rolled out. - for (const auto& cookie_and_status : excluded_cookies) { - if (cookie_and_status.status.ShouldWarn()) { + // |result_with_status| is populated with excluded cookies here based on + // warnings present before WARN_SAMESITE_COMPAT_PAIR can be applied by + // MarkSameSiteCompatPairs(). This is ok because WARN_SAMESITE_COMPAT_PAIR is + // irrelevant unless WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT is already + // present. + for (const auto& cookie_and_access_result : excluded_cookies) { + if (cookie_and_access_result.access_result.status.ShouldWarn()) { result_with_status.push_back( - {cookie_and_status.cookie, cookie_and_status.status}); + {cookie_and_access_result.cookie, + cookie_and_access_result.access_result.status}); } } @@ -262,9 +291,9 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( mojom::CookieMatchType match_type = options->match_type; const std::string& match_name = options->name; // TODO(https://crbug.com/993843): Use the statuses passed in |cookie_list|. - for (size_t i = 0; i < cookie_list.size(); ++i) { - const net::CanonicalCookie& cookie = cookie_list[i].cookie; - CookieInclusionStatus status = cookie_list[i].status; + for (const net::CookieWithAccessResult& cookie_item : cookie_list) { + const net::CanonicalCookie& cookie = cookie_item.cookie; + net::CookieInclusionStatus status = cookie_item.access_result.status; const std::string& cookie_name = cookie.Name(); if (match_type == mojom::CookieMatchType::EQUALS) { @@ -281,14 +310,18 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( if (blocked) { status.AddExclusionReason( - CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); + net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); } else { - result.push_back(cookie); + result.push_back(cookie_item); } result_with_status.push_back({cookie, status}); } if (cookie_observer_) { + // Mark the CookieInclusionStatuses of items in |result_with_status| if they + // are part of a presumed SameSite compatibility pair. + MarkSameSiteCompatPairs(result_with_status, net_options); + cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New( mojom::CookieAccessDetails::Type::kRead, url, site_for_cookies, result_with_status, base::nullopt)); @@ -319,21 +352,24 @@ void RestrictedCookieManager::SetCanonicalCookie( bool blocked = !cookie_settings_->IsCookieAccessAllowed( url, site_for_cookies.RepresentativeUrl(), top_frame_origin); - CookieInclusionStatus status; + net::CookieInclusionStatus status; if (blocked) - status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); + status.AddExclusionReason( + net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); // Don't allow URLs with leading dots like https://.some-weird-domain.com // This probably never happens. if (!net::cookie_util::DomainIsHostOnly(url.host())) - status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN); + status.AddExclusionReason( + net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN); // Don't allow setting cookies on other domains. // TODO(crbug.com/996786): This should never happen. This should eventually // result in a renderer kill, but for now just log metrics. bool domain_match = cookie.IsDomainMatch(url.host()); if (!domain_match) - status.AddExclusionReason(CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH); + status.AddExclusionReason( + net::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH); UMA_HISTOGRAM_BOOLEAN( "Net.RestrictedCookieManager.SetCanonicalCookieDomainMatch", domain_match); @@ -384,12 +420,12 @@ void RestrictedCookieManager::SetCanonicalCookieResult( const net::CanonicalCookie& cookie, const net::CookieOptions& net_options, SetCanonicalCookieCallback user_callback, - net::CanonicalCookie::CookieInclusionStatus status) { + net::CookieInclusionStatus status) { std::vector<net::CookieWithStatus> notify; // TODO(https://crbug.com/977040): Only report pure INCLUDE once samesite // tightening up is rolled out. DCHECK(!status.HasExclusionReason( - CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)); + net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)); if (status.IsInclude() || status.ShouldWarn()) { if (cookie_observer_) { @@ -474,7 +510,7 @@ void RestrictedCookieManager::GetCookiesString( std::move(match_options), base::BindOnce( [](GetCookiesStringCallback user_callback, - const std::vector<net::CanonicalCookie>& cookies) { + const std::vector<net::CookieWithAccessResult>& cookies) { std::move(user_callback) .Run(net::CanonicalCookie::BuildCookieLine(cookies)); }, diff --git a/chromium/services/network/restricted_cookie_manager.h b/chromium/services/network/restricted_cookie_manager.h index 20cdccd5836..1b0b50812e5 100644 --- a/chromium/services/network/restricted_cookie_manager.h +++ b/chromium/services/network/restricted_cookie_manager.h @@ -16,6 +16,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_change_dispatcher.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_store.h" #include "net/cookies/site_for_cookies.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" @@ -111,18 +112,17 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager const net::CookieOptions& net_options, mojom::CookieManagerGetOptionsPtr options, GetAllForUrlCallback callback, - const net::CookieStatusList& cookie_list, - const net::CookieStatusList& excluded_cookies); + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies); // Reports the result of setting the cookie to |network_context_client_|, and // invokes the user callback. - void SetCanonicalCookieResult( - const GURL& url, - const net::SiteForCookies& site_for_cookies, - const net::CanonicalCookie& cookie, - const net::CookieOptions& net_options, - SetCanonicalCookieCallback user_callback, - net::CanonicalCookie::CookieInclusionStatus status); + void SetCanonicalCookieResult(const GURL& url, + const net::SiteForCookies& site_for_cookies, + const net::CanonicalCookie& cookie, + const net::CookieOptions& net_options, + SetCanonicalCookieCallback user_callback, + net::CookieInclusionStatus status); // Called when the Mojo pipe associated with a listener is closed. void RemoveChangeListener(Listener* listener); diff --git a/chromium/services/network/restricted_cookie_manager_unittest.cc b/chromium/services/network/restricted_cookie_manager_unittest.cc index 9b5e887a703..36db8313971 100644 --- a/chromium/services/network/restricted_cookie_manager_unittest.cc +++ b/chromium/services/network/restricted_cookie_manager_unittest.cc @@ -12,11 +12,11 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/functions.h" #include "net/base/features.h" #include "net/cookies/canonical_cookie_test_helpers.h" #include "net/cookies/cookie_constants.h" @@ -44,16 +44,14 @@ class RecordingCookieObserver : public network::mojom::CookieAccessObserver { // The vector is merely to permit use of MatchesCookieLine, there is always // one thing. std::vector<net::CanonicalCookie> cookie; - net::CanonicalCookie::CookieInclusionStatus status; + net::CookieInclusionStatus status; base::Optional<std::string> devtools_request_id; }; RecordingCookieObserver() = default; ~RecordingCookieObserver() override = default; - const std::vector<CookieOp>& recorded_activity() const { - return recorded_activity_; - } + std::vector<CookieOp>& recorded_activity() { return recorded_activity_; } mojo::PendingRemote<mojom::CookieAccessObserver> GetRemote() { mojo::PendingRemote<mojom::CookieAccessObserver> remote; @@ -94,6 +92,7 @@ class RestrictedCookieManagerSync { : cookie_service_(cookie_service) {} ~RestrictedCookieManagerSync() {} + // Wraps GetAllForUrl() but discards CookieAccessResult from returned cookies. std::vector<net::CanonicalCookie> GetAllForUrl( const GURL& url, const GURL& site_for_cookies, @@ -105,8 +104,30 @@ class RestrictedCookieManagerSync { url, net::SiteForCookies::FromUrl(site_for_cookies), top_frame_origin, std::move(options), base::BindLambdaForTesting( - [&run_loop, - &result](const std::vector<net::CanonicalCookie>& backend_result) { + [&run_loop, &result](const std::vector<net::CookieWithAccessResult>& + backend_result) { + result = net::cookie_util::StripAccessResults(backend_result); + run_loop.Quit(); + })); + run_loop.Run(); + return result; + } + + // Returns full CookieWithAccessResult from the backend. + // TODO(chlily): Convert calls to the above method to this one. + std::vector<net::CookieWithAccessResult> GetAllForUrlWithAccessResult( + const GURL& url, + const GURL& site_for_cookies, + const url::Origin& top_frame_origin, + mojom::CookieManagerGetOptionsPtr options) { + base::RunLoop run_loop; + std::vector<net::CookieWithAccessResult> result; + cookie_service_->GetAllForUrl( + url, net::SiteForCookies::FromUrl(site_for_cookies), top_frame_origin, + std::move(options), + base::BindLambdaForTesting( + [&run_loop, &result](const std::vector<net::CookieWithAccessResult>& + backend_result) { result = backend_result; run_loop.Quit(); })); @@ -170,13 +191,12 @@ class RestrictedCookieManagerTest ~RestrictedCookieManagerTest() override {} void SetUp() override { - mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating( + mojo::SetDefaultProcessErrorHandler(base::BindRepeating( &RestrictedCookieManagerTest::OnBadMessage, base::Unretained(this))); } void TearDown() override { - mojo::core::SetDefaultProcessErrorCallback( - mojo::core::ProcessErrorCallback()); + mojo::SetDefaultProcessErrorHandler(base::NullCallback()); } // Set a canonical cookie directly into the store. @@ -184,17 +204,31 @@ class RestrictedCookieManagerTest bool SetCanonicalCookie(const net::CanonicalCookie& cookie, std::string source_scheme, bool can_modify_httponly) { - net::ResultSavingCookieCallback<net::CanonicalCookie::CookieInclusionStatus> - callback; + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; net::CookieOptions options; if (can_modify_httponly) options.set_include_httponly(); cookie_monster_.SetCanonicalCookieAsync( std::make_unique<net::CanonicalCookie>(cookie), net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, - base::BindOnce(&net::ResultSavingCookieCallback< - net::CanonicalCookie::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback))); + callback.WaitUntilDone(); + return callback.result().IsInclude(); + } + + // Set a canonical cookie directly into the store. + // Uses a cookie options that will succeed at setting any cookie. + bool EnsureSetCanonicalCookie(const net::CanonicalCookie& cookie) { + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; + cookie_monster_.SetCanonicalCookieAsync( + std::make_unique<net::CanonicalCookie>(cookie), + net::cookie_util::SimulatedCookieSource(cookie, "https"), + net::CookieOptions::MakeAllInclusive(), + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback))); callback.WaitUntilDone(); return callback.result().IsInclude(); } @@ -243,8 +277,7 @@ class RestrictedCookieManagerTest received_bad_message_ = true; } - const std::vector<RecordingCookieObserver::CookieOp>& recorded_activity() - const { + std::vector<RecordingCookieObserver::CookieOp>& recorded_activity() { return recording_client_.recorded_activity(); } @@ -483,8 +516,7 @@ TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicy) { net::MatchesCookieLine("cookie-name=cookie-value")); EXPECT_TRUE( recorded_activity()[1].status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_USER_PREFERENCES})); + {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); } TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) { @@ -530,8 +562,7 @@ TEST_P(RestrictedCookieManagerTest, GetAllForUrlPolicyWarnActual) { net::MatchesCookieLine("cookie-name=cookie-value")); EXPECT_TRUE( recorded_activity()[0].status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_SAMESITE_NONE_INSECURE})); + {net::CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE})); } TEST_P(RestrictedCookieManagerTest, SetCanonicalCookie) { @@ -596,7 +627,7 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookieValidateDomain) { url::Origin::Create(GURL("https://example.com")))); ASSERT_EQ(1u, recorded_activity().size()); EXPECT_TRUE(recorded_activity()[0].status.HasExclusionReason( - net::CanonicalCookie::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH)); + net::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH)); auto options = mojom::CookieManagerGetOptions::New(); options->name = "cookie"; @@ -708,8 +739,7 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookiePolicy) { EXPECT_THAT(recorded_activity()[1].cookie, net::MatchesCookieLine("A2=B2")); EXPECT_TRUE( recorded_activity()[1].status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_USER_PREFERENCES})); + {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); // Read back, in first-party context auto options = mojom::CookieManagerGetOptions::New(); @@ -755,7 +785,7 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookiePolicyWarnActual) { EXPECT_THAT(recorded_activity()[0].cookie, net::MatchesCookieLine("A=B")); EXPECT_TRUE( recorded_activity()[0].status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: + {net::CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); } @@ -857,6 +887,233 @@ TEST_P(RestrictedCookieManagerTest, SameSiteCookiesSpecialScheme) { EXPECT_THAT(cookies, testing::SizeIs(0)); } +// Test that the WARN_SAMESITE_COMPAT_PAIR warning is applied correctly to +// CookieInclusionStatuses passed to the CookieAccessObserver, +// but does not get into the cookies returned to the renderer. +TEST_P(RestrictedCookieManagerTest, SameSiteCompatPairWarning_AppliedOnGet) { + // Set cookies directly into the store that form a compat pair. + ASSERT_TRUE(EnsureSetCanonicalCookie(net::CanonicalCookie( + "name", "value", "example.com", "/", base::Time(), base::Time(), + base::Time(), /* secure = */ true, + /* httponly = */ false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT))); + ASSERT_TRUE(EnsureSetCanonicalCookie(net::CanonicalCookie( + "name_legacy", "value", "example.com", "/", base::Time(), base::Time(), + base::Time(), /* secure = */ true, + /* httponly = */ false, net::CookieSameSite::UNSPECIFIED, + net::COOKIE_PRIORITY_DEFAULT))); + + // Get cookies from the RestrictedCookieManager in a same-site context (should + // not trigger warnings). + { + auto options = mojom::CookieManagerGetOptions::New(); + options->name = "name"; + options->match_type = mojom::CookieMatchType::STARTS_WITH; + + std::vector<net::CookieWithAccessResult> cookies = + sync_service_->GetAllForUrlWithAccessResult( + GURL("https://example.com/test/"), GURL("https://example.com"), + url::Origin::Create(GURL("https://example.com")), + std::move(options)); + + ASSERT_THAT(cookies, testing::SizeIs(2)); + EXPECT_EQ("name", cookies[0].cookie.Name()); + EXPECT_EQ("name_legacy", cookies[1].cookie.Name()); + + // No warning is applied to returned cookies. + EXPECT_EQ(net::CookieInclusionStatus(), cookies[0].access_result.status); + EXPECT_EQ(net::CookieInclusionStatus(), cookies[1].access_result.status); + } + + // No warning is applied to the CookieInclusionStatuses passed to the + // CookieAccessObserver. + ASSERT_EQ(2u, recorded_activity().size()); + EXPECT_EQ(recorded_activity()[0].get, true); + EXPECT_EQ(recorded_activity()[0].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[0].site_for_cookies, "https://example.com/"); + EXPECT_THAT(recorded_activity()[0].cookie, + net::MatchesCookieLine("name=value")); + EXPECT_EQ(net::CookieInclusionStatus(), recorded_activity()[0].status); + EXPECT_EQ(recorded_activity()[1].get, true); + EXPECT_EQ(recorded_activity()[1].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[1].site_for_cookies, "https://example.com/"); + EXPECT_THAT(recorded_activity()[1].cookie, + net::MatchesCookieLine("name_legacy=value")); + EXPECT_EQ(net::CookieInclusionStatus(), recorded_activity()[1].status); + + recorded_activity().clear(); + + // Get cookies from the RestrictedCookieManager in a cross-site context to + // trigger warnings. + service_->OverrideSiteForCookiesForTesting( + net::SiteForCookies::FromUrl(GURL("https://notexample.com"))); + { + auto options = mojom::CookieManagerGetOptions::New(); + options->name = "name"; + options->match_type = mojom::CookieMatchType::STARTS_WITH; + + std::vector<net::CookieWithAccessResult> cookies = + sync_service_->GetAllForUrlWithAccessResult( + GURL("https://example.com/test/"), GURL("https://notexample.com"), + url::Origin::Create(GURL("https://example.com")), + std::move(options)); + + ASSERT_THAT(cookies, testing::SizeIs(1)); + EXPECT_EQ("name", cookies[0].cookie.Name()); + + // The warning is not applied to returned cookies. + EXPECT_EQ(net::CookieInclusionStatus(), cookies[0].access_result.status); + } + + // The warning is applied to the CookieInclusionStatuses passed to the + // CookieAccessObserver. + ASSERT_EQ(2u, recorded_activity().size()); + EXPECT_EQ(recorded_activity()[0].get, true); + EXPECT_EQ(recorded_activity()[0].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[0].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[0].cookie, + net::MatchesCookieLine("name_legacy=value")); + EXPECT_TRUE( + recorded_activity()[0].status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus:: + EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); + EXPECT_TRUE(recorded_activity()[0].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT, + net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR})); + EXPECT_EQ(recorded_activity()[1].get, true); + EXPECT_EQ(recorded_activity()[1].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[1].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[1].cookie, + net::MatchesCookieLine("name=value")); + EXPECT_TRUE(recorded_activity()[1].status.IsInclude()); + EXPECT_TRUE(recorded_activity()[1].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR})); +} + +// Test that the WARN_SAMESITE_COMPAT_PAIR warning is not applied if either of +// the cookies in the pair is HttpOnly and the access is from a script. +TEST_P(RestrictedCookieManagerTest, SameSiteCompatPairWarning_HttpOnly) { + // Use a cross-site context to trigger warnings. + service_->OverrideSiteForCookiesForTesting( + net::SiteForCookies::FromUrl(GURL("https://notexample.com"))); + + // Set cookies directly into the store that form a compat pair. + // One of them is HttpOnly. + ASSERT_TRUE(EnsureSetCanonicalCookie(net::CanonicalCookie( + "name", "value", "example.com", "/", base::Time(), base::Time(), + base::Time(), /* secure = */ true, + /* httponly = */ true, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT))); + ASSERT_TRUE(EnsureSetCanonicalCookie(net::CanonicalCookie( + "name_legacy", "value", "example.com", "/", base::Time(), base::Time(), + base::Time(), /* secure = */ true, + /* httponly = */ false, net::CookieSameSite::UNSPECIFIED, + net::COOKIE_PRIORITY_DEFAULT))); + + // Get cookies from the RestrictedCookieManager in a cross-site context. + { + auto options = mojom::CookieManagerGetOptions::New(); + options->name = "name"; + options->match_type = mojom::CookieMatchType::STARTS_WITH; + + std::vector<net::CookieWithAccessResult> cookies = + sync_service_->GetAllForUrlWithAccessResult( + GURL("https://example.com/test/"), GURL("https://notexample.com"), + url::Origin::Create(GURL("https://example.com")), + std::move(options)); + + if (GetParam() == mojom::RestrictedCookieManagerRole::SCRIPT) { + ASSERT_THAT(cookies, testing::SizeIs(0)); + } else { // mojom::RestrictedCookieManagerRole::NETWORK + ASSERT_THAT(cookies, testing::SizeIs(1)); + EXPECT_EQ("name", cookies[0].cookie.Name()); + + // The warning is not applied to returned cookies. + EXPECT_EQ(net::CookieInclusionStatus(), cookies[0].access_result.status); + } + } + + ASSERT_EQ(GetParam() == mojom::RestrictedCookieManagerRole::SCRIPT ? 1u : 2u, + recorded_activity().size()); + EXPECT_EQ(recorded_activity()[0].get, true); + EXPECT_EQ(recorded_activity()[0].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[0].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[0].cookie, + net::MatchesCookieLine("name_legacy=value")); + EXPECT_TRUE( + recorded_activity()[0].status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus:: + EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); + if (GetParam() == mojom::RestrictedCookieManagerRole::SCRIPT) { + // No compat pair warning in script context. + EXPECT_TRUE( + recorded_activity()[0].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus:: + WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); + return; + } + // Compat pair warning is applied in a network context. + EXPECT_TRUE(recorded_activity()[0].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus::WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT, + net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR})); + + EXPECT_EQ(recorded_activity()[1].get, true); + EXPECT_EQ(recorded_activity()[1].url, "https://example.com/test/"); + EXPECT_EQ(recorded_activity()[1].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[1].cookie, + net::MatchesCookieLine("name=value")); + EXPECT_TRUE(recorded_activity()[1].status.IsInclude()); + EXPECT_TRUE(recorded_activity()[1].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR})); +} + +// Test that compat pair warning is not applied when RestrictedCookieManager +// sets a cookie. +TEST_P(RestrictedCookieManagerTest, SameSiteCompatPairWarning_NotAppliedOnSet) { + // Even a cross-site context should not apply warnings for setting cookies. + service_->OverrideSiteForCookiesForTesting( + net::SiteForCookies::FromUrl(GURL("https://notexample.com"))); + + { + // SameSite=None cookie is set. + auto cookie = net::CanonicalCookie::Create( + GURL("https://example.com"), "A=B; SameSite=none; Secure", + base::Time::Now(), base::nullopt /* server_time */); + EXPECT_TRUE(sync_service_->SetCanonicalCookie( + *cookie, GURL("https://example.com"), GURL("https://notexample.com"), + url::Origin::Create(GURL("https://example.com")))); + } + ASSERT_EQ(1u, recorded_activity().size()); + EXPECT_EQ(recorded_activity()[0].get, false); + EXPECT_EQ(recorded_activity()[0].url, "https://example.com/"); + EXPECT_EQ(recorded_activity()[0].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[0].cookie, net::MatchesCookieLine("A=B")); + EXPECT_EQ(net::CookieInclusionStatus(), recorded_activity()[0].status); + + { + // Legacy cookie is rejected. + auto cookie = net::CanonicalCookie::Create(GURL("https://example.com"), + "A_compat=B", base::Time::Now(), + base::nullopt /* server_time */); + EXPECT_FALSE(sync_service_->SetCanonicalCookie( + *cookie, GURL("https://example.com"), GURL("https://notexample.com"), + url::Origin::Create(GURL("https://example.com")))); + } + ASSERT_EQ(2u, recorded_activity().size()); + EXPECT_EQ(recorded_activity()[1].get, false); + EXPECT_EQ(recorded_activity()[1].url, "https://example.com/"); + EXPECT_EQ(recorded_activity()[1].site_for_cookies, "https://notexample.com/"); + EXPECT_THAT(recorded_activity()[1].cookie, + net::MatchesCookieLine("A_compat=B")); + EXPECT_TRUE( + recorded_activity()[1].status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus:: + EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); + EXPECT_TRUE(recorded_activity()[1].status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus:: + WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); +} + namespace { // Stashes the cookie changes it receives, for testing. @@ -1033,13 +1290,12 @@ TEST_P(RestrictedCookieManagerTest, ChangeNotificationIncludesAccessSemantics) { base::nullopt); // Set cookie directly into the CookieMonster, using all-inclusive options. - net::ResultSavingCookieCallback<net::CanonicalCookie::CookieInclusionStatus> - callback; + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; cookie_monster_.SetCanonicalCookieAsync( std::move(cookie), cookie_url, net::CookieOptions::MakeAllInclusive(), - base::BindOnce(&net::ResultSavingCookieCallback< - net::CanonicalCookie::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback))); callback.WaitUntilDone(); ASSERT_TRUE(callback.result().IsInclude()); @@ -1089,14 +1345,13 @@ TEST_P(RestrictedCookieManagerTest, NoChangeNotificationForNonlegacyCookie) { base::Time::Now(), base::nullopt); // Set cookies directly into the CookieMonster, using all-inclusive options. - net::ResultSavingCookieCallback<net::CanonicalCookie::CookieInclusionStatus> - callback1; + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback1; cookie_monster_.SetCanonicalCookieAsync( std::move(unspecified_cookie), cookie_url, net::CookieOptions::MakeAllInclusive(), - base::BindOnce(&net::ResultSavingCookieCallback< - net::CanonicalCookie::CookieInclusionStatus>::Run, - base::Unretained(&callback1))); + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback1))); callback1.WaitUntilDone(); ASSERT_TRUE(callback1.result().IsInclude()); @@ -1105,14 +1360,13 @@ TEST_P(RestrictedCookieManagerTest, NoChangeNotificationForNonlegacyCookie) { base::RunLoop().RunUntilIdle(); ASSERT_THAT(listener.observed_changes(), testing::SizeIs(0)); - net::ResultSavingCookieCallback<net::CanonicalCookie::CookieInclusionStatus> - callback2; + net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback2; cookie_monster_.SetCanonicalCookieAsync( std::move(samesite_none_cookie), cookie_url, net::CookieOptions::MakeAllInclusive(), - base::BindOnce(&net::ResultSavingCookieCallback< - net::CanonicalCookie::CookieInclusionStatus>::Run, - base::Unretained(&callback2))); + base::BindOnce( + &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + base::Unretained(&callback2))); callback2.WaitUntilDone(); ASSERT_TRUE(callback2.result().IsInclude()); diff --git a/chromium/services/network/ssl_config_service_mojo_unittest.cc b/chromium/services/network/ssl_config_service_mojo_unittest.cc index 54459ce6469..8679184550f 100644 --- a/chromium/services/network/ssl_config_service_mojo_unittest.cc +++ b/chromium/services/network/ssl_config_service_mojo_unittest.cc @@ -4,6 +4,7 @@ #include "services/network/ssl_config_service_mojo.h" +#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/run_loop.h" #include "base/stl_util.h" @@ -28,8 +29,10 @@ #include "net/url_request/url_request_context.h" #include "services/network/network_context.h" #include "services/network/network_service.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/ssl_config.mojom.h" +#include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -156,6 +159,12 @@ class NetworkServiceSSLConfigServiceTest : public testing::Test { // stores it in |network_context_|. void SetUpNetworkContext( mojom::NetworkContextParamsPtr network_context_params) { + // Use a dummy CertVerifier that always passes cert verification, since + // these unittests don't need to test the behavior of a real CertVerifier. + // There are a parallel set of tests in services/cert_verifier/ that *do* + // test CertVerifier behavior. + network_context_params->cert_verifier_params = + FakeTestCertVerifierParamsFactory::GetCertVerifierParams(); ssl_config_client_.reset(); network_context_params->ssl_config_client_receiver = ssl_config_client_.BindNewPipeAndPassReceiver(); @@ -432,10 +441,10 @@ TEST_F(NetworkServiceSSLConfigServiceTest, TEST_F(NetworkServiceSSLConfigServiceTest, InitialConfigTLS13Hardening) { net::SSLContextConfig expected_net_config; - expected_net_config.tls13_hardening_for_local_anchors_enabled = true; + expected_net_config.tls13_hardening_for_local_anchors_enabled = false; mojom::SSLConfigPtr mojo_config = mojom::SSLConfig::New(); - mojo_config->tls13_hardening_for_local_anchors_enabled = true; + mojo_config->tls13_hardening_for_local_anchors_enabled = false; RunConversionTests(*mojo_config, expected_net_config); } @@ -489,66 +498,5 @@ TEST_F(NetworkServiceSSLConfigServiceTest, CanShareConnectionWithClientCerts) { config_service->CanShareConnectionWithClientCerts("example.net")); } -#if !defined(OS_IOS) && !defined(OS_ANDROID) -TEST_F(NetworkServiceSSLConfigServiceTest, CRLSetIsApplied) { - SetUpNetworkContext(mojom::NetworkContextParams::New()); - - SSLConfigServiceMojo* config_service = static_cast<SSLConfigServiceMojo*>( - network_context_->url_request_context()->ssl_config_service()); - - scoped_refptr<net::X509Certificate> root_cert = - net::CreateCertificateChainFromFile( - net::GetTestCertsDirectory(), "root_ca_cert.pem", - net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE); - ASSERT_TRUE(root_cert); - net::ScopedTestRoot test_root(root_cert.get()); - - scoped_refptr<net::X509Certificate> cert = - net::CreateCertificateChainFromFile( - net::GetTestCertsDirectory(), "ok_cert.pem", - net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE); - ASSERT_TRUE(cert); - - // Ensure that |cert| is trusted without any CRLSet explicitly configured. - net::TestCompletionCallback callback1; - net::CertVerifyResult cert_verify_result1; - std::unique_ptr<net::CertVerifier::Request> request1; - int result = network_context_->url_request_context()->cert_verifier()->Verify( - net::CertVerifier::RequestParams(cert, "127.0.0.1", - /*flags=*/0, - /*ocsp_response=*/std::string(), - /*sct_list=*/std::string()), - &cert_verify_result1, callback1.callback(), &request1, - net::NetLogWithSource()); - ASSERT_THAT(callback1.GetResult(result), net::test::IsOk()); - - // Configure an explicit CRLSet that removes trust in |leaf_cert| by SPKI. - base::StringPiece spki; - ASSERT_TRUE(net::asn1::ExtractSPKIFromDERCert( - net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()), - &spki)); - net::SHA256HashValue spki_sha256; - crypto::SHA256HashString(spki, spki_sha256.data, sizeof(spki_sha256.data)); - - config_service->OnNewCRLSet(net::CRLSet::ForTesting( - false, &spki_sha256, cert->serial_number(), "", {})); - - // Ensure that |cert| is revoked, due to the CRLSet being applied. - net::TestCompletionCallback callback2; - net::CertVerifyResult cert_verify_result2; - std::unique_ptr<net::CertVerifier::Request> request2; - result = network_context_->url_request_context()->cert_verifier()->Verify( - net::CertVerifier::RequestParams(cert, "127.0.0.1", - /*flags=*/0, - /*ocsp_response=*/std::string(), - /*sct_list=*/std::string()), - &cert_verify_result2, callback2.callback(), &request2, - net::NetLogWithSource()); - ASSERT_THAT(callback2.GetResult(result), - net::test::IsError(net::ERR_CERT_REVOKED)); -} - -#endif // !defined(OS_IOS) && !defined(OS_ANDROID) - } // namespace } // namespace network diff --git a/chromium/services/network/tls_client_socket_unittest.cc b/chromium/services/network/tls_client_socket_unittest.cc index f5aa04d3371..4c13918e9d3 100644 --- a/chromium/services/network/tls_client_socket_unittest.cc +++ b/chromium/services/network/tls_client_socket_unittest.cc @@ -19,6 +19,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "net/base/completion_once_callback.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/base/test_completion_callback.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/socket/server_socket.h" @@ -175,7 +176,7 @@ class TLSClientSocketTestBase { base::RunLoop run_loop; int net_error = net::ERR_FAILED; proxy_resolving_factory_->CreateProxyResolvingSocket( - url, nullptr /* options */, + url, net::NetworkIsolationKey(), nullptr /* options */, net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS), std::move(receiver), mojo::NullRemote() /* observer */, base::BindLambdaForTesting( diff --git a/chromium/services/network/trust_tokens/BUILD.gn b/chromium/services/network/trust_tokens/BUILD.gn index a575ad1abbc..2e105004a31 100644 --- a/chromium/services/network/trust_tokens/BUILD.gn +++ b/chromium/services/network/trust_tokens/BUILD.gn @@ -80,6 +80,7 @@ source_set("trust_tokens") { deps = [ ":storage_proto", "//base", + "//base/util/values:values_util", "//components/cbor", "//components/sqlite_proto", "//services/network/public/cpp", diff --git a/chromium/services/network/trust_tokens/has_trust_tokens_answerer_unittest.cc b/chromium/services/network/trust_tokens/has_trust_tokens_answerer_unittest.cc index b886aa93fda..f94124aa50f 100644 --- a/chromium/services/network/trust_tokens/has_trust_tokens_answerer_unittest.cc +++ b/chromium/services/network/trust_tokens/has_trust_tokens_answerer_unittest.cc @@ -6,6 +6,7 @@ #include "base/strings/stringprintf.h" #include "base/test/bind_test_util.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom.h" #include "services/network/trust_tokens/pending_trust_token_store.h" #include "services/network/trust_tokens/trust_token_parameterization.h" diff --git a/chromium/services/network/trust_tokens/signed_redemption_record_serialization.h b/chromium/services/network/trust_tokens/signed_redemption_record_serialization.h index a84afe1ebbf..26103396b58 100644 --- a/chromium/services/network/trust_tokens/signed_redemption_record_serialization.h +++ b/chromium/services/network/trust_tokens/signed_redemption_record_serialization.h @@ -9,6 +9,7 @@ #include "base/containers/span.h" #include "base/optional.h" +#include "base/strings/string_piece_forward.h" #include "base/time/time.h" namespace network { diff --git a/chromium/services/network/trust_tokens/sqlite_trust_token_persister.cc b/chromium/services/network/trust_tokens/sqlite_trust_token_persister.cc index 8fb3c7fc162..f7593979961 100644 --- a/chromium/services/network/trust_tokens/sqlite_trust_token_persister.cc +++ b/chromium/services/network/trust_tokens/sqlite_trust_token_persister.cc @@ -6,6 +6,7 @@ #include "base/callback_forward.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/memory/scoped_refptr.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_split.h" @@ -145,6 +146,11 @@ void SQLiteTrustTokenPersister::CreateForFilePath( TrustTokenDatabaseOwner::Create( /*db_opener=*/base::BindOnce( [](const base::FilePath& path, sql::Database* db) { + const base::FilePath directory = path.DirName(); + if (!base::PathExists(directory) && + !base::CreateDirectory(directory)) { + return false; + } return db->Open(path); }, path), diff --git a/chromium/services/network/trust_tokens/sqlite_trust_token_persister.h b/chromium/services/network/trust_tokens/sqlite_trust_token_persister.h index 04f18b8488d..2081479ac1e 100644 --- a/chromium/services/network/trust_tokens/sqlite_trust_token_persister.h +++ b/chromium/services/network/trust_tokens/sqlite_trust_token_persister.h @@ -39,7 +39,8 @@ class SQLiteTrustTokenPersister : public TrustTokenPersister { // Constructs a SQLiteTrustTokenPersister backed by an on-disk // database: // - |db_task_runner| will be used for posting blocking database IO; - // - |path| will store the database. + // - |path| will store the database; if its parent directory doesn't exist, + // the method will attempt to create the directory. // - |flush_delay_for_writes| is the maximum time before each write is flushed // to the underlying database. // diff --git a/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc b/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc index a4416358354..908d96fd0eb 100644 --- a/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc +++ b/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc @@ -9,6 +9,7 @@ #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/run_loop.h" #include "base/task/post_task.h" #include "base/test/bind_test_util.h" @@ -87,4 +88,34 @@ TEST(SQLiteTrustTokenPersister, PutReinitializeAndGet) { base::DeleteFile(temp_path, false); } +// Ensure that it's possible to create a Trust Tokens persister on top of a +// directory that does not already exist (regression test for +// crbug.com/1098019). +TEST(SQLiteTrustTokenPersister, NonexistentDirectory) { + base::test::TaskEnvironment env; + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath temp_path = temp_dir.GetPath().Append( + FILE_PATH_LITERAL("some-nonexistent-directory/my-database")); + ASSERT_FALSE(base::PathExists(temp_path.DirName())); + + std::unique_ptr<SQLiteTrustTokenPersister> persister; + SQLiteTrustTokenPersister::CreateForFilePath( + base::ThreadTaskRunnerHandle::Get(), temp_path, + /*flush_delay_for_writes=*/base::TimeDelta(), + base::BindLambdaForTesting( + [&persister](std::unique_ptr<SQLiteTrustTokenPersister> created) { + persister = std::move(created); + base::RunLoop().Quit(); + })); + env.RunUntilIdle(); // Allow initialization to complete. + ASSERT_TRUE(persister); + + persister.reset(); + // Wait until the persister's TrustTokenDatabaseOwner finishes closing its + // database asynchronously, so as not to leak after the test concludes. + env.RunUntilIdle(); +} + } // namespace network diff --git a/chromium/services/network/trust_tokens/trust_token_http_headers.cc b/chromium/services/network/trust_tokens/trust_token_http_headers.cc index 2054fe3272e..9a29a03306a 100644 --- a/chromium/services/network/trust_tokens/trust_token_http_headers.cc +++ b/chromium/services/network/trust_tokens/trust_token_http_headers.cc @@ -13,7 +13,8 @@ const std::vector<base::StringPiece>& TrustTokensRequestHeaders() { static base::NoDestructor<std::vector<base::StringPiece>> headers{ {kTrustTokensRequestHeaderSecSignature, kTrustTokensRequestHeaderSecSignedRedemptionRecord, - kTrustTokensRequestHeaderSecTime, kTrustTokensSecTrustTokenHeader}}; + kTrustTokensRequestHeaderSecTime, kTrustTokensSecTrustTokenHeader, + kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData}}; return *headers; } diff --git a/chromium/services/network/trust_tokens/trust_token_http_headers.h b/chromium/services/network/trust_tokens/trust_token_http_headers.h index 74d54967163..4bb6247c946 100644 --- a/chromium/services/network/trust_tokens/trust_token_http_headers.h +++ b/chromium/services/network/trust_tokens/trust_token_http_headers.h @@ -44,6 +44,11 @@ constexpr char kTrustTokensRequestHeaderSecSignedRedemptionRecord[] = // header denotes an empty list. constexpr char kTrustTokensRequestHeaderSignedHeaders[] = "Signed-Headers"; +// As a request header, provides optional additional client-specified signing +// data alongside signed requests. +constexpr char kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData[] = + "Sec-Trust-Tokens-Additional-Signing-Data"; + // Returns a view of all of the Trust Tokens-internal request headers. // This vector contains all of the headers that clients must not provide on // requests bearing Trust Tokens operations, because they are added internally diff --git a/chromium/services/network/trust_tokens/trust_token_key_commitment_parser.cc b/chromium/services/network/trust_tokens/trust_token_key_commitment_parser.cc index 888129d8f71..6bcfe8abe58 100644 --- a/chromium/services/network/trust_tokens/trust_token_key_commitment_parser.cc +++ b/chromium/services/network/trust_tokens/trust_token_key_commitment_parser.cc @@ -144,7 +144,7 @@ const char kTrustTokenKeyCommitmentKeyField[] = "Y"; // https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#bookmark=id.6wh9crbxdizi // { -// "batchsize" : ..., // Optional batch size; value of type int. +// "batchsize" : ..., // Batch size; value of type int. // "srrkey" : ..., // Required Signed Redemption Record (SRR) // // verification key, in base64. // diff --git a/chromium/services/network/trust_tokens/trust_token_parameterization.h b/chromium/services/network/trust_tokens/trust_token_parameterization.h index 77f9af67e7c..93209d72c3f 100644 --- a/chromium/services/network/trust_tokens/trust_token_parameterization.h +++ b/chromium/services/network/trust_tokens/trust_token_parameterization.h @@ -11,6 +11,11 @@ namespace network { +// Note: Some of the constants in this file might spiritually be part of the +// network service public API and belong in the corresponding +// services/network/public file; just because they're currently here, not there, +// doesn't mean there's necessarily a reason they can't be moved. + // Priority for running blocking Trust Tokens database IO. This is given value // USER_VISIBLE because Trust Tokens DB operations can sometimes be in the // loading critical path, but generally only for subresources. @@ -48,14 +53,6 @@ constexpr size_t kTrustTokenKeyCommitmentRegistryMaxSizeBytes = 1 << 22; // 500 is chosen as a high-but-not-excessive value for initial experimentation. constexpr int kTrustTokenPerIssuerTokenCapacity = 500; -// The maximum number of trust token issuers allowed to be associated with a -// given top-level origin. -// -// This value is quite low because registering additional issuers with an origin -// has a number of privacy risks (for instance, whether or not a user has any -// tokens issued by a given issuer reveals one bit of identifying information). -constexpr int kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers = 2; - // The maximum Trust Tokens batch size (i.e., number of tokens to request from // an issuer). constexpr int kMaximumTrustTokenIssuanceBatchSize = 100; diff --git a/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc b/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc index 5fd1bc39281..130509bbee4 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc @@ -13,6 +13,7 @@ #include "net/log/net_log_with_source.h" #include "net/url_request/url_request.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom-shared.h" #include "services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer.h" #include "services/network/trust_tokens/boringssl_trust_token_redemption_cryptographer.h" @@ -21,6 +22,7 @@ #include "services/network/trust_tokens/suitable_trust_token_origin.h" #include "services/network/trust_tokens/trust_token_http_headers.h" #include "services/network/trust_tokens/trust_token_key_commitment_controller.h" +#include "services/network/trust_tokens/trust_token_parameterization.h" #include "services/network/trust_tokens/trust_token_request_canonicalizer.h" #include "services/network/trust_tokens/trust_token_request_issuance_helper.h" #include "services/network/trust_tokens/trust_token_request_redemption_helper.h" @@ -140,9 +142,10 @@ void TrustTokenRequestHelperFactory::ConstructHelperUsingStore( TrustTokenRequestSigningHelper::Params signing_params( std::move(*maybe_issuer), top_frame_origin, std::move(params->additional_signed_headers), - params->include_timestamp_header, params->sign_request_data); + params->include_timestamp_header, params->sign_request_data, + params->possibly_unsafe_additional_signing_data); - LogOutcome(net_log, "Missing/unsuitable 'issuer' parameter"); + LogOutcome(net_log, "Success"); std::move(done).Run(std::unique_ptr<TrustTokenRequestHelper>( new TrustTokenRequestSigningHelper( store, std::move(signing_params), diff --git a/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc index 61082ec429b..10c2b2cbb41 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc @@ -12,10 +12,12 @@ #include "net/url_request/url_request.h" #include "services/network/public/cpp/optional_trust_token_params.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom.h" #include "services/network/trust_tokens/pending_trust_token_store.h" #include "services/network/trust_tokens/test/trust_token_test_util.h" #include "services/network/trust_tokens/trust_token_http_headers.h" +#include "services/network/trust_tokens/trust_token_parameterization.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" @@ -163,6 +165,20 @@ TEST_F(TrustTokenRequestHelperFactoryTest, mojom::TrustTokenOperationStatus::kInvalidArgument); } +TEST_F(TrustTokenRequestHelperFactoryTest, + WillCreateSigningHelperWithAdditionalData) { + auto request = CreateSuitableRequest(); + + auto params = suitable_params().Clone(); + params->type = mojom::TrustTokenOperationType::kSigning; + params->possibly_unsafe_additional_signing_data = + std::string(kTrustTokenAdditionalSigningDataMaxSizeBytes, 'a'); + + auto result = CreateHelperAndWaitForResult(suitable_request(), *params); + ASSERT_TRUE(result.ok()); + EXPECT_TRUE(result.TakeOrCrash()); +} + TEST_F(TrustTokenRequestHelperFactoryTest, CreatesSigningHelper) { auto params = suitable_params().Clone(); params->type = mojom::TrustTokenOperationType::kSigning; diff --git a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc index 0bafdc97d9f..7799e2af869 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc @@ -14,6 +14,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_test_util.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "services/network/trust_tokens/proto/public.pb.h" diff --git a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc index 492987da51c..87661babf5c 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc @@ -14,6 +14,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_test_util.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "services/network/trust_tokens/proto/public.pb.h" #include "services/network/trust_tokens/test/trust_token_test_util.h" diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc index 682b698cc07..5a185ef4261 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc @@ -22,6 +22,7 @@ #include "net/http/structured_headers.h" #include "net/url_request/url_request.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom-shared.h" #include "services/network/trust_tokens/proto/public.pb.h" #include "services/network/trust_tokens/trust_token_http_headers.h" @@ -76,6 +77,7 @@ base::Optional<std::vector<std::string>> ParseTrustTokenSignedHeadersHeader( const char* const TrustTokenRequestSigningHelper::kSignableRequestHeaders[]{ kTrustTokensRequestHeaderSecSignedRedemptionRecord, kTrustTokensRequestHeaderSecTime, + kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData, }; constexpr char @@ -201,16 +203,20 @@ TrustTokenRequestSigningHelper::TrustTokenRequestSigningHelper( TrustTokenRequestSigningHelper::~TrustTokenRequestSigningHelper() = default; -Params::Params(SuitableTrustTokenOrigin issuer, - SuitableTrustTokenOrigin toplevel, - std::vector<std::string> additional_headers_to_sign, - bool should_add_timestamp, - mojom::TrustTokenSignRequestData sign_request_data) +Params::Params( + SuitableTrustTokenOrigin issuer, + SuitableTrustTokenOrigin toplevel, + std::vector<std::string> additional_headers_to_sign, + bool should_add_timestamp, + mojom::TrustTokenSignRequestData sign_request_data, + base::Optional<std::string> possibly_unsafe_additional_signing_data) : issuer(std::move(issuer)), toplevel(std::move(toplevel)), additional_headers_to_sign(std::move(additional_headers_to_sign)), should_add_timestamp(should_add_timestamp), - sign_request_data(sign_request_data) {} + sign_request_data(sign_request_data), + possibly_unsafe_additional_signing_data( + possibly_unsafe_additional_signing_data) {} Params::Params(SuitableTrustTokenOrigin issuer, SuitableTrustTokenOrigin toplevel) @@ -279,6 +285,43 @@ void TrustTokenRequestSigningHelper::Begin( return; } + if (params_.possibly_unsafe_additional_signing_data) { + if (params_.possibly_unsafe_additional_signing_data->size() > + kTrustTokenAdditionalSigningDataMaxSizeBytes) { + LogOutcome(net_log_, "Overly long additionalSigningData"); + + AttachSignedRedemptionRecordHeader(request, std::string()); + std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); + return; + } + + if (!net::HttpUtil::IsValidHeaderValue( + *params_.possibly_unsafe_additional_signing_data)) { + LogOutcome(net_log_, + "additionalSigningData was not a valid HTTP header value"); + + AttachSignedRedemptionRecordHeader(request, std::string()); + std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); + return; + } + + // |request| is guaranteed to not have + // kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData because + // network::TrustTokenRequestHeaders() contains this header name, so the + // request would have been rejected as failing a precondition if the header + // were present. + DCHECK(!request->extra_request_headers().HasHeader( + kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData)); + + request->SetExtraRequestHeaderByName( + kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData, + *params_.possibly_unsafe_additional_signing_data, + /*overwrite=*/true); + + params_.additional_headers_to_sign.push_back( + kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData); + } + base::Optional<std::vector<std::string>> maybe_headers_to_sign = GetHeadersToSignAndUpdateSignedHeadersHeader( request, params_.additional_headers_to_sign); diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h index 0520b20d17c..831ee3a68b4 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h @@ -77,7 +77,8 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { SuitableTrustTokenOrigin toplevel, std::vector<std::string> additional_headers_to_sign, bool should_add_timestamp, - mojom::TrustTokenSignRequestData sign_request_data); + mojom::TrustTokenSignRequestData sign_request_data, + base::Optional<std::string> possibly_unsafe_additional_signing_data); // Minimal convenience constructor. Other fields have reasonable defaults, // but it's necessary to have |issuer| and |toplevel| at construction time @@ -119,6 +120,18 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { // kHeadersOnly, the request's headers will be the only request data used. // If it is kOmit, no signature will be attached. mojom::TrustTokenSignRequestData sign_request_data; + + // |possibly_unsafe_additional_signing_data| stores the contents of + // arbitrary extra client-provided data to include in the outgoing request's + // Sec-Trust-Tokens-Additional-Signing-Data header. + // + // If this is longer than 2048 or not valid to include as a header value, + // the signing operation will fail. + // + // Otherwise, the value will be attached in the + // Sec-Trust-Tokens-Additional-Signing-Data header and the header name will + // be added to the list of headers to sign. + base::Optional<std::string> possibly_unsafe_additional_signing_data; }; // Class Signer is responsible for the actual generation of signatures over @@ -177,8 +190,7 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { // 1. The caller specified headers for signing other than those in // kSignableRequestHeaders (or if the request has a malformed or otherwise // invalid signed issuers list in its Signed-Headers header); or - // 2. |token_store_| contains no SRR for this issuer-toplevel pair, - // returns kOk and attaches an empty Sec-Signed-Redemption-Record header; or + // 2. |token_store_| contains no SRR for this issuer-toplevel pair; or // 3. an internal error occurs during signing or header serialization. // // POSTCONDITIONS: diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc index 09f2b070816..08a9bad1ae9 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc @@ -26,6 +26,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_test_util.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom-shared.h" #include "services/network/trust_tokens/proto/public.pb.h" #include "services/network/trust_tokens/test/signed_request_verification_util.h" @@ -500,9 +501,9 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyTimestampHeader) { std::string signature_string; ASSERT_NO_FATAL_FAILURE( AssertHasSignatureAndExtract(*my_request, &signature_string)); - std::string retrieved_url_spec; + std::string retrieved_timestamp; ASSERT_NO_FATAL_FAILURE(AssertDecodesToCborAndExtractField( - signature_string, "sec-time", &retrieved_url_spec)); + signature_string, "sec-time", &retrieved_timestamp)); } // Test a round-trip sign-and-verify additionally signing over the destination @@ -593,4 +594,117 @@ TEST_F(TrustTokenRequestSigningHelperTest, CatchesSignatureFailure) { EXPECT_THAT(*my_request, Header("Sec-Signed-Redemption-Record", IsEmpty())); } +// Test a round-trip sign-and-verify with signed headers when adding additional +// signing data. +TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyAdditionalSigningData) { + std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting(); + + TrustTokenRequestSigningHelper::Params params( + *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com")), + *SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"))); + params.sign_request_data = mojom::TrustTokenSignRequestData::kHeadersOnly; + params.possibly_unsafe_additional_signing_data = + "some additional data to sign"; + + SignedTrustTokenRedemptionRecord record; + record.set_body("I am a signed token redemption record"); + record.set_public_key("key"); + store->SetRedemptionRecord(params.issuer, params.toplevel, record); + + auto canonicalizer = std::make_unique<TrustTokenRequestCanonicalizer>(); + TrustTokenRequestSigningHelper helper(store.get(), std::move(params), + std::make_unique<IdentitySigner>(), + std::move(canonicalizer)); + + auto my_request = MakeURLRequest("https://destination.com/"); + my_request->set_initiator( + url::Origin::Create(GURL("https://initiator.com/"))); + mojom::TrustTokenOperationStatus result = + ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); + + EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); + ASSERT_NO_FATAL_FAILURE( + ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( + my_request.get())); + + std::string signature_string; + ASSERT_NO_FATAL_FAILURE( + AssertHasSignatureAndExtract(*my_request, &signature_string)); + std::string retrieved_additional_signing_data; + ASSERT_NO_FATAL_FAILURE(AssertDecodesToCborAndExtractField( + signature_string, "sec-trust-tokens-additional-signing-data", + &retrieved_additional_signing_data)); + + EXPECT_EQ(retrieved_additional_signing_data, "some additional data to sign"); +} + +TEST_F(TrustTokenRequestSigningHelperTest, + RejectsOnOverlongAdditionalSigningData) { + std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting(); + + TrustTokenRequestSigningHelper::Params params( + *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com")), + *SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"))); + params.sign_request_data = mojom::TrustTokenSignRequestData::kHeadersOnly; + params.possibly_unsafe_additional_signing_data = + std::string(kTrustTokenAdditionalSigningDataMaxSizeBytes + 1, 'a'); + + SignedTrustTokenRedemptionRecord my_record; + my_record.set_public_key("key"); + store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + + TrustTokenRequestSigningHelper helper( + store.get(), std::move(params), std::make_unique<FakeSigner>(), + std::make_unique<TrustTokenRequestCanonicalizer>()); + + auto my_request = MakeURLRequest("https://destination.com/"); + my_request->set_initiator( + url::Origin::Create(GURL("https://initiator.com/"))); + + mojom::TrustTokenOperationStatus result = + ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); + + // In failure cases, the signing helper should return kOk but attach an empty + // SRR header. + EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); + EXPECT_THAT(*my_request, Header("Sec-Signed-Redemption-Record", IsEmpty())); + EXPECT_THAT(*my_request, Not(Header("Signed-Headers"))); + EXPECT_THAT(*my_request, + Not(Header("Sec-Trust-Tokens-Additional-Signing-Data"))); +} + +TEST_F(TrustTokenRequestSigningHelperTest, + RejectsOnAdditionalSigningDataThatIsNotAValidHeaderValue) { + std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting(); + + TrustTokenRequestSigningHelper::Params params( + *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com")), + *SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"))); + params.sign_request_data = mojom::TrustTokenSignRequestData::kHeadersOnly; + params.possibly_unsafe_additional_signing_data = "\r"; + + SignedTrustTokenRedemptionRecord my_record; + my_record.set_public_key("key"); + store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + + TrustTokenRequestSigningHelper helper( + store.get(), std::move(params), std::make_unique<FakeSigner>(), + std::make_unique<TrustTokenRequestCanonicalizer>()); + + auto my_request = MakeURLRequest("https://destination.com/"); + my_request->set_initiator( + url::Origin::Create(GURL("https://initiator.com/"))); + + mojom::TrustTokenOperationStatus result = + ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); + + // In failure cases, the signing helper should return kOk but attach an empty + // SRR header. + EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); + EXPECT_THAT(*my_request, Header("Sec-Signed-Redemption-Record", IsEmpty())); + EXPECT_THAT(*my_request, Not(Header("Signed-Headers"))); + EXPECT_THAT(*my_request, + Not(Header("Sec-Trust-Tokens-Additional-Signing-Data"))); +} + } // namespace network diff --git a/chromium/services/network/trust_tokens/trust_token_store.cc b/chromium/services/network/trust_tokens/trust_token_store.cc index e9568c59d0c..090531035f4 100644 --- a/chromium/services/network/trust_tokens/trust_token_store.cc +++ b/chromium/services/network/trust_tokens/trust_token_store.cc @@ -10,6 +10,7 @@ #include "base/optional.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/trust_tokens.mojom-forward.h" #include "services/network/trust_tokens/in_memory_trust_token_persister.h" #include "services/network/trust_tokens/proto/public.pb.h" diff --git a/chromium/services/network/trust_tokens/trust_token_store_unittest.cc b/chromium/services/network/trust_tokens/trust_token_store_unittest.cc index 5f5f231be28..8819797097b 100644 --- a/chromium/services/network/trust_tokens/trust_token_store_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_store_unittest.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/test/task_environment.h" #include "base/time/time.h" +#include "services/network/public/cpp/trust_token_parameterization.h" #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/trust_tokens/in_memory_trust_token_persister.h" #include "services/network/trust_tokens/proto/public.pb.h" diff --git a/chromium/services/network/trust_tokens/types.cc b/chromium/services/network/trust_tokens/types.cc index dfa993f2ed3..5911600dbb0 100644 --- a/chromium/services/network/trust_tokens/types.cc +++ b/chromium/services/network/trust_tokens/types.cc @@ -2,24 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/network/trust_tokens/types.h" #include "base/time/time.h" -#include "base/value_conversions.h" -#include "base/values.h" -#include "url/origin.h" +#include "base/util/values/values_util.h" namespace network { namespace internal { base::Optional<base::Time> StringToTime(base::StringPiece my_string) { - base::Time ret; - if (!base::GetValueAsTime(base::Value(my_string), &ret)) - return base::nullopt; - return ret; + return util::ValueToTime(base::Value(my_string)); } std::string TimeToString(base::Time my_time) { - return base::CreateTimeValue(my_time).GetString(); + return util::TimeToValue(my_time).GetString(); } } // namespace internal diff --git a/chromium/services/network/url_loader.cc b/chromium/services/network/url_loader.cc index c9e8660da7a..9d2d455613f 100644 --- a/chromium/services/network/url_loader.cc +++ b/chromium/services/network/url_loader.cc @@ -35,6 +35,7 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_file_element_reader.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/static_cookie_policy.h" #include "net/ssl/client_cert_store.h" #include "net/ssl/ssl_connection_status_flags.h" @@ -44,12 +45,12 @@ #include "net/url_request/url_request_context_getter.h" #include "services/network/chunked_data_pipe_upload_data_stream.h" #include "services/network/data_pipe_element_reader.h" -#include "services/network/empty_url_loader_client.h" #include "services/network/network_usage_accumulator.h" #include "services/network/origin_policy/origin_policy_constants.h" #include "services/network/origin_policy/origin_policy_manager.h" #include "services/network/public/cpp/constants.h" #include "services/network/public/cpp/cross_origin_resource_policy.h" +#include "services/network/public/cpp/empty_url_loader_client.h" #include "services/network/public/cpp/header_util.h" #include "services/network/public/cpp/net_adapters.h" #include "services/network/public/cpp/network_switches.h" @@ -316,14 +317,13 @@ class SSLPrivateKeyInternal : public net::SSLPrivateKey { DISALLOW_COPY_AND_ASSIGN(SSLPrivateKeyInternal); }; -bool ShouldNotifyAboutCookie( - net::CanonicalCookie::CookieInclusionStatus status) { +bool ShouldNotifyAboutCookie(net::CookieInclusionStatus status) { // Notify about cookies actually used, and those blocked by preferences --- // for purposes of cookie UI --- as well those carrying warnings pertaining to // SameSite features, in order to issue a deprecation warning for them. return status.IsInclude() || status.ShouldWarn() || - status.HasExclusionReason(net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_USER_PREFERENCES); + status.HasExclusionReason( + net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); } mojom::HttpRawRequestResponseInfoPtr BuildRawRequestResponseInfo( @@ -416,6 +416,39 @@ const struct { {"Via", ConcerningHeaderId::kVia}, }; +void ReportFetchUploadStreamingUMA(const net::URLRequest* request, + bool allow_http1_for_streaming_upload) { + // Same as tools/metrics/histograms/enums.xml's. + enum class HttpProtocolScheme { + kHTTP1_1 = 0, + kHTTP2 = 1, + kQUIC = 2, + kMaxValue = kQUIC + } protocol; + const auto connection_info = request->response_info().connection_info; + switch (net::HttpResponseInfo::ConnectionInfoToCoarse(connection_info)) { + case net::HttpResponseInfo::CONNECTION_INFO_COARSE_HTTP1: + protocol = HttpProtocolScheme::kHTTP1_1; + break; + case net::HttpResponseInfo::CONNECTION_INFO_COARSE_HTTP2: + protocol = HttpProtocolScheme::kHTTP2; + break; + case net::HttpResponseInfo::CONNECTION_INFO_COARSE_QUIC: + protocol = HttpProtocolScheme::kQUIC; + break; + case net::HttpResponseInfo::CONNECTION_INFO_COARSE_OTHER: + protocol = HttpProtocolScheme::kHTTP1_1; + break; + } + if (allow_http1_for_streaming_upload) { + base::UmaHistogramEnumeration("Net.Fetch.UploadStreamingProtocolAllowH1", + protocol); + } else { + base::UmaHistogramEnumeration("Net.Fetch.UploadStreamingProtocolNotAllowH1", + protocol); + } +} + } // namespace URLLoader::URLLoader( @@ -427,6 +460,7 @@ URLLoader::URLLoader( int32_t options, const ResourceRequest& request, mojo::PendingRemote<mojom::URLLoaderClient> url_loader_client, + base::Optional<DataPipeUseTracker> response_body_use_tracker, const net::NetworkTrafficAnnotationTag& traffic_annotation, const mojom::URLLoaderFactoryParams* factory_params, mojom::CrossOriginEmbedderPolicyReporter* coep_reporter, @@ -456,6 +490,7 @@ URLLoader::URLLoader( do_not_prompt_for_login_(request.do_not_prompt_for_login), receiver_(this, std::move(url_loader_receiver)), url_loader_client_(std::move(url_loader_client)), + response_body_use_tracker_(std::move(response_body_use_tracker)), writable_handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()), @@ -477,7 +512,11 @@ URLLoader::URLLoader( origin_policy_manager_(nullptr), trust_token_helper_factory_(std::move(trust_token_helper_factory)), isolated_world_origin_(request.isolated_world_origin), - cookie_observer_(std::move(cookie_observer)) { + cookie_observer_(std::move(cookie_observer)), + has_fetch_streaming_upload_body_(HasFetchStreamingUploadBody(&request)), + allow_http1_for_streaming_upload_( + request.request_body && + request.request_body->AllowHTTP1ForStreamingUpload()) { DCHECK(delete_callback_); DCHECK(factory_params_); if (url_loader_header_client && @@ -581,13 +620,17 @@ URLLoader::URLLoader( // net::LOAD_DO_NOT_* are in the process of being converted to // credentials_mode. See https://crbug.com/799935. // TODO(crbug.com/943939): Make this work with CredentialsMode::kSameOrigin. - if (request.credentials_mode == mojom::CredentialsMode::kOmit) { + if (request.credentials_mode == mojom::CredentialsMode::kOmit || + request.credentials_mode == + mojom::CredentialsMode::kOmitBug_775438_Workaround) { const auto creds_mask = net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA; DCHECK((request.load_flags & creds_mask) == 0 || (request.load_flags & creds_mask) == creds_mask); url_request_->set_allow_credentials(false); + url_request_->set_send_client_certs(request.credentials_mode == + mojom::CredentialsMode::kOmit); } url_request_->SetRequestHeadersCallback(base::BindRepeating( @@ -990,8 +1033,33 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, url_loader_client_->OnReceiveRedirect(redirect_info, std::move(response)); } +// static +bool URLLoader::HasFetchStreamingUploadBody(const ResourceRequest* request) { + // Follows blink::mojom::ResourceType::kXhr. + const int kXhr = 13; + if (request->resource_type != kXhr) + return false; + const ResourceRequestBody* request_body = request->request_body.get(); + if (!request_body) + return false; + const std::vector<DataElement>* elements = request_body->elements(); + if (elements->size() == 0u) + return false; + // https://fetch.spec.whatwg.org/#concept-bodyinit-extract + // Body's source is null means the body is not extracted from ReadableStream. + // See blink::PopulateResourceRequest() for actual construction. + if (elements->size() > 1u) + return false; + return elements->at(0).type() == mojom::DataElementType::kChunkedDataPipe; +} + void URLLoader::OnAuthRequired(net::URLRequest* url_request, const net::AuthChallengeInfo& auth_info) { + if (has_fetch_streaming_upload_body_) { + NotifyCompleted(net::ERR_FAILED); + // |this| may have been deleted. + return; + } if (!network_context_client_) { OnAuthCredentials(base::nullopt); return; @@ -1074,6 +1142,10 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { has_received_response_ = true; ReportFlaggedResponseCookies(); + if (has_fetch_streaming_upload_body_) { + ReportFetchUploadStreamingUMA(url_request_.get(), + allow_http1_for_streaming_upload_); + } if (net_error != net::OK) { NotifyCompleted(net_error); @@ -1092,6 +1164,9 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { NotifyCompleted(net::ERR_INSUFFICIENT_RESOURCES); return; } + if (response_body_use_tracker_) { + response_body_use_tracker_->Activate(); + } DCHECK(response_body_stream_.is_valid()); DCHECK(consumer_handle_.is_valid()); @@ -1563,6 +1638,7 @@ void URLLoader::NotifyCompleted(int error_code) { KeepaliveRequestResult::kErrorBeforeResponseArrival); } } + response_body_use_tracker_ = base::nullopt; // Ensure sending the final upload progress message here, since // OnResponseCompleted can be called without OnResponseStarted on cancellation // or error cases. @@ -1685,6 +1761,8 @@ void URLLoader::CompletePendingWrite(bool success) { // destroyed. response_body_stream_ = pending_write_->Complete(pending_write_buffer_offset_); + } else { + response_body_use_tracker_ = base::nullopt; } total_written_bytes_ += pending_write_buffer_offset_; pending_write_ = nullptr; @@ -1717,10 +1795,13 @@ void URLLoader::SetRawRequestHeadersAndNotify( if (cookie_observer_) { net::CookieStatusList reported_cookies; - for (const auto& cookie_and_status : url_request_->maybe_sent_cookies()) { - if (ShouldNotifyAboutCookie(cookie_and_status.status)) { + for (const auto& cookie_with_access_result : + url_request_->maybe_sent_cookies()) { + if (ShouldNotifyAboutCookie( + cookie_with_access_result.access_result.status)) { reported_cookies.push_back( - {cookie_and_status.cookie, cookie_and_status.status}); + {cookie_with_access_result.cookie, + cookie_with_access_result.access_result.status}); } } @@ -1873,7 +1954,7 @@ URLLoader::BlockResponseForCorbResult URLLoader::BlockResponseForCorb() { // Discard any remaining callbacks or data by rerouting the pipes to // EmptyURLLoaderClient. receiver_.reset(); - EmptyURLLoaderClient::DrainURLRequest( + EmptyURLLoaderClientWrapper::DrainURLRequest( url_loader_client_.BindNewPipeAndPassReceiver(), receiver_.BindNewPipeAndPassRemote()); receiver_.set_disconnect_handler( diff --git a/chromium/services/network/url_loader.h b/chromium/services/network/url_loader.h index a8c41f64ae6..33e6b100ef3 100644 --- a/chromium/services/network/url_loader.h +++ b/chromium/services/network/url_loader.h @@ -29,6 +29,7 @@ #include "net/url_request/url_request.h" #include "services/network/cross_origin_read_blocking.h" #include "services/network/keepalive_statistics_recorder.h" +#include "services/network/network_service.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" #include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h" @@ -46,7 +47,7 @@ namespace net { class HttpResponseHeaders; class URLRequestContext; -} +} // namespace net namespace network { @@ -104,6 +105,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader int32_t options, const ResourceRequest& request, mojo::PendingRemote<mojom::URLLoaderClient> url_loader_client, + base::Optional<DataPipeUseTracker> response_body_use_tracker, const net::NetworkTrafficAnnotationTag& traffic_annotation, const mojom::URLLoaderFactoryParams* factory_params, mojom::CrossOriginEmbedderPolicyReporter* reporter, @@ -208,6 +210,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader const net::HttpRequestHeaders& request_headers, bool added_during_redirect); + static bool HasFetchStreamingUploadBody(const ResourceRequest*); + private: // This class is used to set the URLLoader as user data on a URLRequest. This // is used instead of URLLoader directly because SetUserData requires a @@ -359,6 +363,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader int64_t total_written_bytes_ = 0; mojo::ScopedDataPipeProducerHandle response_body_stream_; + base::Optional<DataPipeUseTracker> response_body_use_tracker_; scoped_refptr<NetToMojoPendingBuffer> pending_write_; uint32_t pending_write_buffer_size_ = 0; uint32_t pending_write_buffer_offset_ = 0; @@ -490,6 +495,14 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader // Observer listening to all cookie reads and writes made by this request. mojo::Remote<mojom::CookieAccessObserver> cookie_observer_; + // Indicates |url_request_| is fetch upload request and that has streaming + // body. + const bool has_fetch_streaming_upload_body_; + + // Indicates whether fetch upload streaming is allowed/rejected over H/1. + // Even if this is false but there is a QUIC/H2 stream, the upload is allowed. + const bool allow_http1_for_streaming_upload_; + base::WeakPtrFactory<URLLoader> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(URLLoader); diff --git a/chromium/services/network/url_loader.md b/chromium/services/network/url_loader.md new file mode 100644 index 00000000000..18d5e490493 --- /dev/null +++ b/chromium/services/network/url_loader.md @@ -0,0 +1,7 @@ +# URLLoader +TODO(loading-dev): Write brief description about URLLoaderFactory, URLLoader and +URLLoaderClient. + +A deck that explains how Mojo connections for these URLLoader classes can be +established to issue a network request with diagrams: +https://docs.google.com/presentation/d/1ku7pkh09h6sQ6epudsVvHehRzvanAU7ckzfiVvMoljo/edit?usp=sharing diff --git a/chromium/services/network/url_loader_factory.cc b/chromium/services/network/url_loader_factory.cc index 965b6bd501e..c8f02a3760f 100644 --- a/chromium/services/network/url_loader_factory.cc +++ b/chromium/services/network/url_loader_factory.cc @@ -24,6 +24,7 @@ #include "services/network/network_usage_accumulator.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/resource_scheduler/resource_scheduler_client.h" #include "services/network/trust_tokens/trust_token_request_helper_factory.h" #include "services/network/url_loader.h" @@ -130,6 +131,7 @@ void URLLoaderFactory::CreateLoaderAndStart( mojom::NetworkServiceClient* network_service_client = nullptr; base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder; base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator; + base::Optional<DataPipeUseTracker> data_pipe_use_tracker; if (context_->network_service()) { network_service_client = context_->network_service()->client(); keepalive_statistics_recorder = context_->network_service() @@ -137,6 +139,8 @@ void URLLoaderFactory::CreateLoaderAndStart( ->AsWeakPtr(); network_usage_accumulator = context_->network_service()->network_usage_accumulator()->AsWeakPtr(); + data_pipe_use_tracker.emplace(context_->network_service(), + DataPipeUser::kUrlLoader); } bool exhausted = false; @@ -259,6 +263,7 @@ void URLLoaderFactory::CreateLoaderAndStart( base::BindOnce(&cors::CorsURLLoaderFactory::DestroyURLLoader, base::Unretained(cors_url_loader_factory_)), std::move(receiver), options, url_request, std::move(client), + std::move(data_pipe_use_tracker), static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation), params_.get(), coep_reporter_ ? coep_reporter_.get() : nullptr, request_id, keepalive_request_size, resource_scheduler_client_, diff --git a/chromium/services/network/url_loader_unittest.cc b/chromium/services/network/url_loader_unittest.cc index 9b9a75c283a..dc8375fd926 100644 --- a/chromium/services/network/url_loader_unittest.cc +++ b/chromium/services/network/url_loader_unittest.cc @@ -50,6 +50,7 @@ #include "net/cert/internal/parse_name.h" #include "net/cert/test_root_certs.h" #include "net/cookies/cookie_change_dispatcher.h" +#include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_util.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_response_info.h" @@ -502,7 +503,8 @@ class URLLoaderTest : public testing::Test { network_context_client.get(), DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), options, request, - client_.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client_.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -1080,7 +1082,8 @@ TEST_F(URLLoaderTest, DestroyOnURLLoaderPipeClosed) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -1136,7 +1139,8 @@ TEST_F(URLLoaderTest, CloseResponseBodyConsumerBeforeProducer) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -1194,9 +1198,9 @@ TEST_F(URLLoaderTest, PauseReadingBodyFromNetBeforeResponseHeaders) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -1274,9 +1278,9 @@ TEST_F(URLLoaderTest, PauseReadingBodyFromNetWhenReadIsPending) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -1343,9 +1347,9 @@ TEST_F(URLLoaderTest, ResumeReadingBodyFromNetAfterClosingConsumer) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -1407,9 +1411,9 @@ TEST_F(URLLoaderTest, MultiplePauseResumeReadingBodyFromNet) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -1662,9 +1666,9 @@ TEST_F(URLLoaderTest, UploadFileCanceled) { network_context_client.get(), DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -1850,9 +1854,9 @@ TEST_F(URLLoaderTest, UploadChunkedDataPipe) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - nullptr /* resource_scheduler_client */, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, nullptr /* resource_scheduler_client */, nullptr /* keepalive_statistics_reporter */, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, @@ -1922,7 +1926,8 @@ TEST_F(URLLoaderTest, RedirectModifiedHeaders) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, @@ -1980,7 +1985,8 @@ TEST_F(URLLoaderTest, RedirectFailsOnModifyUnsafeHeader) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2021,7 +2027,8 @@ TEST_F(URLLoaderTest, RedirectLogsModifiedConcerningHeader) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2076,7 +2083,8 @@ TEST_F(URLLoaderTest, RedirectRemoveHeader) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2121,7 +2129,8 @@ TEST_F(URLLoaderTest, RedirectRemoveHeaderAndAddItBack) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2170,7 +2179,8 @@ TEST_F(URLLoaderTest, UpgradeAddsSecHeaders) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2223,7 +2233,8 @@ TEST_F(URLLoaderTest, DowngradeRemovesSecHeaders) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2285,7 +2296,8 @@ TEST_F(URLLoaderTest, RedirectChainRemovesAndAddsSecHeaders) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2353,7 +2365,9 @@ TEST_F(URLLoaderTest, RedirectSecHeadersUser) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2387,7 +2401,9 @@ TEST_F(URLLoaderTest, RedirectDirectlyModifiedSecHeadersUser) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2494,7 +2510,8 @@ TEST_F(URLLoaderTest, ResourceSchedulerIntegration) { nullptr /* network_context_client */, NeverInvokedDeleteLoaderCallback(), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2518,7 +2535,8 @@ TEST_F(URLLoaderTest, ResourceSchedulerIntegration) { context(), nullptr /* network_service_client */, nullptr /* network_context_client */, NeverInvokedDeleteLoaderCallback(), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2557,7 +2575,9 @@ TEST_F(URLLoaderTest, ReadPipeClosedWhileReadTaskPosted) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2637,7 +2657,7 @@ class MockCookieObserver : public network::mojom::CookieAccessObserver { // The full details are available for the tests to query manually, but // they are not covered by operator== (and testing::ElementsAre). GURL url; - net::CanonicalCookie::CookieInclusionStatus status; + net::CookieInclusionStatus status; }; mojo::PendingRemote<mojom::CookieAccessObserver> GetRemote() { @@ -2696,11 +2716,11 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { int32_t process_id, int32_t routing_id, const std::string& devtools_request_id, - const net::CookieStatusList& cookies_with_status, + const net::CookieAccessResultList& cookies_with_access_result, std::vector<network::mojom::HttpRawHeaderPairPtr> headers) override { raw_request_cookies_.insert(raw_request_cookies_.end(), - cookies_with_status.begin(), - cookies_with_status.end()); + cookies_with_access_result.begin(), + cookies_with_access_result.end()); devtools_request_id_ = devtools_request_id; @@ -2758,7 +2778,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { return raw_response_cookies_; } - const net::CookieStatusList& raw_request_cookies() const { + const net::CookieAccessResultList& raw_request_cookies() const { return raw_request_cookies_; } @@ -2775,7 +2795,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { std::string devtools_request_id_; base::Optional<std::string> raw_response_headers_; - net::CookieStatusList raw_request_cookies_; + net::CookieAccessResultList raw_request_cookies_; base::OnceClosure wait_for_raw_request_; size_t wait_for_raw_request_goal_ = 0u; @@ -2947,7 +2967,8 @@ TEST_F(URLLoaderTest, SetAuth) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -2993,7 +3014,8 @@ TEST_F(URLLoaderTest, CancelAuth) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3039,7 +3061,8 @@ TEST_F(URLLoaderTest, TwoChallenges) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3086,7 +3109,8 @@ TEST_F(URLLoaderTest, NoAuthRequiredForFavicon) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3132,7 +3156,8 @@ TEST_F(URLLoaderTest, HttpAuthResponseHeadersAvailable) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3176,7 +3201,8 @@ TEST_F(URLLoaderTest, CorbEffectiveWithCors) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3218,7 +3244,8 @@ TEST_F(URLLoaderTest, CorbExcludedWithNoCors) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3263,7 +3290,8 @@ TEST_F(URLLoaderTest, CorbEffectiveWithNoCorsWhenNoActualPlugin) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3300,7 +3328,9 @@ TEST_F(URLLoaderTest, FollowRedirectTwice) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3404,7 +3434,8 @@ TEST_F(URLLoaderTest, ClientAuthRespondTwice) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3457,9 +3488,9 @@ TEST_F(URLLoaderTest, ClientAuthDestroyResponder) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -3501,9 +3532,9 @@ TEST_F(URLLoaderTest, ClientAuthCancelConnection) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -3544,7 +3575,8 @@ TEST_F(URLLoaderTest, ClientAuthCancelCertificateSelection) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3596,7 +3628,8 @@ TEST_F(URLLoaderTest, ClientAuthNoCertificate) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3653,7 +3686,8 @@ TEST_F(URLLoaderTest, ClientAuthCertificateWithValidSignature) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3712,7 +3746,8 @@ TEST_F(URLLoaderTest, ClientAuthCertificateWithInvalidSignature) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3753,9 +3788,9 @@ TEST_F(URLLoaderTest, BlockAllCookies) { DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionBlockAllCookies, request, client()->CreateRemote(), - TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, - 0 /* request_id */, 0 /* keepalive_request_size */, - resource_scheduler_client(), nullptr, + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -3783,7 +3818,8 @@ TEST_F(URLLoaderTest, BlockOnlyThirdPartyCookies) { DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionBlockThirdPartyCookies, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3812,7 +3848,9 @@ TEST_F(URLLoaderTest, AllowAllCookies) { context(), &network_service_client, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3822,6 +3860,180 @@ TEST_F(URLLoaderTest, AllowAllCookies) { EXPECT_TRUE(url_loader->AllowCookies(first_party_url, site_for_cookies)); EXPECT_TRUE(url_loader->AllowCookies(third_party_url, site_for_cookies)); } + +// Tests that a request with CredentialsMode::kOmit still sends client +// certificates. This should be removed when crbug.com/775438 is fixed. +TEST_F(URLLoaderTest, CredentialsModeOmit) { + // Set up a server that requires certificates. + net::SSLServerConfig ssl_config; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; + net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); + test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); + test_server.AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("services/test/data"))); + ASSERT_TRUE(test_server.Start()); + + // Make sure the client has valid certificates. + std::unique_ptr<net::FakeClientCertIdentity> identity = + net::FakeClientCertIdentity::CreateFromCertAndKeyFiles( + net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); + ASSERT_TRUE(identity); + scoped_refptr<TestSSLPrivateKey> private_key = + base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); + + MockNetworkServiceClient network_service_client; + MockNetworkContextClient network_context_client; + network_context_client.set_certificate_response( + MockNetworkContextClient::CertificateResponse:: + VALID_CERTIFICATE_SIGNATURE); + network_context_client.set_private_key(private_key); + network_context_client.set_certificate(identity->certificate()); + + ResourceRequest request = + CreateResourceRequest("GET", test_server.GetURL("/simple_page.html")); + request.credentials_mode = mojom::CredentialsMode::kOmit; + + base::RunLoop delete_run_loop; + mojo::Remote<mojom::URLLoader> loader; + std::unique_ptr<URLLoader> url_loader; + mojom::URLLoaderFactoryParams params; + params.process_id = kProcessId; + params.is_corb_enabled = false; + url_loader = std::make_unique<URLLoader>( + context(), &network_service_client, &network_context_client, + DeleteLoaderCallback(&delete_run_loop, &url_loader), + loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + nullptr /* network_usage_accumulator */, nullptr /* header_client */, + nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, + mojo::NullRemote() /* cookie_observer */); + + client()->RunUntilComplete(); + delete_run_loop.Run(); + + EXPECT_EQ(net::OK, client()->completion_status().error_code); +} + +// Tests that a request with CredentialsMode::kOmitBug_775438_Workaround +// doesn't send client certificates. +TEST_F(URLLoaderTest, CredentialsModeOmitWorkaround) { + // Set up a server that requires certificates. + net::SSLServerConfig ssl_config; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; + net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); + test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); + test_server.AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("services/test/data"))); + ASSERT_TRUE(test_server.Start()); + + // Make sure the client has valid certificates. + std::unique_ptr<net::FakeClientCertIdentity> identity = + net::FakeClientCertIdentity::CreateFromCertAndKeyFiles( + net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); + ASSERT_TRUE(identity); + scoped_refptr<TestSSLPrivateKey> private_key = + base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); + + MockNetworkServiceClient network_service_client; + MockNetworkContextClient network_context_client; + network_context_client.set_certificate_response( + MockNetworkContextClient::CertificateResponse:: + VALID_CERTIFICATE_SIGNATURE); + network_context_client.set_private_key(private_key); + network_context_client.set_certificate(identity->certificate()); + + ResourceRequest request = + CreateResourceRequest("GET", test_server.GetURL("/simple_page.html")); + request.credentials_mode = mojom::CredentialsMode::kOmitBug_775438_Workaround; + + base::RunLoop delete_run_loop; + mojo::Remote<mojom::URLLoader> loader; + std::unique_ptr<URLLoader> url_loader; + mojom::URLLoaderFactoryParams params; + params.process_id = kProcessId; + params.is_corb_enabled = false; + url_loader = std::make_unique<URLLoader>( + context(), &network_service_client, &network_context_client, + DeleteLoaderCallback(&delete_run_loop, &url_loader), + loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + nullptr /* network_usage_accumulator */, nullptr /* header_client */, + nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, + mojo::NullRemote() /* cookie_observer */); + + client()->RunUntilComplete(); + delete_run_loop.Run(); + + EXPECT_EQ(0, network_context_client.on_certificate_requested_counter()); + EXPECT_NE(net::OK, client()->completion_status().error_code); +} + +// Tests that a request with CredentialsMode::kOmitBug_775438_Workaround +// doesn't send client certificates with a server that optionally requires +// certificates. +TEST_F(URLLoaderTest, CredentialsModeOmitWorkaroundWithOptionalCerts) { + // Set up a server that requires certificates. + net::SSLServerConfig ssl_config; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT; + net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); + test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); + test_server.AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("services/test/data"))); + ASSERT_TRUE(test_server.Start()); + + // Make sure the client has valid certificates. + std::unique_ptr<net::FakeClientCertIdentity> identity = + net::FakeClientCertIdentity::CreateFromCertAndKeyFiles( + net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); + ASSERT_TRUE(identity); + scoped_refptr<TestSSLPrivateKey> private_key = + base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); + + MockNetworkServiceClient network_service_client; + MockNetworkContextClient network_context_client; + network_context_client.set_certificate_response( + MockNetworkContextClient::CertificateResponse:: + VALID_CERTIFICATE_SIGNATURE); + network_context_client.set_private_key(private_key); + network_context_client.set_certificate(identity->certificate()); + + ResourceRequest request = + CreateResourceRequest("GET", test_server.GetURL("/simple_page.html")); + request.credentials_mode = mojom::CredentialsMode::kOmitBug_775438_Workaround; + + base::RunLoop delete_run_loop; + mojo::Remote<mojom::URLLoader> loader; + std::unique_ptr<URLLoader> url_loader; + mojom::URLLoaderFactoryParams params; + params.process_id = kProcessId; + params.is_corb_enabled = false; + url_loader = std::make_unique<URLLoader>( + context(), &network_service_client, &network_context_client, + DeleteLoaderCallback(&delete_run_loop, &url_loader), + loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + nullptr /* network_usage_accumulator */, nullptr /* header_client */, + nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, + mojo::NullRemote() /* cookie_observer */); + + client()->RunUntilComplete(); + delete_run_loop.Run(); + + EXPECT_EQ(0, network_context_client.on_certificate_requested_counter()); + EXPECT_EQ(net::OK, client()->completion_status().error_code); +} #endif // !defined(OS_IOS) TEST_F(URLLoaderTest, CookieReporting) { @@ -3843,9 +4055,11 @@ TEST_F(URLLoaderTest, CookieReporting) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -3875,9 +4089,11 @@ TEST_F(URLLoaderTest, CookieReporting) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -3914,7 +4130,8 @@ TEST_F(URLLoaderTest, CookieReportingRedirect) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -3958,9 +4175,11 @@ TEST_F(URLLoaderTest, CookieReportingAuth) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -4003,9 +4222,11 @@ TEST_F(URLLoaderTest, RawRequestCookies) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4019,8 +4240,8 @@ TEST_F(URLLoaderTest, RawRequestCookies) { network_service_client.raw_request_cookies()[0].cookie.Name()); EXPECT_EQ("b", network_service_client.raw_request_cookies()[0].cookie.Value()); - EXPECT_TRUE( - network_service_client.raw_request_cookies()[0].status.IsInclude()); + EXPECT_TRUE(network_service_client.raw_request_cookies()[0] + .access_result.status.IsInclude()); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4054,9 +4275,11 @@ TEST_F(URLLoaderTest, RawRequestCookiesFlagged) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4071,9 +4294,8 @@ TEST_F(URLLoaderTest, RawRequestCookiesFlagged) { EXPECT_EQ("b", network_service_client.raw_request_cookies()[0].cookie.Value()); EXPECT_TRUE(network_service_client.raw_request_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_NOT_ON_PATH})); + .access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH})); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4098,9 +4320,11 @@ TEST_F(URLLoaderTest, RawResponseCookies) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4145,9 +4369,11 @@ TEST_F(URLLoaderTest, RawResponseCookiesInvalid) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4159,10 +4385,10 @@ TEST_F(URLLoaderTest, RawResponseCookiesInvalid) { network_service_client.WaitUntilRawResponse(1u); // On these failures the cookie object is not created EXPECT_FALSE(network_service_client.raw_response_cookies()[0].cookie); - EXPECT_TRUE(network_service_client.raw_response_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_FAILURE_TO_STORE})); + EXPECT_TRUE( + network_service_client.raw_response_cookies()[0] + .status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE})); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4191,7 +4417,9 @@ TEST_F(URLLoaderTest, RawResponseCookiesRedirect) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -4243,7 +4471,9 @@ TEST_F(URLLoaderTest, RawResponseCookiesRedirect) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.BindNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -4262,8 +4492,7 @@ TEST_F(URLLoaderTest, RawResponseCookiesRedirect) { network_service_client.raw_response_cookies()[0].cookie->IsSecure()); EXPECT_TRUE(network_service_client.raw_response_cookies()[0] .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_SECURE_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); } } @@ -4291,9 +4520,11 @@ TEST_F(URLLoaderTest, RawResponseCookiesAuth) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4336,9 +4567,11 @@ TEST_F(URLLoaderTest, RawResponseCookiesAuth) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4352,8 +4585,7 @@ TEST_F(URLLoaderTest, RawResponseCookiesAuth) { network_service_client.raw_response_cookies()[0].cookie->IsSecure()); EXPECT_TRUE(network_service_client.raw_response_cookies()[0] .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_SECURE_ONLY})); + {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4379,9 +4611,11 @@ TEST_F(URLLoaderTest, RawResponseQUIC) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, mojo::NullRemote() /* cookie_observer */); @@ -4431,9 +4665,11 @@ TEST_F(URLLoaderTest, CookieReportingCategories) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -4452,11 +4688,11 @@ TEST_F(URLLoaderTest, CookieReportingCategories) { if (net::cookie_util::IsSameSiteByDefaultCookiesEnabled()) { EXPECT_TRUE(cookie_observer.observed_cookies()[0] .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: + {net::CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); } EXPECT_TRUE(cookie_observer.observed_cookies()[0].status.HasWarningReason( - net::CanonicalCookie::CookieInclusionStatus::WarningReason:: + net::CookieInclusionStatus::WarningReason:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT)); } @@ -4486,9 +4722,11 @@ TEST_F(URLLoaderTest, CookieReportingCategories) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -4501,10 +4739,10 @@ TEST_F(URLLoaderTest, CookieReportingCategories) { EXPECT_THAT(cookie_observer.observed_cookies(), testing::ElementsAre(MockCookieObserver::CookieDetails{ CookieAccessType::kChange, "a", "b", false})); - EXPECT_TRUE(cookie_observer.observed_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( - {net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_USER_PREFERENCES})); + EXPECT_TRUE( + cookie_observer.observed_cookies()[0] + .status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); test_network_delegate()->set_cookie_options(0); } @@ -4528,9 +4766,11 @@ TEST_F(URLLoaderTest, CookieReportingCategories) { context(), &network_service_client, &network_context_client, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), mojom::kURLLoadOptionNone, - request, loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, - ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, - 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, + request, loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, + 0 /* request_id */, 0 /* keepalive_request_size */, + resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, cookie_observer.GetRemote()); @@ -4620,7 +4860,9 @@ TEST_F(URLLoaderTest, OriginPolicyManagerCalled) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -4672,7 +4914,9 @@ TEST_F(URLLoaderTest, OriginPolicyManagerCalled) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -4711,7 +4955,9 @@ TEST_F(URLLoaderTest, OriginPolicyManagerCalled) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -4756,7 +5002,9 @@ TEST_F(URLLoaderTest, OriginPolicyManagerCalled) { nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader.InitWithNewPipeAndPassReceiver(), 0, request, - loader_client.CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + loader_client.CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -5001,7 +5249,8 @@ TEST_P(URLLoaderSyncOrAsyncTrustTokenOperationTest, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -5056,7 +5305,8 @@ TEST_P(URLLoaderSyncOrAsyncTrustTokenOperationTest, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -5100,7 +5350,8 @@ TEST_P(URLLoaderSyncOrAsyncTrustTokenOperationTest, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -5143,7 +5394,8 @@ TEST_P(URLLoaderSyncOrAsyncTrustTokenOperationTest, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, @@ -5185,7 +5437,8 @@ TEST_P(URLLoaderSyncOrAsyncTrustTokenOperationTest, nullptr /* network_context_client */, DeleteLoaderCallback(&delete_run_loop, &url_loader), loader_remote.InitWithNewPipeAndPassReceiver(), 0, request, - client()->CreateRemote(), TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, + client()->CreateRemote(), /*reponse_body_use_tracker=*/base::nullopt, + TRAFFIC_ANNOTATION_FOR_TESTS, ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, 0 /* keepalive_request_size */, resource_scheduler_client(), nullptr, nullptr /* network_usage_accumulator */, nullptr /* header_client */, diff --git a/chromium/services/network/websocket.cc b/chromium/services/network/websocket.cc index ee535a0aa2c..dcf9d7ccb77 100644 --- a/chromium/services/network/websocket.cc +++ b/chromium/services/network/websocket.cc @@ -11,9 +11,11 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/numerics/safe_conversions.h" #include "base/single_thread_task_runner.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" @@ -36,11 +38,16 @@ #include "net/websockets/websocket_frame.h" // for WebSocketFrameHeader::OpCode #include "net/websockets/websocket_handshake_request_info.h" #include "net/websockets/websocket_handshake_response_info.h" +#include "services/network/public/cpp/features.h" #include "services/network/websocket_factory.h" namespace network { namespace { +// What is considered a "small message" for the purposes of small message +// reassembly. +constexpr uint64_t kSmallMessageThreshhold = 1 << 16; + // Convert a mojom::WebSocketMessageType to a // net::WebSocketFrameHeader::OpCode net::WebSocketFrameHeader::OpCode MessageTypeToOpCode( @@ -118,14 +125,13 @@ class WebSocket::WebSocketEventHandler final void OnAddChannelResponse( std::unique_ptr<net::WebSocketHandshakeResponseInfo> response, const std::string& selected_subprotocol, - const std::string& extensions, - int64_t send_flow_control_quota) override; + const std::string& extensions) override; void OnDataFrame(bool fin, WebSocketMessageType type, base::span<const char> payload) override; + void OnSendDataFrameDone() override; bool HasPendingDataFrames() override; void OnClosingHandshake() override; - void OnSendFlowControlQuotaAdded(int64_t quota) override; void OnDropChannel(bool was_clean, uint16_t code, const std::string& reason) override; @@ -172,8 +178,7 @@ void WebSocket::WebSocketEventHandler::OnCreateURLRequest( void WebSocket::WebSocketEventHandler::OnAddChannelResponse( std::unique_ptr<net::WebSocketHandshakeResponseInfo> response, const std::string& selected_protocol, - const std::string& extensions, - int64_t send_flow_control_quota) { + const std::string& extensions) { DVLOG(3) << "WebSocketEventHandler::OnAddChannelResponse @" << reinterpret_cast<void*>(this) << " selected_protocol=\"" << selected_protocol << "\"" @@ -206,6 +211,7 @@ void WebSocket::WebSocketEventHandler::OnAddChannelResponse( impl_->Reset(); return; } + impl_->data_pipe_use_tracker_.Activate(); const MojoResult mojo_result = impl_->writable_watcher_.Watch( impl_->writable_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_WATCH_CONDITION_SATISFIED, @@ -241,8 +247,6 @@ void WebSocket::WebSocketEventHandler::OnAddChannelResponse( impl_->header_client_.reset(); impl_->client_.set_disconnect_handler(base::BindOnce( &WebSocket::OnConnectionError, base::Unretained(impl_), FROM_HERE)); - - impl_->client_->AddSendFlowControlQuota(send_flow_control_quota); } void WebSocket::WebSocketEventHandler::OnDataFrame( @@ -259,6 +263,11 @@ void WebSocket::WebSocketEventHandler::OnDataFrame( impl_->SendPendingDataFrames(); } +void WebSocket::WebSocketEventHandler::OnSendDataFrameDone() { + impl_->ResumeDataPipeReading(); + return; +} + bool WebSocket::WebSocketEventHandler::HasPendingDataFrames() { return !impl_->pending_data_frames_.empty(); } @@ -270,14 +279,6 @@ void WebSocket::WebSocketEventHandler::OnClosingHandshake() { impl_->client_->OnClosingHandshake(); } -void WebSocket::WebSocketEventHandler::OnSendFlowControlQuotaAdded( - int64_t quota) { - DVLOG(3) << "WebSocketEventHandler::OnSendFlowControlQuotaAdded @" - << reinterpret_cast<void*>(this) << " quota=" << quota; - - impl_->client_->AddSendFlowControlQuota(quota); -} - void WebSocket::WebSocketEventHandler::OnDropChannel( bool was_clean, uint16_t code, @@ -370,6 +371,14 @@ int WebSocket::WebSocketEventHandler::OnAuthRequired( return net::ERR_IO_PENDING; } +struct WebSocket::CloseInfo { + CloseInfo(uint16_t code, const std::string& reason) + : code(code), reason(reason) {} + + const uint16_t code; + const std::string reason; +}; + WebSocket::WebSocket( WebSocketFactory* factory, const GURL& url, @@ -386,6 +395,7 @@ WebSocket::WebSocket( mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client, WebSocketThrottler::PendingConnection pending_connection_tracker, + DataPipeUseTracker data_pipe_use_tracker, base::TimeDelta delay) : factory_(factory), handshake_client_(std::move(handshake_client)), @@ -404,7 +414,10 @@ WebSocket::WebSocket( base::ThreadTaskRunnerHandle::Get()), readable_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, - base::ThreadTaskRunnerHandle::Get()) { + base::ThreadTaskRunnerHandle::Get()), + data_pipe_use_tracker_(std::move(data_pipe_use_tracker)), + reassemble_short_messages_(base::FeatureList::IsEnabled( + network::features::kWebSocketReassembleShortMessages)) { DCHECK(handshake_client_); // If |require_network_isolation_key| is set on the URLRequestContext, // |isolation_info| must not be empty. @@ -447,32 +460,6 @@ WebSocket::~WebSocket() { // static const void* const WebSocket::kUserDataKey = &WebSocket::kUserDataKey; -void WebSocket::SendFrame(bool fin, - mojom::WebSocketMessageType type, - base::span<const uint8_t> data) { - DVLOG(3) << "WebSocket::SendFrame @" << reinterpret_cast<void*>(this) - << " fin=" << fin << " type=" << type << " data is " << data.size() - << " bytes"; - - DCHECK(channel_) - << "WebSocket::SendFrame is called but there is no active channel."; - DCHECK(handshake_succeeded_); - // This is guaranteed by the maximum size enforced on mojo messages. - DCHECK_LE(data.size(), static_cast<size_t>(INT_MAX)); - - // This is guaranteed by mojo. - DCHECK(IsKnownEnumValue(type)); - - // TODO(darin): Avoid this copy. - auto data_to_pass = base::MakeRefCounted<net::IOBuffer>(data.size()); - memcpy(data_to_pass->data(), data.data(), data.size()); - - // It's okay to ignore the result here because we don't access |this| after - // this point. - ignore_result(channel_->SendFrame(fin, MessageTypeToOpCode(type), - std::move(data_to_pass), data.size())); -} - void WebSocket::SendMessage(mojom::WebSocketMessageType type, uint64_t data_length) { DVLOG(3) << "WebSocket::SendMessage @" << reinterpret_cast<void*>(this) @@ -489,11 +476,15 @@ void WebSocket::SendMessage(mojom::WebSocketMessageType type, } DCHECK(IsKnownEnumValue(type)); - pending_send_data_frames_.push(DataFrame(type, data_length)); + const bool do_not_fragment = + reassemble_short_messages_ && data_length <= kSmallMessageThreshhold; + + pending_send_data_frames_.push(DataFrame(type, data_length, do_not_fragment)); // Safe if ReadAndSendFromDataPipe() deletes |this| because this method is // only called from mojo. - ReadAndSendFromDataPipe(); + if (!blocked_on_websocket_channel_) + ReadAndSendFromDataPipe(); } void WebSocket::StartReceiving() { @@ -503,13 +494,19 @@ void WebSocket::StartReceiving() { void WebSocket::StartClosingHandshake(uint16_t code, const std::string& reason) { - DVLOG(3) << "WebSocket::StartClosingHandshake @" - << reinterpret_cast<void*>(this) << " code=" << code << " reason=\"" - << reason << "\""; + DVLOG(3) << "WebSocket::StartClosingHandshake @" << this << " code=" << code + << " reason=\"" << reason << "\""; - DCHECK(channel_) - << "WebSocket::SendFrame is called but there is no active channel."; + DCHECK(channel_) << "WebSocket::StartClosingHandshake is called but there is " + "no active channel."; DCHECK(handshake_succeeded_); + if (!pending_send_data_frames_.empty()) { + // This has only been observed happening on Windows 7, but the Mojo API + // doesn't guarantee that it won't happen on other platforms. + pending_start_closing_handshake_ = + std::make_unique<CloseInfo>(code, reason); + return; + } ignore_result(channel_->StartClosingHandshake(code, reason)); } @@ -628,6 +625,7 @@ void WebSocket::SendPendingDataFrames() { << reinterpret_cast<void*>(this) << ", pending_data_frames_.size=" << pending_data_frames_.size() << ", wait_for_writable_?" << wait_for_writable_; + if (wait_for_writable_) { return; } @@ -714,7 +712,9 @@ void WebSocket::ReadAndSendFromDataPipe() { &buffer, &readable_size, MOJO_READ_DATA_FLAG_NONE); if (begin_result == MOJO_RESULT_SHOULD_WAIT) { wait_for_readable_ = true; - readable_watcher_.ArmOrNotify(); + if (!blocked_on_websocket_channel_) { + readable_watcher_.ArmOrNotify(); + } return; } if (begin_result == MOJO_RESULT_FAILED_PRECONDITION) { @@ -722,18 +722,55 @@ void WebSocket::ReadAndSendFromDataPipe() { } DCHECK_EQ(begin_result, MOJO_RESULT_OK); + if (readable_size < data_frame.data_length && data_frame.do_not_fragment && + !message_under_reassembly_) { + // The cast is needed to unambiguously select a constructor on 32-bit + // platforms. + message_under_reassembly_ = base::MakeRefCounted<net::IOBuffer>( + base::checked_cast<size_t>(data_frame.data_length)); + DCHECK_EQ(bytes_reassembled_, 0u); + } + + if (message_under_reassembly_) { + const size_t bytes_to_copy = + std::min(static_cast<uint64_t>(readable_size), + data_frame.data_length - bytes_reassembled_); + memcpy(message_under_reassembly_->data() + bytes_reassembled_, buffer, + bytes_to_copy); + bytes_reassembled_ += bytes_to_copy; + + const MojoResult end_result = readable_->EndReadData(bytes_to_copy); + DCHECK_EQ(end_result, MOJO_RESULT_OK); + + DCHECK_LE(bytes_reassembled_, data_frame.data_length); + if (bytes_reassembled_ == data_frame.data_length) { + bytes_reassembled_ = 0; + blocked_on_websocket_channel_ = true; + if (channel_->SendFrame( + /*fin=*/true, MessageTypeToOpCode(data_frame.type), + std::move(message_under_reassembly_), data_frame.data_length) == + net::WebSocketChannel::CHANNEL_DELETED) { + // |this| has been deleted. + return; + } + pending_send_data_frames_.pop(); + } + + continue; + } + const size_t size_to_send = std::min(static_cast<uint64_t>(readable_size), data_frame.data_length); auto data_to_pass = base::MakeRefCounted<net::IOBuffer>(size_to_send); const bool is_final = (size_to_send == data_frame.data_length); memcpy(data_to_pass->data(), buffer, size_to_send); + blocked_on_websocket_channel_ = true; if (channel_->SendFrame(is_final, MessageTypeToOpCode(data_frame.type), std::move(data_to_pass), size_to_send) == net::WebSocketChannel::CHANNEL_DELETED) { // |this| has been deleted. return; } - const MojoResult end_result = readable_->EndReadData(size_to_send); DCHECK_EQ(end_result, MOJO_RESULT_OK); @@ -746,7 +783,17 @@ void WebSocket::ReadAndSendFromDataPipe() { data_frame.type = mojom::WebSocketMessageType::CONTINUATION; data_frame.data_length -= size_to_send; } - return; + if (pending_start_closing_handshake_) { + std::unique_ptr<CloseInfo> close_info = + std::move(pending_start_closing_handshake_); + ignore_result( + channel_->StartClosingHandshake(close_info->code, close_info->reason)); + } +} + +void WebSocket::ResumeDataPipeReading() { + blocked_on_websocket_channel_ = false; + readable_watcher_.ArmOrNotify(); } void WebSocket::OnSSLCertificateErrorResponse( @@ -811,6 +858,7 @@ void WebSocket::Reset() { auth_handler_.reset(); header_client_.reset(); receiver_.reset(); + data_pipe_use_tracker_.Reset(); // net::WebSocketChannel requires that we delete it at this point. channel_.reset(); diff --git a/chromium/services/network/websocket.h b/chromium/services/network/websocket.h index 234d92ff3ef..4d4f3ef8c08 100644 --- a/chromium/services/network/websocket.h +++ b/chromium/services/network/websocket.h @@ -15,6 +15,7 @@ #include "base/containers/queue.h" #include "base/containers/span.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/time/time.h" @@ -22,6 +23,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/websockets/websocket_event_interface.h" +#include "services/network/network_service.h" #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/websocket.mojom.h" #include "services/network/websocket_throttler.h" @@ -34,9 +36,10 @@ class Location; } // namespace base namespace net { +class IOBuffer; class IsolationInfo; -class SiteForCookies; class SSLInfo; +class SiteForCookies; class WebSocketChannel; } // namespace net @@ -66,13 +69,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client, WebSocketThrottler::PendingConnection pending_connection_tracker, + DataPipeUseTracker, base::TimeDelta delay); ~WebSocket() override; // mojom::WebSocket methods: - void SendFrame(bool fin, - mojom::WebSocketMessageType type, - base::span<const uint8_t> data) override; void SendMessage(mojom::WebSocketMessageType type, uint64_t data_length) override; void StartReceiving() override; @@ -102,6 +103,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { private: class WebSocketEventHandler; + struct CloseInfo; // This class is used to set the WebSocket as user data on a URLRequest. This // is used instead of WebSocket directly because SetUserData requires a @@ -120,10 +122,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { }; struct DataFrame final { - DataFrame(mojom::WebSocketMessageType type, uint64_t data_length) - : type(type), data_length(data_length) {} + DataFrame(mojom::WebSocketMessageType type, + uint64_t data_length, + bool do_not_fragment) + : type(type), + data_length(data_length), + do_not_fragment(do_not_fragment) {} mojom::WebSocketMessageType type; uint64_t data_length; + const bool do_not_fragment; }; void OnConnectionError(const base::Location& set_from); @@ -165,6 +172,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { // ReadAndSendFromDataPipe() may indirectly delete |this|. void ReadAndSendFromDataPipe(); + void ResumeDataPipeReading(); // |factory_| owns |this|. WebSocketFactory* const factory_; @@ -210,6 +218,30 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { mojo::SimpleWatcher readable_watcher_; base::queue<DataFrame> pending_send_data_frames_; bool wait_for_readable_ = false; + bool blocked_on_websocket_channel_ = false; + + DataPipeUseTracker data_pipe_use_tracker_; + + // True if we should preserve the old behaviour where <=64KB messages were + // never fragmented. + // TODO(ricea): Remove the flag once we know whether we really need this or + // not. See https://crbug.com/1086273. + const bool reassemble_short_messages_; + + // Temporary buffer for storage of short messages that have been fragmented by + // the data pipe. Only messages that are actually fragmented are copied into + // here. + scoped_refptr<net::IOBuffer> message_under_reassembly_; + + // Number of bytes that have been written to |message_under_reassembly_| so + // far. + size_t bytes_reassembled_ = 0; + + // Set when StartClosingHandshake() is called while + // |pending_send_data_frames_| is non-empty. This can happen due to a race + // condition between the readable signal on the data pipe and the channel on + // which StartClosingHandshake() is called. + std::unique_ptr<CloseInfo> pending_start_closing_handshake_; base::WeakPtrFactory<WebSocket> weak_ptr_factory_{this}; diff --git a/chromium/services/network/websocket_factory.cc b/chromium/services/network/websocket_factory.cc index 37c59b0dfa2..597b4bcfdc6 100644 --- a/chromium/services/network/websocket_factory.cc +++ b/chromium/services/network/websocket_factory.cc @@ -64,6 +64,7 @@ void WebSocketFactory::CreateWebSocket( options, has_raw_headers_access, std::move(handshake_client), std::move(auth_handler), std::move(header_client), throttler_.IssuePendingConnectionTracker(process_id), + DataPipeUseTracker(context_->network_service(), DataPipeUser::kWebSocket), throttler_.CalculateDelay(process_id))); } |