chromium/chrome/browser/policy/test/policy_certs_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 <memory>
#include <string>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/nss_service.h"
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/policy/networking/policy_cert_service.h"
#include "chrome/browser/policy/networking/policy_cert_service_factory.h"
#include "chrome/browser/policy/networking/user_network_configuration_updater_ash.h"
#include "chrome/browser/policy/networking/user_network_configuration_updater_factory.h"
#include "chrome/browser/policy/profile_policy_connector_builder.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/network/network_cert_loader.h"
#include "chromeos/ash/components/network/onc/onc_certificate_importer.h"
#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
#include "chromeos/components/onc/onc_test_utils.h"
#include "chromeos/test/chromeos_test_utils.h"
#include "components/onc/onc_constants.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/policy/policy_constants.h"
#include "components/policy/proto/cloud_policy.pb.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "crypto/scoped_test_nss_db.h"
#include "net/cert/cert_database.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_util_nss.h"
#include "net/test/cert_test_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/login/lock/screen_locker_tester.h"
#include "chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h"
#include "chrome/browser/ash/profiles/profile_helper.h"

using ::testing::NotNull;
#endif

namespace em = enterprise_management;

namespace policy {
namespace {

// Test data file storing an ONC blob with an Authority certificate.
constexpr char kRootCaCertOnc[] = "root-ca-cert.onc";
constexpr char kRootAndIntermediateCaCertsOnc[] =
    "root-and-intermediate-ca-certs.onc";
// A PEM-encoded certificate which was signed by the Authority specified in
// |kRootCaCertOnc|.
constexpr char kServerCert[] = "ok_cert.pem";
// A PEM-encoded certificate which was signed by the intermediate Authority
// specified in |kRootAndIntermediateCaCertsOnc|.
constexpr char kServerCertByIntermediate[] = "ok_cert_by_intermediate.pem";
// The PEM-encoded Authority certificate specified by |kRootCaCertOnc|.
constexpr char kRootCaCert[] = "root_ca_cert.pem";

// Allows waiting until the list of policy-pushed web-trusted certificates
// changes.
class WebTrustedCertsChangedObserver
    : public ash::PolicyCertificateProvider::Observer {
 public:
  WebTrustedCertsChangedObserver() = default;

  WebTrustedCertsChangedObserver(const WebTrustedCertsChangedObserver&) =
      delete;
  WebTrustedCertsChangedObserver& operator=(
      const WebTrustedCertsChangedObserver&) = delete;

  // ash::PolicyCertificateProvider::Observer
  void OnPolicyProvidedCertsChanged() override { run_loop_.Quit(); }

  void Wait() { run_loop_.Run(); }

 private:
  base::RunLoop run_loop_;
};

// Allows waiting until |NetworkCertLoader| updates its list of certificates.
class NetworkCertLoaderTestObserver : public ash::NetworkCertLoader::Observer {
 public:
  explicit NetworkCertLoaderTestObserver(
      ash::NetworkCertLoader* network_cert_loader)
      : network_cert_loader_(network_cert_loader) {
    network_cert_loader_->AddObserver(this);
  }

  NetworkCertLoaderTestObserver(const NetworkCertLoaderTestObserver&) = delete;
  NetworkCertLoaderTestObserver& operator=(
      const NetworkCertLoaderTestObserver&) = delete;

  ~NetworkCertLoaderTestObserver() override {
    network_cert_loader_->RemoveObserver(this);
  }

  // ash::NetworkCertLoader::Observer
  void OnCertificatesLoaded() override { run_loop_.Quit(); }

  void Wait() { run_loop_.Run(); }

 private:
  raw_ptr<ash::NetworkCertLoader> network_cert_loader_;
  base::RunLoop run_loop_;
};

// Retrieves the path to the directory containing certificates designated for
// testing of policy-provided certificates into *|out_test_certs_path|.
base::FilePath GetTestCertsPath() {
  base::FilePath test_data_dir;
  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));

  base::FilePath test_certs_path =
      test_data_dir.AppendASCII("policy").AppendASCII("test_certs");
  base::ScopedAllowBlockingForTesting allow_io;
  EXPECT_TRUE(base::DirectoryExists(test_certs_path));
  return test_certs_path;
}

