123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- // 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 <memory>
- #include "base/feature_list.h"
- #include "base/files/file_util.h"
- #include "base/run_loop.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/task_traits.h"
- #include "base/test/bind.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/test/task_environment.h"
- #include "build/build_config.h"
- #include "mojo/public/cpp/bindings/pending_remote.h"
- #include "net/base/test_completion_callback.h"
- #include "net/cert/asn1_util.h"
- #include "net/cert/test_root_certs.h"
- #include "net/cert/x509_util.h"
- #include "net/test/cert_test_util.h"
- #include "net/test/embedded_test_server/embedded_test_server.h"
- #include "net/test/gtest_util.h"
- #include "net/test/test_data_directory.h"
- #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
- #include "net/url_request/url_request_context.h"
- #include "services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader.h"
- #include "services/cert_verifier/cert_verifier_service_factory.h"
- #include "services/network/network_context.h"
- #include "services/network/network_service.h"
- #include "services/network/public/cpp/cert_verifier/mojo_cert_verifier.h"
- #include "services/network/public/cpp/features.h"
- #include "services/network/public/cpp/resource_request.h"
- #include "services/network/public/mojom/cert_verifier_service.mojom.h"
- #include "services/network/public/mojom/network_context.mojom.h"
- #include "services/network/ssl_config_service_mojo.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"
- namespace cert_verifier {
- namespace {
- const base::FilePath::CharType kServicesTestData[] =
- FILE_PATH_LITERAL("services/test/data");
- } // namespace
- // Base class for any tests that just need a NetworkService and a
- // TaskEnvironment, and to create NetworkContexts using the NetworkService.
- class NetworkServiceIntegrationTest : public testing::Test {
- public:
- NetworkServiceIntegrationTest()
- : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
- service_(network::NetworkService::CreateForTesting()),
- cert_verifier_service_impl_(
- cert_verifier_service_remote_.BindNewPipeAndPassReceiver()) {}
- ~NetworkServiceIntegrationTest() override = default;
- void DestroyService() { service_.reset(); }
- network::mojom::NetworkContextParamsPtr CreateNetworkContextParams() {
- mojo::PendingRemote<mojom::CertVerifierService> cv_service_remote;
- // Create a cert verifier service.
- cert_verifier_service_impl_.GetNewCertVerifierForTesting(
- cv_service_remote.InitWithNewPipeAndPassReceiver(),
- mojom::CertVerifierCreationParams::New(),
- &cert_net_fetcher_url_loader_);
- network::mojom::NetworkContextParamsPtr params =
- network::mojom::NetworkContextParams::New();
- params->cert_verifier_params =
- network::mojom::CertVerifierServiceRemoteParams::New(
- std::move(cv_service_remote));
- // Use a fixed proxy config, to avoid dependencies on local network
- // configuration.
- params->initial_proxy_config =
- net::ProxyConfigWithAnnotation::CreateDirect();
- return params;
- }
- void CreateNetworkContext(network::mojom::NetworkContextParamsPtr params) {
- network_context_ = std::make_unique<network::NetworkContext>(
- service_.get(), network_context_remote_.BindNewPipeAndPassReceiver(),
- std::move(params));
- }
- void LoadURL(const GURL& url,
- int options = network::mojom::kURLLoadOptionNone) {
- network::ResourceRequest request;
- request.url = url;
- request.method = "GET";
- request.request_initiator = url::Origin();
- StartLoadingURL(request, 0 /* process_id */, options);
- client_->RunUntilComplete();
- }
- void StartLoadingURL(const network::ResourceRequest& request,
- uint32_t process_id,
- int options = network::mojom::kURLLoadOptionNone) {
- client_ = std::make_unique<network::TestURLLoaderClient>();
- mojo::Remote<network::mojom::URLLoaderFactory> loader_factory;
- network::mojom::URLLoaderFactoryParamsPtr params =
- network::mojom::URLLoaderFactoryParams::New();
- params->process_id = process_id;
- params->is_corb_enabled = false;
- network_context_->CreateURLLoaderFactory(
- loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
- loader_.reset();
- loader_factory->CreateLoaderAndStart(
- loader_.BindNewPipeAndPassReceiver(), 1, options, request,
- client_->CreateRemote(),
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
- }
- network::TestURLLoaderClient* client() { return client_.get(); }
- base::test::TaskEnvironment* task_environment() { return &task_environment_; }
- network::NetworkService* service() const { return service_.get(); }
- network::NetworkContext* network_context() { return network_context_.get(); }
- mojo::Remote<network::mojom::NetworkContext>& network_context_remote() {
- return network_context_remote_;
- }
- CertNetFetcherURLLoader* cert_net_fetcher_url_loader() {
- return cert_net_fetcher_url_loader_.get();
- }
- private:
- base::test::TaskEnvironment task_environment_;
- std::unique_ptr<network::NetworkService> service_;
- mojo::Remote<network::mojom::NetworkContext> network_context_remote_;
- std::unique_ptr<network::NetworkContext> network_context_;
- mojo::Remote<mojom::CertVerifierServiceFactory> cert_verifier_service_remote_;
- CertVerifierServiceFactoryImpl cert_verifier_service_impl_;
- scoped_refptr<CertNetFetcherURLLoader> cert_net_fetcher_url_loader_;
- std::unique_ptr<network::TestURLLoaderClient> client_;
- mojo::Remote<network::mojom::URLLoader> loader_;
- };
- // CRLSets are not supported on iOS and Android system verifiers.
- #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
- class NetworkServiceCRLSetTest : public NetworkServiceIntegrationTest {
- public:
- NetworkServiceCRLSetTest()
- : test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
- ~NetworkServiceCRLSetTest() override = default;
- void SetUp() override {
- NetworkServiceIntegrationTest::SetUp();
- test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
- test_server_.AddDefaultHandlers(base::FilePath(kServicesTestData));
- ASSERT_TRUE(test_server_.Start());
- }
- GURL GetURL(std::string url) { return test_server_.GetURL(std::move(url)); }
- private:
- net::EmbeddedTestServer test_server_;
- };
- // Verifies CRLSets take effect if configured on the service.
- TEST_F(NetworkServiceCRLSetTest, CRLSetIsApplied) {
- CreateNetworkContext(CreateNetworkContextParams());
- uint32_t options =
- network::mojom::kURLLoadOptionSendSSLInfoWithResponse |
- network::mojom::kURLLoadOptionSendSSLInfoForCertificateError;
- // Make sure the test server loads fine with no CRLSet.
- LoadURL(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;
- network_context()->CloseAllConnections(run_loop.QuitClosure());
- run_loop.Run();
- }
- // Make sure the connection fails, due to the certificate being revoked.
- LoadURL(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(NetworkServiceCRLSetTest, CRLSetIsPassedToNewContexts) {
- // 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(CreateNetworkContextParams());
- uint32_t options =
- network::mojom::kURLLoadOptionSendSSLInfoWithResponse |
- network::mojom::kURLLoadOptionSendSSLInfoForCertificateError;
- // Make sure the connection fails, due to the certificate being revoked.
- LoadURL(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(NetworkServiceCRLSetTest, CRLSetIsUpdatedIfNewer) {
- // 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(CreateNetworkContextParams());
- uint32_t options =
- network::mojom::kURLLoadOptionSendSSLInfoWithResponse |
- network::mojom::kURLLoadOptionSendSSLInfoForCertificateError;
- // Make sure the connection loads, due to the root being permitted.
- LoadURL(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;
- network_context()->CloseAllConnections(run_loop.QuitClosure());
- run_loop.Run();
- }
- // Make sure the connection fails, due to the certificate being revoked.
- LoadURL(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(NetworkServiceCRLSetTest, CRLSetDoesNotDowngrade) {
- // 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(CreateNetworkContextParams());
- uint32_t options =
- network::mojom::kURLLoadOptionSendSSLInfoWithResponse |
- network::mojom::kURLLoadOptionSendSSLInfoForCertificateError;
- // Make sure the connection fails, due to the certificate being revoked.
- LoadURL(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;
- network_context()->CloseAllConnections(run_loop.QuitClosure());
- run_loop.Run();
- }
- // Make sure the connection still fails, due to the newer CRLSet still
- // applying.
- LoadURL(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_remote().reset();
- CreateNetworkContext(CreateNetworkContextParams());
- // The newer CRLSet that blocks the connection should still apply, even to
- // new NetworkContexts.
- LoadURL(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 // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
- // TODO(crbug.com/860189): AIA tests fail on iOS
- #if BUILDFLAG(IS_IOS)
- #define MAYBE(test_name) DISABLED_##test_name
- #else
- #define MAYBE(test_name) test_name
- #endif
- class NetworkServiceAIATest : public NetworkServiceIntegrationTest {
- public:
- NetworkServiceAIATest() : test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
- ~NetworkServiceAIATest() override = default;
- void SetUp() override {
- NetworkServiceIntegrationTest::SetUp();
- network::mojom::NetworkContextParamsPtr context_params =
- CreateNetworkContextParams();
- CreateNetworkContext(std::move(context_params));
- 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());
- }
- void PerformAIATest() {
- LoadURL(test_server_.GetURL("/echo"),
- network::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());
- }
- private:
- net::EmbeddedTestServer test_server_;
- };
- TEST_F(NetworkServiceAIATest, MAYBE(AIAFetching)) {
- PerformAIATest();
- }
- // Check that AIA fetching still succeeds even after the URLLoaderFactory
- // backing the CertNetFetcherURLLoader disconnects.
- // Only relevant if testing with the CertVerifierService, and the underlying
- // CertVerifier uses the CertNetFetcher.
- TEST_F(NetworkServiceAIATest,
- MAYBE(AIAFetchingWithURLLoaderFactoryDisconnect)) {
- if (!cert_net_fetcher_url_loader()) {
- // TODO(crbug.com/1015706): Switch to GTEST_SKIP().
- LOG(WARNING) << "Skipping AIA reconnection test because the underlying "
- "cert verifier does not use a CertNetFetcherURLLoader.";
- return;
- }
- PerformAIATest();
- // Disconnect the URLLoaderFactory used by the CertNetFetcherURLLoader.
- cert_net_fetcher_url_loader()->DisconnectURLLoaderFactoryForTesting();
- // Try running the test again to test reconnection of the
- // CertNetFetcherURLLoader.
- PerformAIATest();
- // Repeat one more time to be sure.
- cert_net_fetcher_url_loader()->DisconnectURLLoaderFactoryForTesting();
- PerformAIATest();
- }
- } // namespace cert_verifier
|