1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
|
// Copyright 2017 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 "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.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/task/current_thread.h"
#include "base/test/test_future.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/ash/login/helper.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/test/login_or_lock_screen_visible_waiter.h"
#include "chrome/browser/ash/login/test/oobe_base_test.h"
#include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
#include "chrome/browser/ash/login/wizard_controller.h"
#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
#include "chrome/browser/ash/policy/login/login_policy_test_base.h"
#include "chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.h"
#include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
#include "chrome/browser/ash/profiles/profile_helper.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/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/ui/ash/login/login_display_host.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.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/ash/components/network/policy_certificate_provider.h"
#include "chromeos/ash/components/policy/device_local_account/device_local_account_type.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 "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.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/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 "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"
#include "url/gurl.h"
namespace policy {
namespace {
namespace em = ::enterprise_management;
// 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 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 the |CertDatabase| notifies its observers that a client
// cert change has occurred.
class CertDatabaseChangedObserver : public net::CertDatabase::Observer {
public:
CertDatabaseChangedObserver() = default;
CertDatabaseChangedObserver(const CertDatabaseChangedObserver&) = delete;
CertDatabaseChangedObserver& operator=(const CertDatabaseChangedObserver&) =
delete;
void OnClientCertStoreChanged() override { run_loop_.Quit(); }
void Wait() { run_loop_.Run(); }
private:
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);
WebTrustedCertsChangedObserver trust_roots_changed_observer;
user_network_configuration_updater->AddPolicyProvidedCertsObserver(
&trust_roots_changed_observer);
PolicyMap policy;
policy.Set(key::kOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, 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);
}
// 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) {
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);
}
// 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(NssCertDatabaseGetter database_getter,
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_split_callback = base::SplitOnceCallback(base::BindOnce(
&IsCertInNSSDatabaseOnIOThreadWithCertDb, subject_common_name,
out_cert_found, std::move(done_closure)));
net::NSSCertDatabase* cert_db =
std::move(database_getter)
.Run(std::move(did_get_cert_db_split_callback.first));
if (cert_db) {
std::move(did_get_cert_db_split_callback.second).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;
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(IsCertInNSSDatabaseOnIOThread,
NssServiceFactory::GetForContext(profile)
->CreateNSSCertDatabaseGetterForIOThread(),
subject_common_name, &cert_found, run_loop.QuitClosure()));
run_loop.Run();
return cert_found;
}
} // namespace
// 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();
ASSERT_NO_FATAL_FAILURE(user_policy_certs_helper_.Initialize());
// Set up the mock policy provider.
EXPECT_CALL(user_policy_provider_, IsInitializationComplete(testing::_))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(user_policy_provider_, IsFirstPolicyLoadComplete(testing::_))
.WillRepeatedly(testing::Return(true));
BrowserPolicyConnector::SetPolicyProviderForTesting(&user_policy_provider_);
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(ash::switches::kLoginManager);
command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
command_line->AppendSwitchASCII(ash::switches::kLoginProfile, "user");
command_line->AppendSwitch(ash::switches::kOobeSkipPostLogin);
}
ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
const AccountId device_local_account_id_ = AccountId::FromUserEmail(
GenerateDeviceLocalAccountUserId(kDeviceLocalAccountId,
DeviceLocalAccountType::kPublicSession));
MockConfigurationPolicyProvider user_policy_provider_;
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();
policy_test_server_mixin_.UpdateDevicePolicy(proto);
}
// TODO(crbug/874831): Consider migrating to LoggedInMixin and deprecating
// this function.
void StartLogin() {
auto* const wizard_controller = ash::WizardController::default_controller();
ASSERT_TRUE(wizard_controller);
wizard_controller->SkipToLoginForTesting();
ash::LoginOrLockScreenVisibleWaiter().Wait();
// Login into the public session.
auto* controller = ash::ExistingUserController::current_controller();
ASSERT_TRUE(controller);
ash::UserContext user_context(user_manager::UserType::kPublicAccount,
device_local_account_id_);
controller->Login(user_context, ash::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();
ash::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(),
&user_policy_provider_);
EXPECT_EQ(net::OK,
VerifyTestServerCert(browser->profile(),
user_policy_certs_helper_.server_cert()));
}
class PolicyProvidedCertsOnUserSessionInitTest : public LoginPolicyTestBase {
public:
PolicyProvidedCertsOnUserSessionInitTest(
const PolicyProvidedCertsOnUserSessionInitTest&) = delete;
PolicyProvidedCertsOnUserSessionInitTest& operator=(
const PolicyProvidedCertsOnUserSessionInitTest&) = delete;
protected:
PolicyProvidedCertsOnUserSessionInitTest() = default;
void GetPolicySettings(em::CloudPolicySettings* policy) const override {
std::string user_policy_blob = GetTestCertsFileContents(kRootCaCertOnc);
policy->mutable_opennetworkconfiguration()->set_value(user_policy_blob);
}
Profile* active_user_profile() {
const user_manager::User* const user =
user_manager::UserManager::Get()->GetActiveUser();
Profile* const profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
return profile;
}
};
// 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());
OobeBaseTest::WaitForSigninScreen();
TriggerLogIn();
ash::test::WaitForPrimaryUserSessionStart();
EXPECT_EQ(net::OK, VerifyTestServerCert(active_user_profile(), server_cert));
}
// Testing policy-provided client cert import.
class PolicyProvidedClientCertsTest : public DevicePolicyCrosBrowserTest {
protected:
PolicyProvidedClientCertsTest() = default;
~PolicyProvidedClientCertsTest() override = default;
void SetUpInProcessBrowserTestFixture() override {
// Set up the mock policy provider.
EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(provider_, IsFirstPolicyLoadComplete(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);
PolicyMap policy;
policy.Set(key::kOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
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(crbug.com/40589684): 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 {
public:
PolicyProvidedCertsForSigninExtensionTest(
const PolicyProvidedCertsForSigninExtensionTest&) = delete;
PolicyProvidedCertsForSigninExtensionTest& operator=(
const PolicyProvidedCertsForSigninExtensionTest&) = delete;
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 {
// 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::Dict 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 {
ash::StartupUtils::MarkOobeCompleted(); // Pretend that OOBE was complete.
SigninProfileExtensionsPolicyTestBase::SetUpOnMainThread();
signin_profile_ = GetInitialProfile();
ASSERT_TRUE(ash::ProfileHelper::IsSigninProfile(signin_profile_));
extensions::ExtensionRegistry* extension_registry =
extensions::ExtensionRegistry::Get(signin_profile_);
extensions::TestExtensionRegistryObserver extension_1_observer(
extension_registry, kSigninScreenExtension1);
extensions::TestExtensionRegistryObserver extension_2_observer(
extension_registry, kSigninScreenExtension2);
AddExtensionForForceInstallation(kSigninScreenExtension1,
kSigninScreenExtension1UpdateManifestPath);
AddExtensionForForceInstallation(kSigninScreenExtension2,
kSigninScreenExtension2UpdateManifestPath);
extension_1_observer.WaitForExtensionLoaded();
extension_2_observer.WaitForExtensionLoaded();
}
content::StoragePartition* GetStoragePartitionForSigninExtension(
const std::string& extension_id) {
return extensions::util::GetStoragePartitionForExtensionId(
extension_id, signin_profile_, /*can_create=*/false);
}
raw_ptr<Profile, DanglingUntriaged> 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::Dict BuildONCForExtensionScopedCertificate(
const std::string& x509_contents,
const std::string& extension_id) {
auto onc_cert_scope = base::Value::Dict()
.Set(onc::scope::kType, onc::scope::kExtension)
.Set(onc::scope::kId, extension_id);
auto onc_cert_trust_bits =
base::Value::List().Append(onc::certificate::kWeb);
auto onc_certificate =
base::Value::Dict()
.Set(onc::certificate::kGUID, base::Value("guid"))
.Set(onc::certificate::kType, onc::certificate::kAuthority)
.Set(onc::certificate::kX509, x509_contents)
.Set(onc::certificate::kScope, std::move(onc_cert_scope))
.Set(onc::certificate::kTrustBits, std::move(onc_cert_trust_bits));
auto onc_certificates =
base::Value::List().Append(std::move(onc_certificate));
auto onc_dict = base::Value::Dict()
.Set(onc::toplevel_config::kCertificates,
std::move(onc_certificates))
.Set(onc::toplevel_config::kType,
onc::toplevel_config::kUnencryptedConfiguration);
return onc_dict;
}
}; // 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) {
ash::OobeScreenWaiter(ash::OobeBaseTest::GetFirstSigninScreen()).Wait();
content::StoragePartition* signin_profile_default_partition =
signin_profile_->GetDefaultStoragePartition();
// 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 =
ash::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
|