// Reads the contents of the file with name |file_name| in the directory
// returned by GetTestCertsPath into *|out_file_contents|.
std::string GetTestCertsFileContents(const std::string& file_name) {
  base::FilePath test_certs_path = GetTestCertsPath();

  base::ScopedAllowBlockingForTesting allow_io;
  std::string file_contents;
  EXPECT_TRUE(base::ReadFileToString(test_certs_path.AppendASCII(file_name),
                                     &file_contents));
  return file_contents;
}

// Allows setting user policy to assign trust to the CA certificate specified by
// |kRootCaCert|.
class UserPolicyCertsHelper {
 public:
  UserPolicyCertsHelper() = default;
  ~UserPolicyCertsHelper() = default;

  UserPolicyCertsHelper(const UserPolicyCertsHelper& other) = delete;
  UserPolicyCertsHelper& operator=(const UserPolicyCertsHelper& other) = delete;

  // Reads in testing certificates.
  // Run in ASSERT_NO_FATAL_FAILURE.
  void Initialize() {
    base::ScopedAllowBlockingForTesting allow_io;
    base::FilePath test_certs_path = GetTestCertsPath();

    base::FilePath server_cert_path = test_certs_path.AppendASCII(kServerCert);
    server_cert_ = net::ImportCertFromFile(server_cert_path.DirName(),
                                           server_cert_path.BaseName().value());
    ASSERT_TRUE(server_cert_);

    base::FilePath root_cert_path = test_certs_path.AppendASCII(kRootCaCert);
    root_cert_ = net::ImportCertFromFile(root_cert_path.DirName(),
                                         root_cert_path.BaseName().value());
    ASSERT_TRUE(root_cert_);

    base::FilePath server_cert_by_intermediate_path =
        test_certs_path.AppendASCII(kServerCertByIntermediate);
    server_cert_by_intermediate_ = net::ImportCertFromFile(
        server_cert_path.DirName(),
        server_cert_by_intermediate_path.BaseName().value());
    ASSERT_TRUE(server_cert_by_intermediate_);
  }

  // Sets the ONC-policy to the blob defined by |kRootCaCertOnc| and waits until
  // the notification that policy-provided trust roots have changed is sent from
  // |profile|'s UserNetworkConfigurationUpdater.
  void SetRootCertONCUserPolicy(
      Profile* profile,
      MockConfigurationPolicyProvider* mock_policy_provider) {
    std::string onc_policy_data = GetTestCertsFileContents(kRootCaCertOnc);
    SetONCUserPolicy(profile, mock_policy_provider, onc_policy_data);
  }

  // Sets the ONC-policy to the blob defined by |kRootCaCertOnc| and waits until
  // the notification that policy-provided trust roots have changed is sent from
  // |profile|'s UserNetworkConfigurationUpdater.
  void SetRootAndIntermediateCertsONCUserPolicy(
      Profile* profile,
      MockConfigurationPolicyProvider* mock_policy_provider) {
    std::string onc_policy_data =
        GetTestCertsFileContents(kRootAndIntermediateCaCertsOnc);
    SetONCUserPolicy(profile, mock_policy_provider, onc_policy_data);
  }

  const scoped_refptr<net::X509Certificate>& server_cert() {
    return server_cert_;
  }

  const scoped_refptr<net::X509Certificate>& root_cert() { return root_cert_; }

  const scoped_refptr<net::X509Certificate>& server_cert_by_intermediate() {
    return server_cert_by_intermediate_;
  }

