|  | // Copyright 2018 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chrome/browser/certificate_manager_model.h" | 
|  |  | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/observer_list.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/test/test_future.h" | 
|  | #include "build/chromeos_buildflags.h" | 
|  | #include "content/public/test/browser_task_environment.h" | 
|  | #include "crypto/scoped_test_nss_db.h" | 
|  | #include "net/cert/nss_cert_database.h" | 
|  | #include "net/cert/scoped_nss_types.h" | 
|  | #include "net/cert/x509_util_nss.h" | 
|  | #include "net/ssl/client_cert_identity_test_util.h" | 
|  | #include "net/test/cert_builder.h" | 
|  | #include "net/test/cert_test_util.h" | 
|  | #include "net/test/test_data_directory.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | #include "chrome/browser/certificate_provider/certificate_provider.h" | 
|  | #include "chromeos/ash/components/network/policy_certificate_provider.h" | 
|  | #include "chromeos/components/onc/certificate_scope.h" | 
|  | #endif  // BUILDFLAG(IS_CHROMEOS) | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // A fake CertificateManagerModel::Observer that has the ability to execute a | 
|  | // OnceClosure passed to it when |CertificatesRefreshed| is called. | 
|  | class FakeObserver : public CertificateManagerModel::Observer { | 
|  | public: | 
|  | void CertificatesRefreshed() override { | 
|  | if (!run_on_refresh_.is_null()) | 
|  | std::move(run_on_refresh_).Run(); | 
|  | } | 
|  |  | 
|  | // Execute |closure| on the next |CertificatesRefreshed| invocation. | 
|  | void RunOnNextRefresh(base::OnceClosure closure) { | 
|  | run_on_refresh_ = std::move(closure); | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::OnceClosure run_on_refresh_; | 
|  | }; | 
|  |  | 
|  | // Looks up a |CertInfo| in |org_grouping_map| corresponding to |cert|. Returns | 
|  | // nullptr if no such |CertInfo| was found. | 
|  | CertificateManagerModel::CertInfo* GetCertInfoFromOrgGroupingMap( | 
|  | const CertificateManagerModel::OrgGroupingMap& org_grouping_map, | 
|  | CERTCertificate* cert) { | 
|  | for (const auto& org_and_cert_info_list : org_grouping_map) { | 
|  | for (const auto& cert_info : org_and_cert_info_list.second) { | 
|  | if (net::x509_util::IsSameCertificate(cert_info->cert(), cert)) | 
|  | return cert_info.get(); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class CertificateManagerModelTest : public testing::Test { | 
|  | public: | 
|  | CertificateManagerModelTest() {} | 
|  |  | 
|  | CertificateManagerModelTest(const CertificateManagerModelTest&) = delete; | 
|  | CertificateManagerModelTest& operator=(const CertificateManagerModelTest&) = | 
|  | delete; | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | ASSERT_TRUE(test_nssdb_.is_open()); | 
|  |  | 
|  | 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 */); | 
|  |  | 
|  | fake_observer_ = std::make_unique<FakeObserver>(); | 
|  | certificate_manager_model_ = std::make_unique<CertificateManagerModel>( | 
|  | GetCertificateManagerModelParams(), fake_observer_.get(), | 
|  | nss_cert_db_.get()); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | certificate_manager_model_.reset(); | 
|  | nss_cert_db_.reset(); | 
|  | } | 
|  |  | 
|  | // Provides the platform-specific |Params| (containing policy/extension | 
|  | // certificate provides on Chrome OS). | 
|  | virtual std::unique_ptr<CertificateManagerModel::Params> | 
|  | GetCertificateManagerModelParams() { | 
|  | return std::make_unique<CertificateManagerModel::Params>(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Invoke an explicit Refresh if the refresh is triggered and wait until the | 
|  | // observer has been notified. | 
|  | void WaitForRefresh(bool trigger_refresh) { | 
|  | base::RunLoop run_loop; | 
|  | fake_observer_->RunOnNextRefresh(run_loop.QuitClosure()); | 
|  | if (trigger_refresh) { | 
|  | certificate_manager_model_->Refresh(); | 
|  | } | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | content::BrowserTaskEnvironment task_environment_; | 
|  | crypto::ScopedTestNSSDB test_nssdb_; | 
|  | std::unique_ptr<net::NSSCertDatabase> nss_cert_db_; | 
|  | std::unique_ptr<FakeObserver> fake_observer_; | 
|  | std::unique_ptr<CertificateManagerModel> certificate_manager_model_; | 
|  | }; | 
|  |  | 
|  | // CertificateManagerModel correctly lists CA certificates from the platform NSS | 
|  | // Database. | 
|  | TEST_F(CertificateManagerModelTest, ListsCertsFromPlatform) { | 
|  | net::CertificateList orig_certs = CreateCertificateListFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem", | 
|  | net::X509Certificate::FORMAT_AUTO); | 
|  | ASSERT_EQ(1U, orig_certs.size()); | 
|  |  | 
|  | net::CertBuilder cert_builder(orig_certs[0]->cert_buffer(), nullptr); | 
|  | scoped_refptr<net::X509Certificate> x509_cert = | 
|  | cert_builder.GetX509Certificate(); | 
|  |  | 
|  | net::ScopedCERTCertificate cert = | 
|  | net::x509_util::CreateCERTCertificateFromX509Certificate(x509_cert.get()); | 
|  | std::string cert_subject_name = x509_cert->subject().GetDisplayName(); | 
|  |  | 
|  | ASSERT_EQ(SECSuccess, | 
|  | PK11_ImportCert(test_nssdb_.slot(), cert.get(), CK_INVALID_HANDLE, | 
|  | "cert", PR_FALSE /* includeTrust (unused) */)); | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get()); | 
|  | ASSERT_TRUE(cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, cert_info->type()); | 
|  | EXPECT_EQ(base::UTF8ToUTF16(cert_subject_name), cert_info->name()); | 
|  | EXPECT_TRUE(cert_info->can_be_deleted()); | 
|  | // This platform cert is untrusted because it is self-signed and has no | 
|  | // trust bits. | 
|  | EXPECT_TRUE(cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform, | 
|  | cert_info->source()); | 
|  | EXPECT_FALSE(cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | certificate_manager_model_->SetCertTrust(cert.get(), net::CertType::CA_CERT, | 
|  | net::NSSCertDatabase::TRUSTED_SSL); | 
|  | // Wait for refresh without triggering because observer should be notified by | 
|  | // net::CertDatabase and refresh automatically. | 
|  | WaitForRefresh(false /*tigger_for_refresh*/); | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get()); | 
|  | ASSERT_TRUE(cert_info); | 
|  |  | 
|  | EXPECT_FALSE(cert_info->untrusted()); | 
|  | EXPECT_TRUE(cert_info->web_trust_anchor()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // CertificateManagerModel correctly lists client certificates from the platform | 
|  | // NSS Database. | 
|  | TEST_F(CertificateManagerModelTest, ListsClientCertsFromPlatform) { | 
|  | net::ScopedCERTCertificate platform_client_cert; | 
|  | net::ImportClientCertAndKeyFromFile( | 
|  | net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8", | 
|  | test_nssdb_.slot(), &platform_client_cert); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::USER_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, | 
|  | platform_client_cert.get()); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::USER_CERT, platform_cert_info->type()); | 
|  | EXPECT_EQ(u"Client Cert A", platform_cert_info->name()); | 
|  | EXPECT_TRUE(platform_cert_info->can_be_deleted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform, | 
|  | platform_cert_info->source()); | 
|  | EXPECT_FALSE(platform_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(platform_cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | namespace { | 
|  |  | 
|  | class FakePolicyCertificateProvider : public ash::PolicyCertificateProvider { | 
|  | public: | 
|  | void AddPolicyProvidedCertsObserver(Observer* observer) override { | 
|  | observer_list_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | void RemovePolicyProvidedCertsObserver(Observer* observer) override { | 
|  | observer_list_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | net::CertificateList GetAllServerAndAuthorityCertificates( | 
|  | const chromeos::onc::CertificateScope& scope) const override { | 
|  | // The CertificateManagerModel only retrieves profile-wide certificates. | 
|  | EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); | 
|  |  | 
|  | net::CertificateList merged; | 
|  | merged.insert(merged.end(), web_trusted_certs_.begin(), | 
|  | web_trusted_certs_.end()); | 
|  | merged.insert(merged.end(), not_web_trusted_certs_.begin(), | 
|  | not_web_trusted_certs_.end()); | 
|  | return merged; | 
|  | } | 
|  |  | 
|  | net::CertificateList GetAllAuthorityCertificates( | 
|  | const chromeos::onc::CertificateScope& scope) const override { | 
|  | // This function is not called by CertificateManagerModel. | 
|  | NOTREACHED(); | 
|  | return net::CertificateList(); | 
|  | } | 
|  |  | 
|  | net::CertificateList GetWebTrustedCertificates( | 
|  | const chromeos::onc::CertificateScope& scope) const override { | 
|  | // The CertificateManagerModel only retrieves profile-wide certificates. | 
|  | EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); | 
|  |  | 
|  | return web_trusted_certs_; | 
|  | } | 
|  |  | 
|  | net::CertificateList GetCertificatesWithoutWebTrust( | 
|  | const chromeos::onc::CertificateScope& scope) const override { | 
|  | // The CertificateManagerModel only retrieves profile-wide certificates. | 
|  | EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope); | 
|  |  | 
|  | return not_web_trusted_certs_; | 
|  | } | 
|  |  | 
|  | const std::set<std::string>& GetExtensionIdsWithPolicyCertificates() | 
|  | const override { | 
|  | // This function is not called by CertificateManagerModel. | 
|  | NOTREACHED(); | 
|  | return kNoExtensions; | 
|  | } | 
|  |  | 
|  | void SetPolicyProvidedCertificates( | 
|  | const net::CertificateList& web_trusted_certs, | 
|  | const net::CertificateList& not_web_trusted_certs) { | 
|  | web_trusted_certs_ = web_trusted_certs; | 
|  | not_web_trusted_certs_ = not_web_trusted_certs; | 
|  | } | 
|  |  | 
|  | void NotifyObservers() { | 
|  | for (auto& observer : observer_list_) | 
|  | observer.OnPolicyProvidedCertsChanged(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::ObserverList<PolicyCertificateProvider::Observer, | 
|  | true /* check_empty */>::Unchecked observer_list_; | 
|  | net::CertificateList web_trusted_certs_; | 
|  | net::CertificateList not_web_trusted_certs_; | 
|  | const std::set<std::string> kNoExtensions = {}; | 
|  | }; | 
|  |  | 
|  | class FakeExtensionCertificateProvider : public chromeos::CertificateProvider { | 
|  | public: | 
|  | FakeExtensionCertificateProvider( | 
|  | const net::CertificateList* extension_client_certificates, | 
|  | const bool* extensions_hang) | 
|  | : extension_client_certificates_(extension_client_certificates), | 
|  | extensions_hang_(extensions_hang) {} | 
|  |  | 
|  | void GetCertificates( | 
|  | base::OnceCallback<void(net::ClientCertIdentityList)> callback) override { | 
|  | if (*extensions_hang_) | 
|  | return; | 
|  |  | 
|  | std::move(callback).Run(FakeClientCertIdentityListFromCertificateList( | 
|  | *extension_client_certificates_)); | 
|  | } | 
|  |  | 
|  | private: | 
|  | raw_ptr<const net::CertificateList> extension_client_certificates_; | 
|  |  | 
|  | // If *|extensions_hang| is true, the |FakeExtensionCertificateProvider| hangs | 
|  | // - it never calls the callbacks passed to |GetCertificates|. | 
|  | raw_ptr<const bool> extensions_hang_; | 
|  | }; | 
|  |  | 
|  | // Looks up a |CertInfo| in |org_grouping_map| corresponding to |cert|. Returns | 
|  | // nullptr if no such |CertInfo| was found. | 
|  | CertificateManagerModel::CertInfo* GetCertInfoFromOrgGroupingMap( | 
|  | const CertificateManagerModel::OrgGroupingMap& org_grouping_map, | 
|  | const net::X509Certificate* cert) { | 
|  | for (const auto& org_and_cert_info_list : org_grouping_map) { | 
|  | for (const auto& cert_info : org_and_cert_info_list.second) { | 
|  | if (net::x509_util::IsSameCertificate(cert_info->cert(), cert)) | 
|  | return cert_info.get(); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class CertificateManagerModelChromeOSTest : public CertificateManagerModelTest { | 
|  | protected: | 
|  | std::unique_ptr<CertificateManagerModel::Params> | 
|  | GetCertificateManagerModelParams() override { | 
|  | auto params = std::make_unique<CertificateManagerModel::Params>(); | 
|  | params->policy_certs_provider = &policy_certs_provider_; | 
|  | params->extension_certificate_provider = | 
|  | std::make_unique<FakeExtensionCertificateProvider>( | 
|  | &extension_client_certs_, &extensions_hang_); | 
|  | return params; | 
|  | } | 
|  |  | 
|  | void NotifyPolicyObserversAndWaitForRefresh() { | 
|  | base::RunLoop run_loop; | 
|  | fake_observer_->RunOnNextRefresh(run_loop.QuitClosure()); | 
|  | policy_certs_provider_.NotifyObservers(); | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | // Provider for policy certificates. In a non-test environment, this would | 
|  | // usually be the UserNetworkConfigurationUpdater. | 
|  | FakePolicyCertificateProvider policy_certs_provider_; | 
|  |  | 
|  | // List of certificates that will be returned from the | 
|  | // FakeExtensionCertificateProvider. | 
|  | net::CertificateList extension_client_certs_; | 
|  | // If true, the FakeExtensionCertificateProvider hangs. | 
|  | bool extensions_hang_ = false; | 
|  | }; | 
|  |  | 
|  | // CertificateManagerModel correctly lists policy-provided certificates with web | 
|  | // trust. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, ListsWebTrustedCertsFromPolicy) { | 
|  | scoped_refptr<net::X509Certificate> cert = net::ImportCertFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem"); | 
|  | ASSERT_TRUE(cert.get()); | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({cert}, {}); | 
|  |  | 
|  | NotifyPolicyObserversAndWaitForRefresh(); | 
|  |  | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get()); | 
|  | ASSERT_TRUE(cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", cert_info->name()); | 
|  | EXPECT_FALSE(cert_info->can_be_deleted()); | 
|  | EXPECT_FALSE(cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy, | 
|  | cert_info->source()); | 
|  | EXPECT_TRUE(cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | // CertificateManagerModel correctly lists policy-provided certificates without | 
|  | // web trust. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, ListsNotWebTrustedCertsFromPolicy) { | 
|  | scoped_refptr<net::X509Certificate> cert = net::ImportCertFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem"); | 
|  | ASSERT_TRUE(cert.get()); | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({}, {cert}); | 
|  |  | 
|  | NotifyPolicyObserversAndWaitForRefresh(); | 
|  |  | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get()); | 
|  | ASSERT_TRUE(cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", cert_info->name()); | 
|  | EXPECT_FALSE(cert_info->can_be_deleted()); | 
|  | EXPECT_FALSE(cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy, | 
|  | cert_info->source()); | 
|  | EXPECT_FALSE(cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | // CertificateManagerModel correctly lists CA certificates that are in the | 
|  | // platform NSS database and provided by policy with web trust. The | 
|  | // policy-provided certificate hides the platform certificate in this case. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, | 
|  | WebTrustedPolicyCertsWinOverPlatformCerts) { | 
|  | net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem", | 
|  | net::X509Certificate::FORMAT_AUTO); | 
|  | ASSERT_EQ(1U, certs.size()); | 
|  | CERTCertificate* platform_cert = certs[0].get(); | 
|  | ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert, | 
|  | CK_INVALID_HANDLE, "cert", | 
|  | PR_FALSE /* includeTrust (unused) */)); | 
|  |  | 
|  | scoped_refptr<net::X509Certificate> policy_cert = net::ImportCertFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem"); | 
|  | ASSERT_TRUE(policy_cert.get()); | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({policy_cert}, {}); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  | CertificateManagerModel::CertInfo* policy_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get()); | 
|  | ASSERT_TRUE(policy_cert_info); | 
|  |  | 
|  | EXPECT_EQ(platform_cert_info, policy_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, policy_cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", policy_cert_info->name()); | 
|  | EXPECT_FALSE(policy_cert_info->can_be_deleted()); | 
|  | EXPECT_FALSE(policy_cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy, | 
|  | policy_cert_info->source()); | 
|  | EXPECT_TRUE(policy_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(policy_cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | // Remove the cert from policy-provided certs again. The platform certificate | 
|  | // should be visible afterwards. | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({}, {}); | 
|  | NotifyPolicyObserversAndWaitForRefresh(); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, platform_cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", platform_cert_info->name()); | 
|  | EXPECT_TRUE(platform_cert_info->can_be_deleted()); | 
|  | EXPECT_TRUE(platform_cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform, | 
|  | platform_cert_info->source()); | 
|  | EXPECT_FALSE(platform_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(platform_cert_info->hardware_backed()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // CertificateManagerModel correctly lists CA certificates that are in the | 
|  | // platform NSS database and provided by policy without web trust. The platform | 
|  | // certificate hides the policy-provided certificate in this case. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, | 
|  | PlatformCertsWinOverNotWebTrustedCerts) { | 
|  | net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem", | 
|  | net::X509Certificate::FORMAT_AUTO); | 
|  | ASSERT_EQ(1U, certs.size()); | 
|  | CERTCertificate* platform_cert = certs[0].get(); | 
|  | ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert, | 
|  | CK_INVALID_HANDLE, "cert", | 
|  | PR_FALSE /* includeTrust (unused) */)); | 
|  |  | 
|  | scoped_refptr<net::X509Certificate> policy_cert = net::ImportCertFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem"); | 
|  | ASSERT_TRUE(policy_cert.get()); | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({}, {policy_cert}); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  | CertificateManagerModel::CertInfo* policy_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get()); | 
|  | ASSERT_TRUE(policy_cert_info); | 
|  |  | 
|  | EXPECT_EQ(platform_cert_info, policy_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, platform_cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", platform_cert_info->name()); | 
|  | EXPECT_TRUE(platform_cert_info->can_be_deleted()); | 
|  | EXPECT_TRUE(platform_cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform, | 
|  | platform_cert_info->source()); | 
|  | EXPECT_FALSE(platform_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(platform_cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | // Remove the certificate from the platform NSS database. The policy-provided | 
|  | // certificate should be visible afterwards. | 
|  | base::RunLoop run_loop; | 
|  | fake_observer_->RunOnNextRefresh(run_loop.QuitClosure()); | 
|  | base::test::TestFuture<bool> remove_result; | 
|  | certificate_manager_model_->RemoveFromDatabase( | 
|  | net::x509_util::DupCERTCertificate(platform_cert), | 
|  | remove_result.GetCallback()); | 
|  | EXPECT_TRUE(remove_result.Get()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* policy_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get()); | 
|  | ASSERT_TRUE(policy_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::CA_CERT, policy_cert_info->type()); | 
|  | EXPECT_EQ(u"pywebsocket", policy_cert_info->name()); | 
|  | EXPECT_FALSE(policy_cert_info->can_be_deleted()); | 
|  | EXPECT_FALSE(policy_cert_info->untrusted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy, | 
|  | policy_cert_info->source()); | 
|  | EXPECT_FALSE(policy_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(policy_cert_info->hardware_backed()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // When the Extension CertificateProvider hangs (e.g. because an extension is | 
|  | // not responding), policy and platform certificates are still listed. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, | 
|  | PlatformAndPolicyCertsListedWhenExtensionsHang) { | 
|  | extensions_hang_ = true; | 
|  |  | 
|  | net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile( | 
|  | net::GetTestCertsDirectory(), "websocket_cacert.pem", | 
|  | net::X509Certificate::FORMAT_AUTO); | 
|  | ASSERT_EQ(1U, certs.size()); | 
|  | CERTCertificate* platform_cert = certs[0].get(); | 
|  | ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert, | 
|  | CK_INVALID_HANDLE, "cert", | 
|  | PR_FALSE /* includeTrust (unused) */)); | 
|  |  | 
|  | scoped_refptr<net::X509Certificate> policy_cert = | 
|  | net::ImportCertFromFile(net::GetTestCertsDirectory(), "root_ca_cert.pem"); | 
|  | ASSERT_TRUE(policy_cert.get()); | 
|  | policy_certs_provider_.SetPolicyProvidedCertificates({policy_cert}, {}); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::CA_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  | CertificateManagerModel::CertInfo* policy_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get()); | 
|  | ASSERT_TRUE(policy_cert_info); | 
|  |  | 
|  | EXPECT_NE(platform_cert_info, policy_cert_info); | 
|  | } | 
|  |  | 
|  | // CertificateManagerModel lists client certificates provided by extensions. | 
|  | TEST_F(CertificateManagerModelChromeOSTest, ListsExtensionCerts) { | 
|  | scoped_refptr<net::X509Certificate> extension_cert = | 
|  | net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem"); | 
|  | ASSERT_TRUE(extension_cert.get()); | 
|  | extension_client_certs_.push_back(extension_cert); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::USER_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* extension_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get()); | 
|  | ASSERT_TRUE(extension_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::USER_CERT, extension_cert_info->type()); | 
|  | EXPECT_EQ(u"Client Cert A (extension provided)", extension_cert_info->name()); | 
|  | EXPECT_FALSE(extension_cert_info->can_be_deleted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kExtension, | 
|  | extension_cert_info->source()); | 
|  | EXPECT_FALSE(extension_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(extension_cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | TEST_F(CertificateManagerModelChromeOSTest, | 
|  | PlatformCertsWinOverExtensionCerts) { | 
|  | net::ScopedCERTCertificate platform_client_cert; | 
|  | net::ImportClientCertAndKeyFromFile( | 
|  | net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8", | 
|  | test_nssdb_.slot(), &platform_client_cert); | 
|  |  | 
|  | scoped_refptr<net::X509Certificate> extension_cert = | 
|  | net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem"); | 
|  | ASSERT_TRUE(extension_cert.get()); | 
|  | extension_client_certs_.push_back(extension_cert); | 
|  |  | 
|  | WaitForRefresh(true /*tigger_for_refresh*/); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::USER_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* platform_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, | 
|  | platform_client_cert.get()); | 
|  | ASSERT_TRUE(platform_cert_info); | 
|  | CertificateManagerModel::CertInfo* extension_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get()); | 
|  | ASSERT_TRUE(extension_cert_info); | 
|  |  | 
|  | EXPECT_EQ(platform_cert_info, extension_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::USER_CERT, platform_cert_info->type()); | 
|  | EXPECT_EQ(u"Client Cert A", platform_cert_info->name()); | 
|  | EXPECT_TRUE(platform_cert_info->can_be_deleted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform, | 
|  | platform_cert_info->source()); | 
|  | EXPECT_FALSE(platform_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(platform_cert_info->hardware_backed()); | 
|  | } | 
|  |  | 
|  | // Remove the platform client certificate. The extension-provided client | 
|  | // certificate should be visible afterwards. | 
|  | base::RunLoop run_loop; | 
|  | fake_observer_->RunOnNextRefresh(run_loop.QuitClosure()); | 
|  | base::test::TestFuture<bool> remove_result; | 
|  | certificate_manager_model_->RemoveFromDatabase( | 
|  | std::move(platform_client_cert), remove_result.GetCallback()); | 
|  | EXPECT_TRUE(remove_result.Get()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | { | 
|  | CertificateManagerModel::OrgGroupingMap org_grouping_map; | 
|  | certificate_manager_model_->FilterAndBuildOrgGroupingMap( | 
|  | net::CertType::USER_CERT, &org_grouping_map); | 
|  | CertificateManagerModel::CertInfo* extension_cert_info = | 
|  | GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get()); | 
|  | ASSERT_TRUE(extension_cert_info); | 
|  |  | 
|  | EXPECT_EQ(net::CertType::USER_CERT, extension_cert_info->type()); | 
|  | EXPECT_EQ(u"Client Cert A (extension provided)", | 
|  | extension_cert_info->name()); | 
|  | EXPECT_FALSE(extension_cert_info->can_be_deleted()); | 
|  | EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kExtension, | 
|  | extension_cert_info->source()); | 
|  | EXPECT_FALSE(extension_cert_info->web_trust_anchor()); | 
|  | EXPECT_FALSE(extension_cert_info->hardware_backed()); | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif  // BUILDFLAG(IS_CHROMEOS) |