| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/strings/strcat.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" |
| #include "net/test/embedded_test_server/default_handlers.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "services/cert_verifier/cert_verifier_service_factory.h" |
| #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.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/cert_verifier_service.mojom.h" |
| #include "services/network/public/mojom/network_context.mojom.h" |
| #include "services/network/test/test_url_loader_client.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace cert_verifier { |
| namespace { |
| |
| mojo::PendingRemote<mojom::CertVerifierService> GetNewCertVerifierServiceRemote( |
| mojom::CertVerifierServiceFactory* cert_verifier_service_factory, |
| mojom::CertVerifierCreationParamsPtr creation_params) { |
| mojo::PendingRemote<mojom::CertVerifierService> cert_verifier_remote; |
| cert_verifier_service_factory->GetNewCertVerifier( |
| cert_verifier_remote.InitWithNewPipeAndPassReceiver(), |
| std::move(creation_params)); |
| return cert_verifier_remote; |
| } |
| |
| } // namespace |
| |
| class NetworkContextWithRealCertVerifierTest : public testing::Test { |
| public: |
| explicit NetworkContextWithRealCertVerifierTest( |
| base::test::TaskEnvironment::TimeSource time_source = |
| base::test::TaskEnvironment::TimeSource::DEFAULT) |
| : task_environment_(base::test::TaskEnvironment::MainThreadType::IO, |
| time_source), |
| network_change_notifier_( |
| net::NetworkChangeNotifier::CreateMockIfNeeded()), |
| network_service_(network::NetworkService::CreateForTesting()) {} |
| ~NetworkContextWithRealCertVerifierTest() override = default; |
| |
| std::unique_ptr<network::NetworkContext> CreateContextWithParams( |
| network::mojom::NetworkContextParamsPtr context_params) { |
| network_context_remote_.reset(); |
| return std::make_unique<network::NetworkContext>( |
| network_service_.get(), |
| network_context_remote_.BindNewPipeAndPassReceiver(), |
| std::move(context_params)); |
| } |
| |
| network::mojom::CertVerifierServiceRemoteParamsPtr GetCertVerifierParams( |
| mojom::CertVerifierCreationParamsPtr cert_verifier_creation_params = |
| mojom::CertVerifierCreationParams::New()) { |
| if (!cert_verifier_service_factory_) { |
| cert_verifier_service_factory_ = |
| std::make_unique<CertVerifierServiceFactoryImpl>( |
| GetCertVerifierServiceParams(), |
| cert_verifier_service_factory_remote_ |
| .BindNewPipeAndPassReceiver()); |
| } |
| |
| // Create a cert verifier service. |
| auto cert_verifier_service_remote = GetNewCertVerifierServiceRemote( |
| cert_verifier_service_factory_.get(), |
| std::move(cert_verifier_creation_params)); |
| |
| return network::mojom::CertVerifierServiceRemoteParams::New( |
| std::move(cert_verifier_service_remote)); |
| } |
| |
| virtual mojom::CertVerifierServiceParamsPtr GetCertVerifierServiceParams() { |
| return nullptr; |
| } |
| |
| network::mojom::NetworkService* network_service() const { |
| return network_service_.get(); |
| } |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_; |
| std::unique_ptr<network::NetworkService> network_service_; |
| // Stores the mojo::Remote<NetworkContext> of the most recently created |
| // NetworkContext. Not strictly needed, but seems best to mimic real-world |
| // usage. |
| mojo::Remote<network::mojom::NetworkContext> network_context_remote_; |
| |
| mojo::Remote<mojom::CertVerifierServiceFactory> |
| cert_verifier_service_factory_remote_; |
| std::unique_ptr<mojom::CertVerifierServiceFactory> |
| cert_verifier_service_factory_; |
| }; |
| |
| #if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) |
| namespace { |
| |
| network::mojom::NetworkContextParamsPtr CreateContextParams() { |
| network::mojom::NetworkContextParamsPtr params = |
| network::mojom::NetworkContextParams::New(); |
| // Use a fixed proxy config, to avoid dependencies on local network |
| // configuration. |
| params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect(); |
| return params; |
| } |
| |
| std::unique_ptr<network::TestURLLoaderClient> FetchRequest( |
| const network::ResourceRequest& request, |
| network::NetworkContext* network_context, |
| int url_loader_options = network::mojom::kURLLoadOptionNone, |
| int process_id = network::mojom::kBrowserProcessId, |
| network::mojom::URLLoaderFactoryParamsPtr params = nullptr) { |
| mojo::Remote<network::mojom::URLLoaderFactory> loader_factory; |
| if (!params) |
| params = network::mojom::URLLoaderFactoryParams::New(); |
| params->process_id = process_id; |
| params->is_corb_enabled = false; |
| |
| // If |site_for_cookies| is null, any non-empty NIK is fine. Otherwise, the |
| // NIK must be consistent with |site_for_cookies|. |
| if (request.site_for_cookies.IsNull()) { |
| params->isolation_info = net::IsolationInfo::Create( |
| net::IsolationInfo::RequestType::kOther, |
| url::Origin::Create(GURL("https://abc.invalid")), |
| url::Origin::Create(GURL("https://xyz.invalid")), |
| request.site_for_cookies); |
| } else { |
| params->isolation_info = net::IsolationInfo::CreateForInternalRequest( |
| url::Origin::Create(request.site_for_cookies.RepresentativeUrl())); |
| } |
| |
| network_context->CreateURLLoaderFactory( |
| loader_factory.BindNewPipeAndPassReceiver(), std::move(params)); |
| |
| auto client = std::make_unique<network::TestURLLoaderClient>(); |
| mojo::PendingRemote<network::mojom::URLLoader> loader; |
| loader_factory->CreateLoaderAndStart( |
| loader.InitWithNewPipeAndPassReceiver(), 0 /* request_id */, |
| url_loader_options, request, client->CreateRemote(), |
| net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| client->RunUntilComplete(); |
| return client; |
| } |
| |
| } // namespace |
| |
| class NetworkContextCertVerifierBuiltinFeatureFlagTest |
| : public NetworkContextWithRealCertVerifierTest, |
| public testing::WithParamInterface< |
| std::tuple<bool, absl::optional<bool>>> { |
| public: |
| NetworkContextCertVerifierBuiltinFeatureFlagTest() { |
| scoped_feature_list_.InitWithFeatureState( |
| net::features::kCertVerifierBuiltinFeature, |
| feature_use_builtin_cert_verifier()); |
| } |
| |
| mojom::CertVerifierServiceParamsPtr GetCertVerifierServiceParams() override { |
| mojom::CertVerifierServiceParamsPtr params; |
| if (param_use_builtin_cert_verifier().has_value()) { |
| params = mojom::CertVerifierServiceParams::New(); |
| params->use_builtin_cert_verifier = *param_use_builtin_cert_verifier(); |
| } |
| return params; |
| } |
| |
| bool feature_use_builtin_cert_verifier() const { |
| return std::get<0>(GetParam()); |
| } |
| |
| absl::optional<bool> param_use_builtin_cert_verifier() const { |
| return std::get<1>(GetParam()); |
| } |
| |
| bool expected_use_builtin_cert_verifier() const { |
| if (param_use_builtin_cert_verifier().has_value()) |
| return *param_use_builtin_cert_verifier(); |
| return feature_use_builtin_cert_verifier(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| TEST_P(NetworkContextCertVerifierBuiltinFeatureFlagTest, |
| CombinationOfFeatureFlagAndServiceParamsUsesCorrectVerifier) { |
| net::EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::test_server::RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // This just happens to be a 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. |
| network::mojom::NetworkContextParamsPtr params = CreateContextParams(); |
| params->cert_verifier_params = GetCertVerifierParams(); |
| std::unique_ptr<network::NetworkContext> network_context = |
| CreateContextWithParams(std::move(params)); |
| |
| network::ResourceRequest request; |
| request.url = test_server.GetURL("/nocontent"); |
| base::HistogramTester histogram_tester; |
| std::unique_ptr<network::TestURLLoaderClient> client = |
| FetchRequest(request, network_context.get()); |
| EXPECT_EQ(net::OK, client->completion_status().error_code); |
| histogram_tester.ExpectTotalCount( |
| kBuiltinVerifierHistogram, expected_use_builtin_cert_verifier() ? 1 : 0); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| NetworkContextCertVerifierBuiltinFeatureFlagTest, |
| ::testing::Combine(::testing::Bool(), |
| ::testing::Values(absl::nullopt, false, true)), |
| [](const testing::TestParamInfo< |
| NetworkContextCertVerifierBuiltinFeatureFlagTest::ParamType>& info) { |
| return base::StrCat( |
| {std::get<0>(info.param) ? "FeatureTrue" : "FeatureFalse", |
| std::get<1>(info.param).has_value() |
| ? (*std::get<1>(info.param) ? "ParamTrue" : "ParamFalse") |
| : "ParamNotSet"}); |
| }); |
| #endif // BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) |
| |
| // TODO(mattm): can a root store test be added too? Is there any histogram |
| // that can be checked? |
| |
| } // namespace cert_verifier |