 private:
  void SetONCUserPolicy(Profile* profile,
                        MockConfigurationPolicyProvider* mock_policy_provider,
                        const std::string& onc_policy_data) {
    NetworkConfigurationUpdater* user_network_configuration_updater =
        UserNetworkConfigurationUpdaterFactory::GetForBrowserContext(profile);
    if (!user_network_configuration_updater) {
      // In Lacros-Chrome the ONC policy is only handled by the main profile.
      // Secondary profiles ignore it and UserNetworkConfigurationUpdater is not
      // created.s
      return;
    }

    WebTrustedCertsChangedObserver trust_roots_changed_observer;
    user_network_configuration_updater->AddPolicyProvidedCertsObserver(
        &trust_roots_changed_observer);

    policy::PolicyMap policy;
    policy.Set(key::kOpenNetworkConfiguration, policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
               base::Value(onc_policy_data), nullptr);
    mock_policy_provider->UpdateChromePolicy(policy);

    // Note that this relies on the implementation detail that the notification
    // is sent even if the trust roots effectively remain the same.
    trust_roots_changed_observer.Wait();
    user_network_configuration_updater->RemovePolicyProvidedCertsObserver(
        &trust_roots_changed_observer);

    // The above `trust_roots_changed_observer` only ensures that the
    // UpdateAdditionalCertificates message has been sent, but not that the
    // CertVerifierService has received it. Do a FlushForTesting on the loaded
    // CertVerifierServiceUpdaters for `profile`, to ensure any earlier
    // messages on the mojo pipes have been processed.
    profile->ForEachLoadedStoragePartition(
        &content::StoragePartition::FlushCertVerifierInterfaceForTesting);
  }

  // Server Certificate which is signed by authority specified in |kRootCaCert|.
  scoped_refptr<net::X509Certificate> server_cert_;
  // Authority Certificate specified in |kRootCaCert|.
  scoped_refptr<net::X509Certificate> root_cert_;
  // Server Certificate which is signed by an intermediate authority, which
  // itself is signed by the authority specified in |kRootCaCert|.
  // |kRootCaCertOnc| does not know this intermediate authority.
  // |kRootCaAndIntermediateCertsOnc| does know this intermediate authority, but
  // does not request web trust for it. Instead, trust should be delegate from
  // the root authrotiy.
  scoped_refptr<net::X509Certificate> server_cert_by_intermediate_;
};

// A class that allows testing multiple profiles in a browsertest, each having
// its own MockConfigurationPolicyProvider.
// TODO(crbug.com/40718963): Transform this into a general-purpose mixin.
class MultiProfilePolicyProviderHelper {
 public:
  MultiProfilePolicyProviderHelper() = default;
  ~MultiProfilePolicyProviderHelper() = default;

  MultiProfilePolicyProviderHelper(
      const MultiProfilePolicyProviderHelper& other) = delete;
  MultiProfilePolicyProviderHelper& operator=(
      const MultiProfilePolicyProviderHelper& other) = delete;

  void SetUpCommandLine(base::CommandLine* command_line) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
    command_line->AppendSwitch(
        ash::switches::kIgnoreUserProfileMappingForTests);
#endif
  }

  // The test should call this before the initial profile is created by chrome.
  void BeforeInitialProfileCreated() {
    // Set the overridden policy provider for the first Profile (|profile_1_|).
    // Note that the first ptofile will be created automatically by the
    // browser initialization.
    policy_for_profile_1_.SetDefaultReturns(
        /*is_initialization_complete_return=*/true,
        /*is_first_policy_load_complete_return=*/true);
    policy::PushProfilePolicyConnectorProviderForTesting(
        &policy_for_profile_1_);
  }

  // The test should call this after the initial profile is created by chrome.
  void AfterInitialProfileCreated() {
    // Mimics what InProcessBrowserTest does to get the first created Profile.
    const BrowserList* browser_list = BrowserList::GetInstance();
    ASSERT_FALSE(browser_list->empty());
    Browser* first_browser = browser_list->get(0);
    profile_1_ = first_browser->profile();
    ASSERT_TRUE(profile_1_);
  }

  // Creates a additional profile. The Profile can then be accessed by
  // profile_2() and its policy by policy_for_profile_2(). Should be wrapped in
  // ASSERT_NO_FATAL_FAILURE.
  void CreateSecondProfile() {
    ASSERT_FALSE(profile_2_);

    // Prepare policy provider for second profile.
    policy_for_profile_2_.SetDefaultReturns(
        /*is_initialization_complete_return=*/true,
        /*is_first_policy_load_complete_return=*/true);
    policy::PushProfilePolicyConnectorProviderForTesting(
        &policy_for_profile_2_);

    ProfileManager* profile_manager = g_browser_process->profile_manager();
    base::FilePath path_profile =
        profile_manager->GenerateNextProfileDirectoryPath();
    // Create an additional profile.
    profile_2_ =
        &profiles::testing::CreateProfileSync(profile_manager, path_profile);

    // Make sure second profile creation does what we think it does.
    ASSERT_TRUE(profile_1() != profile_2());
  }

  Profile* profile_1() { return profile_1_; }
  Profile* profile_2() { return profile_2_; }

  // Returns the MockConfigurationPolicyProvider for profile_1.
  MockConfigurationPolicyProvider* policy_for_profile_1() {
    return &policy_for_profile_1_;
  }

  // Returns the MockConfigurationPolicyProvider for profile_2.
  MockConfigurationPolicyProvider* policy_for_profile_2() {
    return &policy_for_profile_2_;
  }

 private:
  raw_ptr<Profile, DanglingUntriaged> profile_1_ = nullptr;
  raw_ptr<Profile, DanglingUntriaged> profile_2_ = nullptr;

  testing::NiceMock<MockConfigurationPolicyProvider> policy_for_profile_1_;
  testing::NiceMock<MockConfigurationPolicyProvider> policy_for_profile_2_;
};

