[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chromeos/cert_loader.h" |
| 6 | |
avi | 6e1a22d | 2015-12-21 03:43:20 | [diff] [blame] | 7 | #include <stddef.h> |
dcheng | 0a6e80c | 2016-04-08 18:37:38 | [diff] [blame] | 8 | |
| 9 | #include <memory> |
dcheng | 7df85ba | 2015-12-31 04:56:39 | [diff] [blame] | 10 | #include <utility> |
avi | 6e1a22d | 2015-12-21 03:43:20 | [diff] [blame] | 11 | |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 12 | #include "base/bind.h" |
thestig | b44bd35 | 2014-09-10 01:47:06 | [diff] [blame] | 13 | #include "base/files/file_util.h" |
fdoray | 37b8327d | 2017-02-28 02:42:14 | [diff] [blame] | 14 | #include "base/message_loop/message_loop.h" |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 15 | #include "base/run_loop.h" |
[email protected] | 16dad096 | 2014-03-18 01:29:11 | [diff] [blame] | 16 | #include "crypto/scoped_nss_types.h" |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 17 | #include "crypto/scoped_test_nss_db.h" |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 18 | #include "net/cert/nss_cert_database_chromeos.h" |
| 19 | #include "net/cert/x509_certificate.h" |
| 20 | #include "net/test/cert_test_util.h" |
rsleevi | a69c79a | 2016-06-22 03:28:43 | [diff] [blame] | 21 | #include "net/test/test_data_directory.h" |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 22 | #include "testing/gtest/include/gtest/gtest.h" |
| 23 | |
| 24 | namespace chromeos { |
| 25 | namespace { |
| 26 | |
| 27 | bool IsCertInCertificateList(const net::X509Certificate* cert, |
| 28 | const net::CertificateList& cert_list) { |
| 29 | for (net::CertificateList::const_iterator it = cert_list.begin(); |
| 30 | it != cert_list.end(); |
| 31 | ++it) { |
| 32 | if (net::X509Certificate::IsSameOSCert((*it)->os_cert_handle(), |
| 33 | cert->os_cert_handle())) { |
| 34 | return true; |
| 35 | } |
| 36 | } |
| 37 | return false; |
| 38 | } |
| 39 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 40 | class TestNSSCertDatabase : public net::NSSCertDatabaseChromeOS { |
| 41 | public: |
| 42 | TestNSSCertDatabase(crypto::ScopedPK11Slot public_slot, |
| 43 | crypto::ScopedPK11Slot private_slot) |
dcheng | 7df85ba | 2015-12-31 04:56:39 | [diff] [blame] | 44 | : NSSCertDatabaseChromeOS(std::move(public_slot), |
| 45 | std::move(private_slot)) {} |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 46 | ~TestNSSCertDatabase() override {} |
| 47 | |
| 48 | void NotifyOfCertAdded(const net::X509Certificate* cert) { |
mattm | fd05a1f | 2017-02-18 06:18:44 | [diff] [blame] | 49 | NSSCertDatabaseChromeOS::NotifyObserversCertDBChanged(); |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 50 | } |
| 51 | }; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 52 | |
| 53 | class CertLoaderTest : public testing::Test, |
| 54 | public CertLoader::Observer { |
| 55 | public: |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 56 | CertLoaderTest() |
| 57 | : cert_loader_(nullptr), certificates_loaded_events_count_(0U) {} |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 58 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 59 | ~CertLoaderTest() override {} |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 60 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 61 | void SetUp() override { |
| 62 | ASSERT_TRUE(primary_db_.is_open()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 63 | |
| 64 | CertLoader::Initialize(); |
| 65 | cert_loader_ = CertLoader::Get(); |
| 66 | cert_loader_->AddObserver(this); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 67 | } |
| 68 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 69 | void TearDown() override { |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 70 | cert_loader_->RemoveObserver(this); |
| 71 | CertLoader::Shutdown(); |
| 72 | } |
| 73 | |
| 74 | protected: |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 75 | void StartCertLoaderWithPrimaryDB() { |
| 76 | CreateCertDatabase(&primary_db_, &primary_certdb_); |
| 77 | cert_loader_->StartWithNSSDB(primary_certdb_.get()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 78 | |
| 79 | base::RunLoop().RunUntilIdle(); |
| 80 | GetAndResetCertificatesLoadedEventsCount(); |
| 81 | } |
| 82 | |
| 83 | // CertLoader::Observer: |
| 84 | // The test keeps count of times the observer method was called. |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 85 | void OnCertificatesLoaded(const net::CertificateList& cert_list, |
| 86 | bool initial_load) override { |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 87 | EXPECT_TRUE(certificates_loaded_events_count_ == 0 || !initial_load); |
| 88 | certificates_loaded_events_count_++; |
| 89 | } |
| 90 | |
| 91 | // Returns the number of |OnCertificatesLoaded| calls observed since the |
| 92 | // last call to this method equals |value|. |
| 93 | size_t GetAndResetCertificatesLoadedEventsCount() { |
| 94 | size_t result = certificates_loaded_events_count_; |
| 95 | certificates_loaded_events_count_ = 0; |
| 96 | return result; |
| 97 | } |
| 98 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 99 | void CreateCertDatabase(crypto::ScopedTestNSSDB* db, |
dcheng | 0a6e80c | 2016-04-08 18:37:38 | [diff] [blame] | 100 | std::unique_ptr<TestNSSCertDatabase>* certdb) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 101 | ASSERT_TRUE(db->is_open()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 102 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 103 | certdb->reset(new TestNSSCertDatabase( |
| 104 | crypto::ScopedPK11Slot(PK11_ReferenceSlot(db->slot())), |
| 105 | crypto::ScopedPK11Slot(PK11_ReferenceSlot(db->slot())))); |
fdoray | 37b8327d | 2017-02-28 02:42:14 | [diff] [blame] | 106 | (*certdb)->SetSlowTaskRunnerForTest(message_loop_.task_runner()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | void ImportCACert(const std::string& cert_file, |
| 110 | net::NSSCertDatabase* database, |
| 111 | net::CertificateList* imported_certs) { |
| 112 | ASSERT_TRUE(database); |
| 113 | ASSERT_TRUE(imported_certs); |
| 114 | |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 115 | *imported_certs = net::CreateCertificateListFromFile( |
| 116 | net::GetTestCertsDirectory(), |
| 117 | cert_file, |
| 118 | net::X509Certificate::FORMAT_AUTO); |
| 119 | ASSERT_EQ(1U, imported_certs->size()); |
| 120 | |
| 121 | net::NSSCertDatabase::ImportCertFailureList failed; |
| 122 | ASSERT_TRUE(database->ImportCACerts(*imported_certs, |
| 123 | net::NSSCertDatabase::TRUST_DEFAULT, |
| 124 | &failed)); |
| 125 | ASSERT_TRUE(failed.empty()); |
| 126 | } |
| 127 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 128 | scoped_refptr<net::X509Certificate> ImportClientCertAndKey( |
| 129 | TestNSSCertDatabase* database) { |
| 130 | // Import a client cert signed by that CA. |
| 131 | scoped_refptr<net::X509Certificate> client_cert( |
| 132 | net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(), |
| 133 | "client_1.pem", "client_1.pk8", |
| 134 | database->GetPrivateSlot().get())); |
| 135 | database->NotifyOfCertAdded(client_cert.get()); |
| 136 | return client_cert; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | CertLoader* cert_loader_; |
| 140 | |
| 141 | // The user is primary as the one whose certificates CertLoader handles, it |
| 142 | // has nothing to do with crypto::InitializeNSSForChromeOSUser is_primary_user |
| 143 | // parameter (which is irrelevant for these tests). |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 144 | crypto::ScopedTestNSSDB primary_db_; |
dcheng | 0a6e80c | 2016-04-08 18:37:38 | [diff] [blame] | 145 | std::unique_ptr<TestNSSCertDatabase> primary_certdb_; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 146 | |
fdoray | 37b8327d | 2017-02-28 02:42:14 | [diff] [blame] | 147 | base::MessageLoop message_loop_; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 148 | |
| 149 | private: |
| 150 | size_t certificates_loaded_events_count_; |
| 151 | }; |
| 152 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 153 | } // namespace |
| 154 | |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 155 | TEST_F(CertLoaderTest, Basic) { |
| 156 | EXPECT_FALSE(cert_loader_->CertificatesLoading()); |
| 157 | EXPECT_FALSE(cert_loader_->certificates_loaded()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 158 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 159 | CreateCertDatabase(&primary_db_, &primary_certdb_); |
| 160 | cert_loader_->StartWithNSSDB(primary_certdb_.get()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 161 | |
| 162 | EXPECT_FALSE(cert_loader_->certificates_loaded()); |
| 163 | EXPECT_TRUE(cert_loader_->CertificatesLoading()); |
| 164 | EXPECT_TRUE(cert_loader_->cert_list().empty()); |
| 165 | |
| 166 | ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount()); |
| 167 | base::RunLoop().RunUntilIdle(); |
| 168 | EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
| 169 | |
| 170 | EXPECT_TRUE(cert_loader_->certificates_loaded()); |
| 171 | EXPECT_FALSE(cert_loader_->CertificatesLoading()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 172 | |
| 173 | // Default CA cert roots should get loaded. |
| 174 | EXPECT_FALSE(cert_loader_->cert_list().empty()); |
| 175 | } |
| 176 | |
| 177 | TEST_F(CertLoaderTest, CertLoaderUpdatesCertListOnNewCert) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 178 | StartCertLoaderWithPrimaryDB(); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 179 | |
| 180 | net::CertificateList certs; |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 181 | ImportCACert("root_ca_cert.pem", primary_certdb_.get(), &certs); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 182 | |
| 183 | // Certs are loaded asynchronously, so the new cert should not yet be in the |
| 184 | // cert list. |
dcheng | 5adf8d2 | 2014-09-11 00:53:37 | [diff] [blame] | 185 | EXPECT_FALSE( |
| 186 | IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 187 | |
| 188 | ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount()); |
| 189 | base::RunLoop().RunUntilIdle(); |
| 190 | EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
| 191 | |
| 192 | // The certificate list should be updated now, as the message loop's been run. |
dcheng | 5adf8d2 | 2014-09-11 00:53:37 | [diff] [blame] | 193 | EXPECT_TRUE( |
| 194 | IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list())); |
pneubeck | ad2f216 | 2014-11-06 10:56:19 | [diff] [blame] | 195 | |
| 196 | EXPECT_FALSE(cert_loader_->IsCertificateHardwareBacked(certs[0].get())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | TEST_F(CertLoaderTest, CertLoaderNoUpdateOnSecondaryDbChanges) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 200 | crypto::ScopedTestNSSDB secondary_db; |
dcheng | 0a6e80c | 2016-04-08 18:37:38 | [diff] [blame] | 201 | std::unique_ptr<TestNSSCertDatabase> secondary_certdb; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 202 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 203 | StartCertLoaderWithPrimaryDB(); |
| 204 | CreateCertDatabase(&secondary_db, &secondary_certdb); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 205 | |
| 206 | net::CertificateList certs; |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 207 | ImportCACert("root_ca_cert.pem", secondary_certdb.get(), &certs); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 208 | |
| 209 | base::RunLoop().RunUntilIdle(); |
| 210 | |
dcheng | 5adf8d2 | 2014-09-11 00:53:37 | [diff] [blame] | 211 | EXPECT_FALSE( |
| 212 | IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 213 | } |
| 214 | |
| 215 | TEST_F(CertLoaderTest, ClientLoaderUpdateOnNewClientCert) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 216 | StartCertLoaderWithPrimaryDB(); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 217 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 218 | scoped_refptr<net::X509Certificate> cert( |
| 219 | ImportClientCertAndKey(primary_certdb_.get())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 220 | |
| 221 | ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount()); |
| 222 | base::RunLoop().RunUntilIdle(); |
| 223 | EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
| 224 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 225 | EXPECT_TRUE(IsCertInCertificateList(cert.get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 226 | } |
| 227 | |
| 228 | TEST_F(CertLoaderTest, CertLoaderNoUpdateOnNewClientCertInSecondaryDb) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 229 | crypto::ScopedTestNSSDB secondary_db; |
dcheng | 0a6e80c | 2016-04-08 18:37:38 | [diff] [blame] | 230 | std::unique_ptr<TestNSSCertDatabase> secondary_certdb; |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 231 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 232 | StartCertLoaderWithPrimaryDB(); |
| 233 | CreateCertDatabase(&secondary_db, &secondary_certdb); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 234 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 235 | scoped_refptr<net::X509Certificate> cert( |
| 236 | ImportClientCertAndKey(secondary_certdb.get())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 237 | |
| 238 | base::RunLoop().RunUntilIdle(); |
| 239 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 240 | EXPECT_FALSE(IsCertInCertificateList(cert.get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 241 | } |
| 242 | |
| 243 | TEST_F(CertLoaderTest, UpdatedOnCertRemoval) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 244 | StartCertLoaderWithPrimaryDB(); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 245 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 246 | scoped_refptr<net::X509Certificate> cert( |
| 247 | ImportClientCertAndKey(primary_certdb_.get())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 248 | |
| 249 | base::RunLoop().RunUntilIdle(); |
| 250 | |
| 251 | ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 252 | ASSERT_TRUE(IsCertInCertificateList(cert.get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 253 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 254 | primary_certdb_->DeleteCertAndKey(cert.get()); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 255 | |
| 256 | ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount()); |
| 257 | base::RunLoop().RunUntilIdle(); |
| 258 | EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
| 259 | |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 260 | ASSERT_FALSE(IsCertInCertificateList(cert.get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 261 | } |
| 262 | |
| 263 | TEST_F(CertLoaderTest, UpdatedOnCACertTrustChange) { |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 264 | StartCertLoaderWithPrimaryDB(); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 265 | |
| 266 | net::CertificateList certs; |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 267 | ImportCACert("root_ca_cert.pem", primary_certdb_.get(), &certs); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 268 | |
| 269 | base::RunLoop().RunUntilIdle(); |
| 270 | ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
dcheng | 5adf8d2 | 2014-09-11 00:53:37 | [diff] [blame] | 271 | ASSERT_TRUE( |
| 272 | IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list())); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 273 | |
| 274 | // The value that should have been set by |ImportCACert|. |
| 275 | ASSERT_EQ(net::NSSCertDatabase::TRUST_DEFAULT, |
pneubeck | 6fd7d48 | 2015-01-10 10:07:38 | [diff] [blame] | 276 | primary_certdb_->GetCertTrust(certs[0].get(), net::CA_CERT)); |
| 277 | ASSERT_TRUE(primary_certdb_->SetCertTrust(certs[0].get(), net::CA_CERT, |
| 278 | net::NSSCertDatabase::TRUSTED_SSL)); |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 279 | |
| 280 | // Cert trust change should trigger certificate reload in cert_loader_. |
| 281 | ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount()); |
| 282 | base::RunLoop().RunUntilIdle(); |
| 283 | EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount()); |
| 284 | } |
| 285 | |
[email protected] | 69295ba | 2014-01-28 06:17:00 | [diff] [blame] | 286 | } // namespace chromeos |