blob: 762169edf14ff31f7afb47b150b34e61793078e9 [file] [log] [blame]
// Copyright (c) 2017 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/bind.h"
#include "base/command_line.h"
#include "base/json/json_writer.h"
#include "base/message_loop/message_loop_current.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/test/session_manager_state_waiter.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/policy/login_policy_test_base.h"
#include "chrome/browser/chromeos/policy/signin_profile_extensions_policy_test_base.h"
#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
#include "chrome/browser/chromeos/policy/user_network_configuration_updater_factory.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/net/nss_context.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/network/onc/onc_certificate_importer.h"
#include "chromeos/network/onc/onc_certificate_importer_impl.h"
#include "chromeos/network/onc/onc_test_utils.h"
#include "chromeos/network/policy_certificate_provider.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/session_manager/core/session_manager.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.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 "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "net/base/features.h"
#include "net/base/test_completion_callback.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 "net/test/test_data_directory.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
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 kClientCertOnc[] = "certificate-client.onc";
constexpr char kRootAndIntermediateCaCertsOnc[] =
"root-and-intermediate-ca-certs.onc";
constexpr char kClientCertSubjectCommonName[] = "lmao";
// 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";
constexpr char kDeviceLocalAccountId[] = "dla1@example.com";
constexpr char kSigninScreenExtension1[] = "ngjobkbdodapjbbncmagbccommkggmnj";
constexpr char kSigninScreenExtension1UpdateManifestPath[] =
"/extensions/signin_screen_manual_test_extension/update_manifest.xml";
const char kSigninScreenExtension2[] = "oclffehlkdgibkainkilopaalpdobkan";
const char kSigninScreenExtension2UpdateManifestPath[] =
"/extensions/api_test/login_screen_apis/update_manifest.xml";
// Allows waiting until the list of policy-pushed web-trusted certificates
// changes.
class WebTrustedCertsChangedObserver
: public chromeos::PolicyCertificateProvider::Observer {
public:
WebTrustedCertsChangedObserver() = default;
// chromeos::PolicyCertificateProvider::Observer
void OnPolicyProvidedCertsChanged() override { run_loop_.Quit(); }
void Wait() { run_loop_.Run(); }
private:
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(WebTrustedCertsChangedObserver);
};
// Allows waiting until |NetworkCertLoader| updates its list of certificates.
class NetworkCertLoaderTestObserver
: public chromeos::NetworkCertLoader::Observer {
public:
explicit NetworkCertLoaderTestObserver(
chromeos::NetworkCertLoader* network_cert_loader)
: network_cert_loader_(network_cert_loader) {
network_cert_loader_->AddObserver(this);
}
~NetworkCertLoaderTestObserver() override {
network_cert_loader_->RemoveObserver(this);
}
// chromeos::NetworkCertLoader::Observer
void OnCertificatesLoaded() override { run_loop_.Quit(); }
void Wait() { run_loop_.Run(); }
private:
chromeos::NetworkCertLoader* network_cert_loader_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(NetworkCertLoaderTestObserver);
};
// Allows waiting until the |CertDatabase| notifies its observers that it has
// changd.
class CertDatabaseChangedObserver : public net::CertDatabase::Observer {
public:
CertDatabaseChangedObserver() {}
void OnCertDBChanged() override { run_loop_.Quit(); }
void Wait() { run_loop_.Run(); }
private:
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(CertDatabaseChangedObserver);
};
// Observer that allows waiting until the background page of the specified
// extension/app loads.
// TODO(https://crbug.com/991464): Extract this into a more generic helper class
// for using in other tests.
class ExtensionBackgroundPageReadyObserver final {
public:
explicit ExtensionBackgroundPageReadyObserver(const std::string& extension_id)
: extension_id_(extension_id),
notification_observer_(
extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
base::Bind(
&ExtensionBackgroundPageReadyObserver::IsNotificationRelevant,
base::Unretained(this))) {}
void Wait() { notification_observer_.Wait(); }
private:
// Callback which is used for |WindowedNotificationObserver| for checking
// whether the condition being awaited is met.
bool IsNotificationRelevant(
const content::NotificationSource& source,
const content::NotificationDetails& details) const {
return content::Source<const extensions::Extension>(source)->id() ==
extension_id_;
}
const std::string extension_id_;
content::WindowedNotificationObserver notification_observer_;
DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver);
};
// 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;
// Installs the BrowserPolicyConnector to set user policy.
void SetUpInProcessBrowserTestFixture() {
is_set_up_ = true;
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());
base::FilePath root_cert_path = test_certs_path.AppendASCII(kRootCaCert);
root_cert_ = net::ImportCertFromFile(root_cert_path.DirName(),
root_cert_path.BaseName().value());
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());
// Set up the mock policy provider.
EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
.WillRepeatedly(testing::Return(true));
BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
}
// 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) {
std::string onc_policy_data = GetTestCertsFileContents(kRootCaCertOnc);
SetONCUserPolicy(profile, 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) {
std::string onc_policy_data =
GetTestCertsFileContents(kRootAndIntermediateCaCertsOnc);
SetONCUserPolicy(profile, 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, const std::string& onc_policy_data) {
ASSERT_TRUE(is_set_up_);
UserNetworkConfigurationUpdater* user_network_configuration_updater =
UserNetworkConfigurationUpdaterFactory::GetForBrowserContext(profile);
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,
std::make_unique<base::Value>(onc_policy_data), nullptr);
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);
}
// This is set to true when |SetUpInProcessBrowserTestFixture| has been
// called.
bool is_set_up_ = false;
MockConfigurationPolicyProvider provider_;
// 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_;
};
// Verifies |certificate| with |storage_partition|'s CertVerifier and returns
// the result.
int VerifyTestServerCertInStoragePartition(
content::StoragePartition* storage_partition,
const scoped_refptr<net::X509Certificate>& certificate) {
mojo::ScopedAllowSyncCallForTesting allow_sync_call;
int result = net::OK;
storage_partition->GetNetworkContext()->VerifyCertificateForTesting(
certificate, "127.0.0.1", /*ocsp_response=*/std::string(),
/*sct_list=*/std::string(), &result);
return result;
}
// 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(
content::BrowserContext::GetDefaultStoragePartition(profile),
certificate);
}
// Returns true if |cert_handle| refers to a certificate that has a subject
// CommonName equal to |subject_common_name|.
bool HasSubjectCommonName(CERTCertificate* cert_handle,
const std::string& subject_common_name) {
char* nss_text = CERT_GetCommonName(&cert_handle->subject);
if (!nss_text)
return false;
const bool result = subject_common_name == nss_text;
PORT_Free(nss_text);
return result;
}
void IsCertInNSSDatabaseOnIOThreadWithCertList(
const std::string& subject_common_name,
bool* out_system_slot_available,
base::OnceClosure done_closure,
net::ScopedCERTCertificateList certs) {
for (const net::ScopedCERTCertificate& cert : certs) {
if (HasSubjectCommonName(cert.get(), subject_common_name)) {
*out_system_slot_available = true;
break;
}
}
std::move(done_closure).Run();
}
void IsCertInNSSDatabaseOnIOThreadWithCertDb(
const std::string& subject_common_name,
bool* out_system_slot_available,
base::OnceClosure done_closure,
net::NSSCertDatabase* cert_db) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
cert_db->ListCerts(base::BindOnce(
&IsCertInNSSDatabaseOnIOThreadWithCertList, subject_common_name,
out_system_slot_available, std::move(done_closure)));
}
void IsCertInNSSDatabaseOnIOThread(content::ResourceContext* resource_context,
const std::string& subject_common_name,
bool* out_cert_found,
base::OnceClosure done_closure) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
auto did_get_cert_db_callback = base::BindRepeating(
&IsCertInNSSDatabaseOnIOThreadWithCertDb, subject_common_name,
out_cert_found, base::AdaptCallbackForRepeating(std::move(done_closure)));
net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext(
resource_context, did_get_cert_db_callback);
if (cert_db)
did_get_cert_db_callback.Run(cert_db);
}
// Returns true if a certificate with subject CommonName |common_name| is
// present in the |NSSCertDatbase| for |profile|.
bool IsCertInNSSDatabase(Profile* profile,
const std::string& subject_common_name) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::RunLoop run_loop;
bool cert_found = false;
base::PostTask(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(IsCertInNSSDatabaseOnIOThread,
profile->GetResourceContext(), subject_common_name,
&cert_found, run_loop.QuitClosure()));
run_loop.Run();
return cert_found;
}
bool IsCertInCertificateList(
const net::X509Certificate* cert,
const chromeos::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;
}
} // namespace
// Allows testing if user policy provided trust roots take effect, without
// having device policy.
class PolicyProvidedCertsRegularUserTest : public InProcessBrowserTest {
protected:
PolicyProvidedCertsRegularUserTest() {
// 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 */);
}
void SetUpInProcessBrowserTestFixture() override {
InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
user_policy_certs_helper_.SetUpInProcessBrowserTestFixture();
}
UserPolicyCertsHelper user_policy_certs_helper_;
crypto::ScopedTestNSSDB test_nssdb_;
std::unique_ptr<net::NSSCertDatabase> test_nss_cert_db_;
};
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest, TrustAnchorApplied) {
user_policy_certs_helper_.SetRootCertONCUserPolicy(browser()->profile());
EXPECT_EQ(net::OK,
VerifyTestServerCert(browser()->profile(),
user_policy_certs_helper_.server_cert()));
}
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsRegularUserTest,
UntrustedIntermediateAuthorityApplied) {
// Sanity check: Apply ONC policy which does not mention the intermediate
// authority.
user_policy_certs_helper_.SetRootCertONCUserPolicy(browser()->profile());
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyTestServerCert(
browser()->profile(),
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(
browser()->profile());
EXPECT_EQ(net::OK,
VerifyTestServerCert(
browser()->profile(),
user_policy_certs_helper_.server_cert_by_intermediate()));
}
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(chromeos::NetworkCertLoader::IsInitialized());
chromeos::NetworkCertLoader::Get()->SetUserNSSDB(test_nss_cert_db_.get());
EXPECT_FALSE(IsCertInCertificateList(
user_policy_certs_helper_.root_cert().get(),
chromeos::NetworkCertLoader::Get()->authority_certs()));
NetworkCertLoaderTestObserver network_cert_loader_observer(
chromeos::NetworkCertLoader::Get());
user_policy_certs_helper_.SetRootCertONCUserPolicy(browser()->profile());
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(),
chromeos::NetworkCertLoader::Get()->authority_certs()));
}
// Base class for testing policy-provided trust roots with device-local
// accounts. Needs device policy.
class PolicyProvidedCertsDeviceLocalAccountTest
: public DevicePolicyCrosBrowserTest {
public:
PolicyProvidedCertsDeviceLocalAccountTest() {
// 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 */);
}
protected:
virtual void SetupDevicePolicy() = 0;
void SetUpInProcessBrowserTestFixture() override {
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
user_policy_certs_helper_.SetUpInProcessBrowserTestFixture();
device_policy()->policy_data().set_public_key_version(1);
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
proto.mutable_show_user_names()->set_show_user_names(true);
SetupDevicePolicy();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
command_line->AppendSwitch(chromeos::switches::kOobeSkipPostLogin);
}
chromeos::LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
const AccountId device_local_account_id_ =
AccountId::FromUserEmail(GenerateDeviceLocalAccountUserId(
kDeviceLocalAccountId,
DeviceLocalAccount::TYPE_PUBLIC_SESSION));
UserPolicyCertsHelper user_policy_certs_helper_;
crypto::ScopedTestNSSDB test_nssdb_;
std::unique_ptr<net::NSSCertDatabase> test_nss_cert_db_;
};
// Sets up device policy for public session and provides functions to sing into
// it.
class PolicyProvidedCertsPublicSessionTest
: public PolicyProvidedCertsDeviceLocalAccountTest {
protected:
// PolicyProvidedCertsDeviceLocalAccountTest:
void SetupDevicePolicy() override {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
em::DeviceLocalAccountInfoProto* account =
proto.mutable_device_local_accounts()->add_account();
account->set_account_id(kDeviceLocalAccountId);
account->set_type(
em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
RefreshDevicePolicy();
ASSERT_TRUE(local_policy_mixin_.UpdateDevicePolicy(proto));
}
void StartLogin() {
chromeos::WizardController::SkipPostLoginScreensForTesting();
chromeos::WizardController* const wizard_controller =
chromeos::WizardController::default_controller();
ASSERT_TRUE(wizard_controller);
wizard_controller->SkipToLoginForTesting(chromeos::LoginScreenContext());
content::WindowedNotificationObserver(
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
content::NotificationService::AllSources())
.Wait();
// Login into the public session.
chromeos::ExistingUserController* controller =
chromeos::ExistingUserController::current_controller();
ASSERT_TRUE(controller);
chromeos::UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
device_local_account_id_);
controller->Login(user_context, chromeos::SigninSpecifics());
}
};
// TODO(https://crbug.com/874831): Re-enable this after the source of the
// flakiness has been identified.
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsPublicSessionTest,
DISABLED_AllowedInPublicSession) {
StartLogin();
chromeos::test::WaitForPrimaryUserSessionStart();
BrowserList* browser_list = BrowserList::GetInstance();
EXPECT_EQ(1U, browser_list->size());
Browser* browser = browser_list->get(0);
ASSERT_TRUE(browser);
user_policy_certs_helper_.SetRootCertONCUserPolicy(browser->profile());
EXPECT_EQ(net::OK,
VerifyTestServerCert(browser->profile(),
user_policy_certs_helper_.server_cert()));
}
class PolicyProvidedCertsOnUserSessionInitTest : public LoginPolicyTestBase {
protected:
PolicyProvidedCertsOnUserSessionInitTest() {}
void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const override {
std::string user_policy_blob = GetTestCertsFileContents(kRootCaCertOnc);
policy->SetKey(key::kOpenNetworkConfiguration,
base::Value(user_policy_blob));
}
void TriggerLogIn() {
chromeos::LoginDisplayHost::default_host()
->GetOobeUI()
->GetView<chromeos::GaiaScreenHandler>()
->ShowSigninScreenForTest(kAccountId, kAccountPassword, kEmptyServices);
}
Profile* active_user_profile() {
const user_manager::User* const user =
user_manager::UserManager::Get()->GetActiveUser();
Profile* const profile =
chromeos::ProfileHelper::Get()->GetProfileByUser(user);
return profile;
}
private:
DISALLOW_COPY_AND_ASSIGN(PolicyProvidedCertsOnUserSessionInitTest);
};
// Verifies that the policy-provided trust root is active as soon as the user
// session starts.
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsOnUserSessionInitTest,
TrustAnchorsAvailableImmediatelyAfterSessionStart) {
// Load the certificate which is only OK if the policy-provided authority is
// actually trusted.
base::FilePath test_certs_path = GetTestCertsPath();
base::FilePath server_cert_path = test_certs_path.AppendASCII(kServerCert);
scoped_refptr<net::X509Certificate> server_cert = net::ImportCertFromFile(
server_cert_path.DirName(), server_cert_path.BaseName().value());
SkipToLoginScreen();
TriggerLogIn();
EXPECT_FALSE(session_manager::SessionManager::Get()->IsSessionStarted());
chromeos::test::WaitForPrimaryUserSessionStart();
EXPECT_EQ(net::OK, VerifyTestServerCert(active_user_profile(), server_cert));
}
// Testing policy-provided client cert import.
class PolicyProvidedClientCertsTest : public DevicePolicyCrosBrowserTest {
protected:
PolicyProvidedClientCertsTest() {}
~PolicyProvidedClientCertsTest() override {}
void SetUpInProcessBrowserTestFixture() override {
// Set up the mock policy provider.
EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
.WillRepeatedly(testing::Return(true));
BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
}
// Sets the ONC-policy to the blob defined by |kClientCertOnc|. Then waits
// until |CertDatabase| notifies its observers that the database has changed.
void SetClientCertONCPolicy(Profile* profile) {
net::CertDatabase* cert_database = net::CertDatabase::GetInstance();
CertDatabaseChangedObserver cert_database_changed_observer;
cert_database->AddObserver(&cert_database_changed_observer);
const std::string& user_policy_blob =
chromeos::onc::test_utils::ReadTestData(kClientCertOnc);
policy::PolicyMap policy;
policy.Set(key::kOpenNetworkConfiguration, policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
std::make_unique<base::Value>(user_policy_blob), nullptr);
provider_.UpdateChromePolicy(policy);
cert_database_changed_observer.Wait();
cert_database->RemoveObserver(&cert_database_changed_observer);
}
private:
MockConfigurationPolicyProvider provider_;
};
IN_PROC_BROWSER_TEST_F(PolicyProvidedClientCertsTest, ClientCertsImported) {
// Sanity check: we don't expect the client certificate to be present before
// setting the user ONC policy.
EXPECT_FALSE(
IsCertInNSSDatabase(browser()->profile(), kClientCertSubjectCommonName));
SetClientCertONCPolicy(browser()->profile());
EXPECT_TRUE(
IsCertInNSSDatabase(browser()->profile(), kClientCertSubjectCommonName));
}
// TODO(https://crbug.com/874937): Add a test case for a kiosk session.
// Class for testing policy-provided extensions in the sign-in profile.
// Sets a device policy which applies the |kRootCaCert| for
// |kSigninScreenExtension1|. Force-installs |kSigninScreenExtension1| and
// |kSigninScreenExtension2| into the sign-in profile.
class PolicyProvidedCertsForSigninExtensionTest
: public SigninProfileExtensionsPolicyTestBase {
protected:
// Use DEV channel as sign-in screen extensions are currently usable there.
PolicyProvidedCertsForSigninExtensionTest()
: SigninProfileExtensionsPolicyTestBase(version_info::Channel::DEV) {}
~PolicyProvidedCertsForSigninExtensionTest() override = default;
void SetUpInProcessBrowserTestFixture() override {
scoped_feature_list_.InitAndEnableFeature(
net::features::kCertVerifierBuiltinFeature);
// Apply |kRootCaCert| for |kSigninScreenExtension1| in Device ONC policy.
base::FilePath test_certs_path = GetTestCertsPath();
std::string x509_contents;
{
base::ScopedAllowBlockingForTesting allow_io;
ASSERT_TRUE(base::ReadFileToString(
test_certs_path.AppendASCII(kRootCaCert), &x509_contents));
}
base::Value onc_dict = BuildONCForExtensionScopedCertificate(
x509_contents, kSigninScreenExtension1);
ASSERT_TRUE(base::JSONWriter::Write(
onc_dict, device_policy()
->payload()
.mutable_open_network_configuration()
->mutable_open_network_configuration()));
// Load the certificate which is only OK if the policy-provided authority is
// actually trusted.
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_);
SigninProfileExtensionsPolicyTestBase::SetUpInProcessBrowserTestFixture();
}
void SetUpOnMainThread() override {
chromeos::StartupUtils::MarkOobeCompleted(); // Pretend that OOBE was
// complete.
SigninProfileExtensionsPolicyTestBase::SetUpOnMainThread();
signin_profile_ = GetInitialProfile();
ASSERT_TRUE(chromeos::ProfileHelper::IsSigninProfile(signin_profile_));
ExtensionBackgroundPageReadyObserver extension_1_observer(
kSigninScreenExtension1);
ExtensionBackgroundPageReadyObserver extension_2_observer(
kSigninScreenExtension2);
AddExtensionForForceInstallation(kSigninScreenExtension1,
kSigninScreenExtension1UpdateManifestPath);
AddExtensionForForceInstallation(kSigninScreenExtension2,
kSigninScreenExtension2UpdateManifestPath);
extension_1_observer.Wait();
extension_2_observer.Wait();
}
content::StoragePartition* GetStoragePartitionForSigninExtension(
const std::string& extension_id) {
const GURL site =
extensions::util::GetSiteForExtensionId(extension_id, signin_profile_);
return content::BrowserContext::GetStoragePartitionForSite(
signin_profile_, site, /*can_create=*/false);
}
Profile* signin_profile_ = nullptr;
scoped_refptr<net::X509Certificate> server_cert_;
private:
// Builds an ONC policy value that specifies exactly one certificate described
// by |x509_contents| with Web trust to be used for |extension_id|.
base::Value BuildONCForExtensionScopedCertificate(
const std::string& x509_contents,
const std::string& extension_id) {
base::Value onc_cert_scope(base::Value::Type::DICTIONARY);
onc_cert_scope.SetKey(onc::scope::kType,
base::Value(onc::scope::kExtension));
onc_cert_scope.SetKey(onc::scope::kId, base::Value(extension_id));
base::Value onc_cert_trust_bits(base::Value::Type::LIST);
onc_cert_trust_bits.Append(base::Value(onc::certificate::kWeb));
base::Value onc_certificate(base::Value::Type::DICTIONARY);
onc_certificate.SetKey(onc::certificate::kGUID, base::Value("guid"));
onc_certificate.SetKey(onc::certificate::kType,
base::Value(onc::certificate::kAuthority));
onc_certificate.SetKey(onc::certificate::kX509, base::Value(x509_contents));
onc_certificate.SetKey(onc::certificate::kScope, std::move(onc_cert_scope));
onc_certificate.SetKey(onc::certificate::kTrustBits,
std::move(onc_cert_trust_bits));
base::Value onc_certificates(base::Value::Type::LIST);
onc_certificates.Append(std::move(onc_certificate));
base::Value onc_dict(base::Value::Type::DICTIONARY);
onc_dict.SetKey(onc::toplevel_config::kCertificates,
std::move(onc_certificates));
onc_dict.SetKey(
onc::toplevel_config::kType,
base::Value(onc::toplevel_config::kUnencryptedConfiguration));
return onc_dict;
}
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(PolicyProvidedCertsForSigninExtensionTest);
}; // namespace policy
// Verifies that a device-policy-provided, extension-scoped trust anchor is
// active only in the sign-in profile extension for which it was specified.
// Additionally verifies that it is not active
// (*) in the default StoragePartition of the sign-in profile,
// (*) in the StoragePartition used for the webview hosting GAIA and
// (*) in a different sign-in profile extension than the one for which it was
// specified.
// Verification of all these aspects has been intentionally put into one test,
// so if the verification result leaks (e.g. due to accidentally reusing
// caches), the test is able to catch that.
IN_PROC_BROWSER_TEST_F(PolicyProvidedCertsForSigninExtensionTest,
ActiveOnlyInSelectedExtension) {
chromeos::OobeScreenWaiter(chromeos::GaiaView::kScreenId).Wait();
content::StoragePartition* signin_profile_default_partition =
content::BrowserContext::GetDefaultStoragePartition(signin_profile_);
// Active in the StoragePartition of the extension for which the certificate
// has been specified in policy.
content::StoragePartition* extension_1_partition =
GetStoragePartitionForSigninExtension(kSigninScreenExtension1);
ASSERT_TRUE(extension_1_partition);
EXPECT_NE(signin_profile_default_partition, extension_1_partition);
EXPECT_EQ(net::OK, VerifyTestServerCertInStoragePartition(
extension_1_partition, server_cert_));
// Not active in default StoragePartition.
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyTestServerCertInStoragePartition(
signin_profile_default_partition, server_cert_));
// Not active in the StoragePartition used for the webview hosting GAIA.
content::StoragePartition* signin_frame_partition =
chromeos::login::GetSigninPartition();
EXPECT_NE(signin_profile_default_partition, signin_frame_partition);
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyTestServerCertInStoragePartition(signin_frame_partition,
server_cert_));
// Not active in the StoragePartition of another extension.
content::StoragePartition* extension_2_partition =
GetStoragePartitionForSigninExtension(kSigninScreenExtension2);
ASSERT_TRUE(extension_2_partition);
EXPECT_NE(signin_profile_default_partition, extension_2_partition);
EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
VerifyTestServerCertInStoragePartition(extension_2_partition,
server_cert_));
}
} // namespace policy