// Verifies |certificate| with |storage_partition|'s CertVerifier and returns
// the result.
int VerifyTestServerCertInStoragePartition(
    content::StoragePartition* storage_partition,
    const scoped_refptr<net::X509Certificate>& certificate) {
  base::test::TestFuture<int> future;
  storage_partition->GetNetworkContext()->VerifyCertificateForTesting(
      certificate, "127.0.0.1", /*ocsp_response=*/std::string(),
      /*sct_list=*/std::string(), future.GetCallback());
  return future.Get();
}

// Verifies |certificate| with the CertVerifier for |profile|'s default
// StoragePartition and returns the result.
int VerifyTestServerCert(
    Profile* profile,
    const scoped_refptr<net::X509Certificate>& certificate) {
  return VerifyTestServerCertInStoragePartition(
      profile->GetDefaultStoragePartition(), certificate);
}

// Allows testing if user policy provided trust roots take effect, without
// having device policy.
// The parameter specifies whether the CertVerifierService is enabled.
class PolicyProvidedCertsRegularUserTest : public InProcessBrowserTest {
 protected:
  PolicyProvidedCertsRegularUserTest() = default;
  ~PolicyProvidedCertsRegularUserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    multi_profile_policy_helper_.SetUpCommandLine(command_line);
  }

  void SetUpInProcessBrowserTestFixture() override {
    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();

    ASSERT_NO_FATAL_FAILURE(
        multi_profile_policy_helper_.BeforeInitialProfileCreated());
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();

    ASSERT_NO_FATAL_FAILURE(
        multi_profile_policy_helper_.AfterInitialProfileCreated());

    ASSERT_NO_FATAL_FAILURE(user_policy_certs_helper_.Initialize());

    // Use the same testing slot as private and public slot for testing.
    test_nss_cert_db_ = std::make_unique<net::NSSCertDatabase>(
        crypto::ScopedPK11Slot(
            PK11_ReferenceSlot(test_nssdb_.slot())) /* public slot */,
        crypto::ScopedPK11Slot(
            PK11_ReferenceSlot(test_nssdb_.slot())) /* private slot */);
  }

  MultiProfilePolicyProviderHelper multi_profile_policy_helper_;

  UserPolicyCertsHelper user_policy_certs_helper_;

  // A NSSCertDatabase is needed for the tests that do something with
  // NetworkCertLoader.
  crypto::ScopedTestNSSDB test_nssdb_;
  std::unique_ptr<net::NSSCertDatabase> test_nss_cert_db_;
};

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest, NoTrustAnchor) {
  ASSERT_NO_FATAL_FAILURE(multi_profile_policy_helper_.CreateSecondProfile());

  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_1(),
                                 user_policy_certs_helper_.server_cert()));
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_2(),
                                 user_policy_certs_helper_.server_cert()));

  if (PolicyCertServiceFactory::GetForProfile(
          multi_profile_policy_helper_.profile_1())) {
    EXPECT_FALSE(PolicyCertServiceFactory::GetForProfile(
                     multi_profile_policy_helper_.profile_1())
                     ->UsedPolicyCertificates());
  }
  if (PolicyCertServiceFactory::GetForProfile(
          multi_profile_policy_helper_.profile_2())) {
    EXPECT_FALSE(PolicyCertServiceFactory::GetForProfile(
                     multi_profile_policy_helper_.profile_2())
                     ->UsedPolicyCertificates());
  }
}

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest, TrustAnchorApplied) {
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());
  EXPECT_EQ(net::OK,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_1(),
                                 user_policy_certs_helper_.server_cert()));

  EXPECT_TRUE(PolicyCertServiceFactory::GetForProfile(
                  multi_profile_policy_helper_.profile_1())
                  ->UsedPolicyCertificates());
}

