blob: e8a3a651e07bcdac67d0bcbc70e0a4af73ea3448 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include "base/base64.h"
#include "base/json/json_writer.h"
#include "base/strings/strcat.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/common/buildflags.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/features.h"
#include "net/cert/x509_util.h"
#include "net/net_buildflags.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_LINUX)
#include "crypto/scoped_test_nss_db.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/scoped_nss_types.h"
#include "net/cert/x509_util_nss.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "chrome/test/base/android/android_browser_test.h"
#else
#include "chrome/test/base/in_process_browser_test.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "components/onc/onc_constants.h" // nogncheck
#endif
#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/test/test_future.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/net/server_certificate_database_service_factory.h" // nogncheck
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/server_certificate_database/server_certificate_database.h" // nogncheck
#include "components/server_certificate_database/server_certificate_database.pb.h" // nogncheck
#include "components/server_certificate_database/server_certificate_database_service.h" // nogncheck
#include "crypto/sha2.h"
#endif // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
namespace {
class CertVerifierServicePolicyTest : public policy::PolicyTest {
public:
// In some cases, we may need to wait until the certificate policy updates
// propagate.
void UpdateProviderPolicyAndWaitForUpdate(const policy::PolicyMap& policies) {
// If features::kEnableCertManagementUIV2Write is enabled, the cert verifier
// service update is asynchronous and the test needs to wait for the update
// to complete.
// Otherwise, cert changes may not make it to the verifier in time to clear
// the cert verification cache.
// This is safe to do in other cases, as long as the test is expecting the
// cert verifier to get updated certificates.
#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
base::test::TestFuture<void> cert_verifier_service_update_waiter;
browser()
->profile()
->GetDefaultStoragePartition()
->GetCertVerifierServiceUpdater()
->WaitUntilNextUpdateForTesting(
cert_verifier_service_update_waiter.GetCallback());
#endif // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
// Update policy with root
UpdateProviderPolicy(policies);
#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
ASSERT_TRUE(cert_verifier_service_update_waiter.Wait());
#endif // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
}
};
} // namespace
// Testing the CACertificates policy
class CertVerifierServiceCACertificatesPolicyTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
if (add_cert_to_policy()) {
scoped_refptr<net::X509Certificate> root_cert = net::ImportCertFromFile(
net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(b64_cert);
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicy(policies);
}
}
bool add_cert_to_policy() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertificatesPolicyTest,
TestCACertificatesPolicy) {
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_AUTO);
https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server.Start());
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
EXPECT_NE(add_cert_to_policy(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServiceCACertificatesPolicyTest,
::testing::Bool());
// Test update of CACertificates policy after verifier is already
// created.
class CertVerifierServiceCACertificatesUpdatePolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
};
IN_PROC_BROWSER_TEST_F(CertVerifierServiceCACertificatesUpdatePolicyTest,
TestCACertificatesUpdate) {
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root. Clear test roots so that cert validation only happens with what's in
// the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
{
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// No policy set, root cert isn't trusted.
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
{
// Update policy with root
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(std::move(b64_cert));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Updated with policy, root should be trusted.
EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
}
// Testing the CADistrutedCertificates policy
class CertVerifierServiceCADistrustedCertificatesPolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(b64_cert);
policy::PolicyMap policies;
// Distrust the test server certificate
SetPolicy(&policies, policy::key::kCADistrustedCertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicy(policies);
}
};
IN_PROC_BROWSER_TEST_F(CertVerifierServiceCADistrustedCertificatesPolicyTest,
TestPolicy) {
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_AUTO);
https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server.Start());
// We don't clear the test roots but the cert should still be distrusted based
// on the enterprise policy.
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
class CertVerifierServiceCATrustedDistrustedCertificatesPolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
policy::PolicyMap policies;
// Distrust the test server certificate
{
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(b64_cert);
SetPolicy(&policies, policy::key::kCADistrustedCertificates,
std::make_optional(std::move(certs_value)));
}
// Trust the test server certificate
{
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(b64_cert);
SetPolicy(&policies, policy::key::kCACertificates,
std::make_optional(std::move(certs_value)));
}
UpdateProviderPolicy(policies);
}
};
IN_PROC_BROWSER_TEST_F(
CertVerifierServiceCATrustedDistrustedCertificatesPolicyTest,
TestDistrustOverridesTrust) {
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_AUTO);
https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server.Start());
// We don't clear the test roots but the cert should still be distrusted based
// on the enterprise policy.
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
// Test update of CADistrustedCertificates policy after verifier is already
// created.
class CertVerifierServiceCADistrustedCertificatesUpdatePolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
};
IN_PROC_BROWSER_TEST_F(
CertVerifierServiceCADistrustedCertificatesUpdatePolicyTest,
TestCADistrustedCertificatesUpdate) {
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
{
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Cert is trusted by default.
EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
{
// Update policy to distrust the test server certificate
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
policy::PolicyMap policies;
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(std::move(b64_cert));
SetPolicy(&policies, policy::key::kCADistrustedCertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Updated with policy, root should no longer be trusted.
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
}
// Testing the CAHintCertificate policy
class CertVerifierServiceCAHintCertificatesPolicyTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
// Don't serve the intermediate either via AIA or as part of the handshake.
net::EmbeddedTestServer::ServerCertificateConfig cert_config;
cert_config.intermediate =
net::EmbeddedTestServer::IntermediateType::kMissing;
https_test_server_.SetSSLConfig(cert_config);
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
if (add_cert_to_policy()) {
// Add the intermediate as a hint.
scoped_refptr<net::X509Certificate> intermediate_cert =
https_test_server_.GetGeneratedIntermediate();
ASSERT_TRUE(intermediate_cert);
std::string b64_cert =
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
intermediate_cert->cert_buffer()));
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(b64_cert);
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCAHintCertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicy(policies);
}
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
bool add_cert_to_policy() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceCAHintCertificatesPolicyTest,
TestPolicy) {
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
EXPECT_NE(add_cert_to_policy(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServiceCAHintCertificatesPolicyTest,
::testing::Bool());
// Test update of CAHintCertificates policy after verifier is already
// created.
class CertVerifierServiceCAHintCertificatesUpdatePolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
net::EmbeddedTestServer::ServerCertificateConfig cert_config;
cert_config.intermediate =
net::EmbeddedTestServer::IntermediateType::kMissing;
https_test_server_.SetSSLConfig(cert_config);
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
};
IN_PROC_BROWSER_TEST_F(CertVerifierServiceCAHintCertificatesUpdatePolicyTest,
TestCAHintCertificatesUpdate) {
{
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Intermediate isn't found, so can't build the chain
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
{
// Update policy to add the intermediate as a hint by policy
scoped_refptr<net::X509Certificate> intermediate_cert =
https_test_server_.GetGeneratedIntermediate();
ASSERT_TRUE(intermediate_cert);
std::string b64_cert =
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
intermediate_cert->cert_buffer()));
base::Value certs_value(base::Value::Type::LIST);
certs_value.GetList().Append(std::move(b64_cert));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCAHintCertificates,
std::make_optional(std::move(certs_value)));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Updated with policy, intermediate is used so chain can be built.
EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
}
#if BUILDFLAG(IS_LINUX)
// Test the CAPlatformIntegrationEnabled policy.
//
// Ideally we'd have this set up for every platform where this policy is
// supported, but on most platforms its really hard to modify the OS root
// store in an integration test without possibly messing up other tests.
// Except on Linux.
class CertVerifierServiceCAPlatformIntegrationPolicyBaseTest
: public CertVerifierServicePolicyTest {
public:
void SetUpOnMainThread() override {
policy::PolicyTest::SetUpOnMainThread();
// Set up test NSS DB
nss_db_ = std::make_unique<crypto::ScopedTestNSSDB>();
cert_db_ = std::make_unique<net::NSSCertDatabase>(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())),
crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())));
ASSERT_TRUE(nss_db_->is_open());
// Add root cert to test NSS DB.
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
net::ScopedCERTCertificateList nss_certs;
net::ScopedCERTCertificate nss_cert =
net::x509_util::CreateCERTCertificateFromX509Certificate(
root_cert.get());
ASSERT_TRUE(nss_cert);
nss_certs.push_back(std::move(nss_cert));
net::NSSCertDatabase::ImportCertFailureList failure_list;
cert_db_->ImportCACerts(nss_certs,
/*trust_bits=*/net::NSSCertDatabase::TRUSTED_SSL,
&failure_list);
ASSERT_TRUE(failure_list.empty());
}
private:
std::unique_ptr<crypto::ScopedTestNSSDB> nss_db_;
std::unique_ptr<net::NSSCertDatabase> cert_db_;
};
class CertVerifierServiceCAPlatformIntegrationPolicyTest
: public CertVerifierServiceCAPlatformIntegrationPolicyBaseTest,
public testing::WithParamInterface<bool> {
public:
bool platform_root_store_enabled() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceCAPlatformIntegrationPolicyTest,
TestCAPlatformIntegrationPolicy) {
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCAPlatformIntegrationEnabled,
std::optional<base::Value>(platform_root_store_enabled()));
UpdateProviderPolicyAndWaitForUpdate(policies);
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_AUTO);
https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server.Start());
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
EXPECT_NE(platform_root_store_enabled(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServiceCAPlatformIntegrationPolicyTest,
::testing::Bool());
// Test update of CAPlatformIntegrationEnabled policy after verifier is created.
IN_PROC_BROWSER_TEST_F(CertVerifierServiceCAPlatformIntegrationPolicyBaseTest,
TestCAPlatformIntegrationUpdatePolicy) {
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server.Start());
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
// Platform integration is on by default, request should succeed.
EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
// Turn platform integration off
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCAPlatformIntegrationEnabled,
std::optional<base::Value>(false));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server.GetURL("/simple.html"), this));
// Platform integration is false, request should fail to verify cert.
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
#endif // BUILDFLAG(IS_LINUX)
// Test the CACertificatesWithConstraints policy
class CertVerifierServiceCACertsWithConstraintsPolicyTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
// Use a certificate valid for the dns name localhost rather than an IP.
https_test_server_.SetSSLConfig(
net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
// Set policy. DNS constraint only matches when
// set_proper_dns_name_constraint() = true.
std::string dns_name_constraint =
set_proper_dns_name_constraint() ? "localhost" : "cruddyhost";
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value::List certs_with_constraints_value = base::Value::List().Append(
base::Value::Dict()
.Set("certificate", b64_cert)
.Set("constraints",
base::Value::Dict().Set(
"permitted_dns_names",
base::Value::List().Append(dns_name_constraint))));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificatesWithConstraints,
std::make_optional(
base::Value(std::move(certs_with_constraints_value))));
UpdateProviderPolicy(policies);
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
bool set_proper_dns_name_constraint() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertsWithConstraintsPolicyTest,
TestCACertsWithConstraintsPolicy) {
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
EXPECT_NE(set_proper_dns_name_constraint(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServiceCACertsWithConstraintsPolicyTest,
::testing::Bool());
class CertVerifierServiceCACertsWithCIDRConstraintsPolicyTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
// Set policy. CIDR constraint only matches when
// set_proper_cidr_name_constraint() = true.
std::string cidr_name_constraint =
set_proper_cidr_name_constraint() ? "127.127.0.1/8" : "127.127.0.1/16";
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value::List certs_with_constraints_value = base::Value::List().Append(
base::Value::Dict()
.Set("certificate", b64_cert)
.Set("constraints",
base::Value::Dict().Set(
"permitted_cidrs",
base::Value::List().Append(cidr_name_constraint))));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificatesWithConstraints,
std::make_optional(
base::Value(std::move(certs_with_constraints_value))));
UpdateProviderPolicy(policies);
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
bool set_proper_cidr_name_constraint() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertsWithCIDRConstraintsPolicyTest,
TestCACertsWithCIDRConstraintsPolicy) {
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
EXPECT_NE(set_proper_cidr_name_constraint(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(
All,
CertVerifierServiceCACertsWithCIDRConstraintsPolicyTest,
::testing::Bool());
class CertVerifierServiceCACertsWithInvalidCIDRConstraintsPolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
https_test_server_.SetSSLConfig(
net::test_server::EmbeddedTestServer::CERT_AUTO);
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
// Set invalid policy.
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value::List certs_with_constraints_value = base::Value::List().Append(
base::Value::Dict()
.Set("certificate", b64_cert)
.Set("constraints",
base::Value::Dict().Set(
"permitted_cidrs",
base::Value::List().Append("invalidcidr"))));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificatesWithConstraints,
std::make_optional(
base::Value(std::move(certs_with_constraints_value))));
UpdateProviderPolicy(policies);
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
};
IN_PROC_BROWSER_TEST_F(
CertVerifierServiceCACertsWithInvalidCIDRConstraintsPolicyTest,
TestCACertsWithCIDRConstraintsPolicy) {
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// invalid CIDR constraint means the root cert isn't trusted.
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
class CertVerifierServiceCACertsWithConstraintsUpdatePolicyTest
: public CertVerifierServicePolicyTest {
public:
void SetUpInProcessBrowserTestFixture() override {
policy::PolicyTest::SetUpInProcessBrowserTestFixture();
https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_test_server_.Start());
}
net::EmbeddedTestServer https_test_server_{
net::EmbeddedTestServer::TYPE_HTTPS};
};
// Test update of CACertsWithConstraints policy after verifier is already
// created.
IN_PROC_BROWSER_TEST_F(
CertVerifierServiceCACertsWithConstraintsUpdatePolicyTest,
TestCACertsWithCIDRConstraintsPolicy) {
// `net::EmbeddedTestServer` uses `net::TestRootCerts` to install a trusted
// root.
// Clear test roots so that cert validation only happens with
// what's in the relevant root store + policies.
net::TestRootCerts::GetInstance()->Clear();
scoped_refptr<net::X509Certificate> root_cert =
net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath());
ASSERT_TRUE(root_cert);
{
// Set invalid policy.
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value::List certs_with_constraints_value = base::Value::List().Append(
base::Value::Dict()
.Set("certificate", b64_cert)
.Set("constraints",
base::Value::Dict().Set(
"permitted_cidrs",
base::Value::List().Append("invalidcidr"))));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificatesWithConstraints,
std::make_optional(
base::Value(std::move(certs_with_constraints_value))));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// invalid CIDR constraint means the root cert isn't trusted.
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
{
// Update with valid policy
std::string b64_cert = base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()));
base::Value::List certs_with_constraints_value = base::Value::List().Append(
base::Value::Dict()
.Set("certificate", b64_cert)
.Set("constraints",
base::Value::Dict().Set(
"permitted_cidrs",
base::Value::List().Append("127.127.0.1/8"))));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificatesWithConstraints,
std::make_optional(
base::Value(std::move(certs_with_constraints_value))));
UpdateProviderPolicyAndWaitForUpdate(policies);
ASSERT_TRUE(NavigateToUrl(https_test_server_.GetURL("/simple.html"), this));
// Updated with a valid CIDR constraint, cert should be trusted.
EXPECT_FALSE(chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
}
#if BUILDFLAG(IS_CHROMEOS)
// Tests that when certificates are simultaneously added to both the ONC policy
// and the new CACertificates/CAHintCertificates policies, that they are both
// honored.
class CertVerifierServiceNewAndOncCertificatePoliciesTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
bool add_cert_to_policy() const { return GetParam(); }
};
IN_PROC_BROWSER_TEST_P(CertVerifierServiceNewAndOncCertificatePoliciesTest,
TestBothPoliciesSetSimultaneously) {
net::EmbeddedTestServer test_server_for_onc_policy{
net::EmbeddedTestServer::TYPE_HTTPS};
net::EmbeddedTestServer test_server_for_new_policy{
net::EmbeddedTestServer::TYPE_HTTPS};
// Configure both test servers to serve chains with unique roots and with
// an intermediate that is not sent by the testserver nor available via
// AIA. Neither server should be trusted unless its intermediate is
// supplied as a hint via policy and its root is trusted via policy.
net::EmbeddedTestServer::ServerCertificateConfig test_cert_config;
test_cert_config.intermediate =
net::EmbeddedTestServer::IntermediateType::kMissing;
test_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
test_server_for_onc_policy.SetSSLConfig(test_cert_config);
test_server_for_new_policy.SetSSLConfig(test_cert_config);
test_server_for_onc_policy.ServeFilesFromSourceDirectory("chrome/test/data");
test_server_for_new_policy.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(test_server_for_onc_policy.Start());
ASSERT_TRUE(test_server_for_new_policy.Start());
if (add_cert_to_policy()) {
std::string onc_root_pem;
std::string onc_hint_pem;
ASSERT_TRUE(net::X509Certificate::GetPEMEncoded(
test_server_for_onc_policy.GetRoot()->cert_buffer(), &onc_root_pem));
ASSERT_TRUE(net::X509Certificate::GetPEMEncoded(
test_server_for_onc_policy.GetGeneratedIntermediate()->cert_buffer(),
&onc_hint_pem));
auto onc_ca_cert =
base::Value::Dict()
.Set(onc::certificate::kGUID, base::Value("guid_root"))
.Set(onc::certificate::kType, onc::certificate::kAuthority)
.Set(onc::certificate::kX509, onc_root_pem)
.Set(onc::certificate::kTrustBits,
base::Value::List().Append(onc::certificate::kWeb));
auto onc_hint_cert =
base::Value::Dict()
.Set(onc::certificate::kGUID, base::Value("guid_hint"))
.Set(onc::certificate::kType, onc::certificate::kAuthority)
.Set(onc::certificate::kX509, onc_hint_pem);
auto onc_certificates = base::Value::List()
.Append(std::move(onc_ca_cert))
.Append(std::move(onc_hint_cert));
auto onc_policy = base::Value::Dict()
.Set(onc::toplevel_config::kCertificates,
std::move(onc_certificates))
.Set(onc::toplevel_config::kType,
onc::toplevel_config::kUnencryptedConfiguration);
std::string onc_policy_json;
ASSERT_TRUE(base::JSONWriter::Write(onc_policy, &onc_policy_json));
auto new_ca_certs = base::Value::List().Append(
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
test_server_for_new_policy.GetRoot()->cert_buffer())));
auto new_hint_certs = base::Value::List().Append(
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
test_server_for_new_policy.GetGeneratedIntermediate()
->cert_buffer())));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kOpenNetworkConfiguration,
std::make_optional(base::Value(std::move(onc_policy_json))));
SetPolicy(&policies, policy::key::kCACertificates,
std::make_optional(base::Value(std::move(new_ca_certs))));
SetPolicy(&policies, policy::key::kCAHintCertificates,
std::make_optional(base::Value(std::move(new_hint_certs))));
UpdateProviderPolicyAndWaitForUpdate(policies);
}
ASSERT_TRUE(
NavigateToUrl(test_server_for_onc_policy.GetURL("/simple.html"), this));
EXPECT_NE(add_cert_to_policy(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
ASSERT_TRUE(
NavigateToUrl(test_server_for_new_policy.GetURL("/simple.html"), this));
EXPECT_NE(add_cert_to_policy(),
chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServiceNewAndOncCertificatePoliciesTest,
::testing::Bool());
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)
// Tests that when certificates are simultaneously added to both the user
// added certs database and the new CACertificates/CAHintCertificates policies,
// that they are both honored.
class CertVerifierServicePolicyAndUserRootsTest
: public CertVerifierServicePolicyTest,
public testing::WithParamInterface<bool> {
public:
CertVerifierServicePolicyAndUserRootsTest() {
feature_list_.InitWithFeatures({features::kEnableCertManagementUIV2,
features::kEnableCertManagementUIV2Write},
{});
}
bool add_certs() const { return GetParam(); }
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_P(CertVerifierServicePolicyAndUserRootsTest,
UserRootsAndPolicyCombined) {
net::EmbeddedTestServer test_server_for_user_added{
net::EmbeddedTestServer::TYPE_HTTPS};
net::EmbeddedTestServer test_server_for_policy{
net::EmbeddedTestServer::TYPE_HTTPS};
// Configure both test servers to serve chains with unique roots and with
// an intermediate that is not sent by the testserver nor available via
// AIA. Neither server should be trusted unless its intermediate is
// supplied as a hint via policy and its root is trusted via policy.
net::EmbeddedTestServer::ServerCertificateConfig test_cert_config;
test_cert_config.intermediate =
net::EmbeddedTestServer::IntermediateType::kMissing;
test_cert_config.root = net::EmbeddedTestServer::RootType::kUniqueRoot;
test_server_for_user_added.SetSSLConfig(test_cert_config);
test_server_for_policy.SetSSLConfig(test_cert_config);
test_server_for_user_added.ServeFilesFromSourceDirectory("chrome/test/data");
test_server_for_policy.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(test_server_for_user_added.Start());
ASSERT_TRUE(test_server_for_policy.Start());
if (add_certs()) {
net::ServerCertificateDatabaseService* server_certificate_database_service =
net::ServerCertificateDatabaseServiceFactory::GetForBrowserContext(
browser()->profile());
{
scoped_refptr<net::X509Certificate> root_cert =
test_server_for_user_added.GetRoot();
net::ServerCertificateDatabase::CertInformation user_root_info(
root_cert->cert_span());
user_root_info.cert_metadata.mutable_trust()->set_trust_type(
chrome_browser_server_certificate_database::CertificateTrust::
CERTIFICATE_TRUST_TYPE_TRUSTED);
base::test::TestFuture<bool> future;
std::vector<net::ServerCertificateDatabase::CertInformation> cert_infos;
cert_infos.push_back(std::move(user_root_info));
server_certificate_database_service->AddOrUpdateUserCertificates(
std::move(cert_infos), future.GetCallback());
ASSERT_TRUE(future.Get());
}
{
scoped_refptr<net::X509Certificate> hint_cert =
test_server_for_user_added.GetGeneratedIntermediate();
net::ServerCertificateDatabase::CertInformation user_hint_info(
hint_cert->cert_span());
user_hint_info.cert_metadata.mutable_trust()->set_trust_type(
chrome_browser_server_certificate_database::CertificateTrust::
CERTIFICATE_TRUST_TYPE_UNSPECIFIED);
base::test::TestFuture<bool> future;
std::vector<net::ServerCertificateDatabase::CertInformation> cert_infos;
cert_infos.push_back(std::move(user_hint_info));
server_certificate_database_service->AddOrUpdateUserCertificates(
std::move(cert_infos), future.GetCallback());
ASSERT_TRUE(future.Get());
}
auto policy_ca_certs = base::Value::List().Append(
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
test_server_for_policy.GetRoot()->cert_buffer())));
auto policy_hint_certs = base::Value::List().Append(
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
test_server_for_policy.GetGeneratedIntermediate()->cert_buffer())));
policy::PolicyMap policies;
SetPolicy(&policies, policy::key::kCACertificates,
std::make_optional(base::Value(std::move(policy_ca_certs))));
SetPolicy(&policies, policy::key::kCAHintCertificates,
std::make_optional(base::Value(std::move(policy_hint_certs))));
// Policy updates will also trigger an update to the Cert Verifier, pulling
// in the certs from ServerCertificateDatabase.
UpdateProviderPolicyAndWaitForUpdate(policies);
}
ASSERT_TRUE(
NavigateToUrl(test_server_for_policy.GetURL("/simple.html"), this));
EXPECT_NE(add_certs(), chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
ASSERT_TRUE(
NavigateToUrl(test_server_for_user_added.GetURL("/simple.html"), this));
EXPECT_NE(add_certs(), chrome_browser_interstitials::IsShowingInterstitial(
chrome_test_utils::GetActiveWebContents(this)));
}
INSTANTIATE_TEST_SUITE_P(All,
CertVerifierServicePolicyAndUserRootsTest,
::testing::Bool());
#endif // BUILDFLAG(CHROME_ROOT_STORE_CERT_MANAGEMENT_UI)