chromium/chrome/browser/net/cert_verifier_policy_browsertest.cc

// 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

// Testing the CACertificates policy
class CertVerifierServiceCACertificatesPolicyTest
    : public policy::PolicyTest,
      public testing::WithParamInterface<bool> {};

IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertificatesPolicyTest,
                       TestCACertificatesPolicy) {}

INSTANTIATE_TEST_SUITE_P();

// Test update of CACertificates policy after verifier is already
// created.
class CertVerifierServiceCACertificatesUpdatePolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(CertVerifierServiceCACertificatesUpdatePolicyTest,
                       TestCACertificatesUpdate) {}

// Testing the CADistrutedCertificates policy
class CertVerifierServiceCADistrustedCertificatesPolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(CertVerifierServiceCADistrustedCertificatesPolicyTest,
                       TestPolicy) {}

class CertVerifierServiceCATrustedDistrustedCertificatesPolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(
    CertVerifierServiceCATrustedDistrustedCertificatesPolicyTest,
    TestDistrustOverridesTrust) {}

// Test update of CADistrustedCertificates policy after verifier is already
// created.
class CertVerifierServiceCADistrustedCertificatesUpdatePolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(
    CertVerifierServiceCADistrustedCertificatesUpdatePolicyTest,
    TestCADistrustedCertificatesUpdate) {}

// Testing the CAHintCertificate policy
class CertVerifierServiceCAHintCertificatesPolicyTest
    : public policy::PolicyTest,
      public testing::WithParamInterface<bool> {};

IN_PROC_BROWSER_TEST_P(CertVerifierServiceCAHintCertificatesPolicyTest,
                       TestPolicy) {}

INSTANTIATE_TEST_SUITE_P();

// Test update of CAHintCertificates policy after verifier is already
// created.
class CertVerifierServiceCAHintCertificatesUpdatePolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(CertVerifierServiceCAHintCertificatesUpdatePolicyTest,
                       TestCAHintCertificatesUpdate) {}

#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 policy::PolicyTest {};

class CertVerifierServiceCAPlatformIntegrationPolicyTest
    : public CertVerifierServiceCAPlatformIntegrationPolicyBaseTest,
      public testing::WithParamInterface<bool> {};

IN_PROC_BROWSER_TEST_P(CertVerifierServiceCAPlatformIntegrationPolicyTest,
                       TestCAPlatformIntegrationPolicy) {}

INSTANTIATE_TEST_SUITE_P();

// Test update of CAPlatformIntegrationEnabled policy after verifier is created.
IN_PROC_BROWSER_TEST_F(CertVerifierServiceCAPlatformIntegrationPolicyBaseTest,
                       TestCAPlatformIntegrationUpdatePolicy) {}

#endif  // BUILDFLAG(IS_LINUX)

// Test the CACertificatesWithConstraints policy
class CertVerifierServiceCACertsWithConstraintsPolicyTest
    : public policy::PolicyTest,
      public testing::WithParamInterface<bool> {};

IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertsWithConstraintsPolicyTest,
                       TestCACertsWithConstraintsPolicy) {}

INSTANTIATE_TEST_SUITE_P();

class CertVerifierServiceCACertsWithCIDRConstraintsPolicyTest
    : public policy::PolicyTest,
      public testing::WithParamInterface<bool> {};

IN_PROC_BROWSER_TEST_P(CertVerifierServiceCACertsWithCIDRConstraintsPolicyTest,
                       TestCACertsWithCIDRConstraintsPolicy) {}

INSTANTIATE_TEST_SUITE_P();

class CertVerifierServiceCACertsWithInvalidCIDRConstraintsPolicyTest
    : public policy::PolicyTest {};

IN_PROC_BROWSER_TEST_F(
    CertVerifierServiceCACertsWithInvalidCIDRConstraintsPolicyTest,
    TestCACertsWithCIDRConstraintsPolicy) {}

class CertVerifierServiceCACertsWithConstraintsUpdatePolicyTest
    : public policy::PolicyTest {};

// Test update of CACertsWithConstraints policy after verifier is already
// created.
IN_PROC_BROWSER_TEST_F(
    CertVerifierServiceCACertsWithConstraintsUpdatePolicyTest,
    TestCACertsWithCIDRConstraintsPolicy) {}

#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 policy::PolicyTest,
      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))));
    UpdateProviderPolicy(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)