// Test that policy provided trust anchors are available in Incognito mode.
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       TrustAnchorAppliedInIncognito) {
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());

  Profile* otr_profile =
      multi_profile_policy_helper_.profile_1()->GetPrimaryOTRProfile(
          /*create_if_needed=*/true);

  EXPECT_EQ(net::OK, VerifyTestServerCert(
                         otr_profile, user_policy_certs_helper_.server_cert()));
}

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       PrimaryProfileTrustAnchorDoesNotLeak) {
  ASSERT_NO_FATAL_FAILURE(multi_profile_policy_helper_.CreateSecondProfile());

  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());
  EXPECT_EQ(net::OK,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_1(),
                                 user_policy_certs_helper_.server_cert()));
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_2(),
                                 user_policy_certs_helper_.server_cert()));

  EXPECT_TRUE(PolicyCertServiceFactory::GetForProfile(
                  multi_profile_policy_helper_.profile_1())
                  ->UsedPolicyCertificates());
  if (PolicyCertServiceFactory::GetForProfile(
          multi_profile_policy_helper_.profile_2())) {
    EXPECT_FALSE(PolicyCertServiceFactory::GetForProfile(
                     multi_profile_policy_helper_.profile_2())
                     ->UsedPolicyCertificates());
  }
}

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       SecondaryProfileTrustAnchorDoesNotLeakOrIgnored) {
  ASSERT_NO_FATAL_FAILURE(multi_profile_policy_helper_.CreateSecondProfile());

  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_2(),
      multi_profile_policy_helper_.policy_for_profile_2());
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_1(),
                                 user_policy_certs_helper_.server_cert()));
#if BUILDFLAG(IS_CHROMEOS_ASH)
  // TODO(crbug.com/40718963): That the cert from a secondary user's
  // policy is used at all is currently an artifact of the test, which reuses
  // the primary user_manager::User for the secondary Profile.
  // Fix that and then expect ERR_CERT_AUTHORITY_INVALID here too, and rename
  // the test to SecondaryProfileTrustAnchorIgnored.
  // Or, allow secondary Profile ONC policy to set trust anchors for the
  // secondary Profile.
  EXPECT_EQ(net::OK,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_2(),
                                 user_policy_certs_helper_.server_cert()));
#else  // Implies #if BUILDFLAG(IS_CHROMEOS_LACROS), but this is a generally
       // correct behavior according to the comment above.
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_2(),
                                 user_policy_certs_helper_.server_cert()));
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
}

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       UntrustedIntermediateAuthorityApplied) {
  // Sanity check: Apply ONC policy which does not mention the intermediate
  // authority.
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(
                multi_profile_policy_helper_.profile_1(),
                user_policy_certs_helper_.server_cert_by_intermediate()));

  // Now apply ONC policy which mentions the intermediate authority (but does
  // not assign trust to it).
  user_policy_certs_helper_.SetRootAndIntermediateCertsONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());
  EXPECT_EQ(net::OK,
            VerifyTestServerCert(
                multi_profile_policy_helper_.profile_1(),
                user_policy_certs_helper_.server_cert_by_intermediate()));
}

// NetworkCertLoader is only relevant for Ash-Chrome.
#if BUILDFLAG(IS_CHROMEOS_ASH)

bool IsCertInCertificateList(
    const net::X509Certificate* cert,
    const ash::NetworkCertLoader::NetworkCertList& network_cert_list) {
  for (const auto& network_cert : network_cert_list) {
    if (net::x509_util::IsSameCertificate(network_cert.cert(), cert))
      return true;
  }
  return false;
}

IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       AuthorityAvailableThroughNetworkCertLoader) {
  // Set |NetworkCertLoader| to use a test NSS database - otherwise, it is not
  // properly initialized because |UserSessionManager| only sets the primary
  // user's NSS Database in |NetworkCertLoader| if running on ChromeOS according
  // to |base::SysInfo|.
  ASSERT_TRUE(ash::NetworkCertLoader::IsInitialized());
  ash::NetworkCertLoader::Get()->SetUserNSSDB(test_nss_cert_db_.get());

  EXPECT_FALSE(IsCertInCertificateList(
      user_policy_certs_helper_.root_cert().get(),
      ash::NetworkCertLoader::Get()->authority_certs()));
  NetworkCertLoaderTestObserver network_cert_loader_observer(
      ash::NetworkCertLoader::Get());
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_1(),
      multi_profile_policy_helper_.policy_for_profile_1());
  network_cert_loader_observer.Wait();

  // Check that |NetworkCertLoader| is aware of the authority certificate.
  // (Web Trust does not matter for the NetworkCertLoader, but we currently only
  // set a policy with a certificate requesting Web Trust here).
  EXPECT_TRUE(IsCertInCertificateList(
      user_policy_certs_helper_.root_cert().get(),
      ash::NetworkCertLoader::Get()->authority_certs()));
}

// Test that the lock screen profile uses the policy provided custom trusted
// anchors of the primary profile .
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       LockScreenPrimaryProfileCerts) {
  ash::ScreenLockerTester locker;
  locker.Lock();
  // Showing the reauth dialog will create the lock screen profile.
  ash::LockScreenReauthDialogTestHelper::ShowDialogAndWait();
  ASSERT_THAT(ash::ProfileHelper::GetLockScreenProfile(), NotNull());

  // Set policy provided trusted anchors on the primary profile.
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      browser()->profile(),
      multi_profile_policy_helper_.policy_for_profile_1());

  EXPECT_EQ(net::OK,
            VerifyTestServerCert(browser()->profile(),
                                 user_policy_certs_helper_.server_cert()));
  // Verify that the lock screen can access the policy provided certs.
  EXPECT_EQ(net::OK,
            VerifyTestServerCert(ash::ProfileHelper::GetLockScreenProfile(),
                                 user_policy_certs_helper_.server_cert()));

  EXPECT_TRUE(PolicyCertServiceFactory::GetForProfile(browser()->profile())
                  ->UsedPolicyCertificates());
  EXPECT_TRUE(PolicyCertServiceFactory::GetForProfile(
                  ash::ProfileHelper::GetLockScreenProfile())
                  ->UsedPolicyCertificates());
}

// Test that the lock screen profile doesn't use the policy provided custom
// trusted anchors of a secondary profile.
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
                       LockScreenSecondaryProfileCerts) {
  ash::ScreenLockerTester locker;
  locker.Lock();
  // Showing the reauth dialog will create the lock screen profile.
  ash::LockScreenReauthDialogTestHelper::ShowDialogAndWait();
  ASSERT_THAT(ash::ProfileHelper::GetLockScreenProfile(), NotNull());

  ASSERT_NO_FATAL_FAILURE(multi_profile_policy_helper_.CreateSecondProfile());
  user_policy_certs_helper_.SetRootCertONCUserPolicy(
      multi_profile_policy_helper_.profile_2(),
      multi_profile_policy_helper_.policy_for_profile_2());

  EXPECT_EQ(net::OK,
            VerifyTestServerCert(multi_profile_policy_helper_.profile_2(),
                                 user_policy_certs_helper_.server_cert()));
  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
            VerifyTestServerCert(ash::ProfileHelper::GetLockScreenProfile(),
                                 user_policy_certs_helper_.server_cert()));
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace
}  // namespace policy