| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "net/cert/nss_cert_database_chromeos.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "base/message_loop/message_loop_proxy.h" | |
| 10 #include "base/run_loop.h" | |
| 11 #include "crypto/nss_util_internal.h" | |
| 12 #include "crypto/scoped_test_nss_chromeos_user.h" | |
| 13 #include "crypto/scoped_test_nss_db.h" | |
| 14 #include "net/base/test_data_directory.h" | |
| 15 #include "net/cert/cert_database.h" | |
| 16 #include "net/test/cert_test_util.h" | |
| 17 #include "testing/gtest/include/gtest/gtest.h" | |
| 18 | |
| 19 namespace net { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 bool IsCertInCertificateList(const X509Certificate* cert, | |
| 24 const CertificateList& cert_list) { | |
| 25 for (CertificateList::const_iterator it = cert_list.begin(); | |
| 26 it != cert_list.end(); | |
| 27 ++it) { | |
| 28 if (X509Certificate::IsSameOSCert((*it)->os_cert_handle(), | |
| 29 cert->os_cert_handle())) | |
| 30 return true; | |
| 31 } | |
| 32 return false; | |
| 33 } | |
| 34 | |
| 35 void SwapCertLists(CertificateList* destination, | |
| 36 scoped_ptr<CertificateList> source) { | |
| 37 ASSERT_TRUE(destination); | |
| 38 ASSERT_TRUE(source); | |
| 39 | |
| 40 destination->swap(*source); | |
| 41 } | |
| 42 | |
| 43 } // namespace | |
| 44 | |
| 45 class NSSCertDatabaseChromeOSTest : public testing::Test, | |
| 46 public CertDatabase::Observer { | |
| 47 public: | |
| 48 NSSCertDatabaseChromeOSTest() | |
| 49 : observer_added_(false), user_1_("user1"), user_2_("user2") {} | |
| 50 | |
| 51 void SetUp() override { | |
| 52 // Initialize nss_util slots. | |
| 53 ASSERT_TRUE(user_1_.constructed_successfully()); | |
| 54 ASSERT_TRUE(user_2_.constructed_successfully()); | |
| 55 user_1_.FinishInit(); | |
| 56 user_2_.FinishInit(); | |
| 57 | |
| 58 // Create NSSCertDatabaseChromeOS for each user. | |
| 59 db_1_.reset(new NSSCertDatabaseChromeOS( | |
| 60 crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()), | |
| 61 crypto::GetPrivateSlotForChromeOSUser( | |
| 62 user_1_.username_hash(), | |
| 63 base::Callback<void(crypto::ScopedPK11Slot)>()))); | |
| 64 db_1_->SetSlowTaskRunnerForTest(base::MessageLoopProxy::current()); | |
| 65 db_1_->SetSystemSlot( | |
| 66 crypto::ScopedPK11Slot(PK11_ReferenceSlot(system_db_.slot()))); | |
| 67 db_2_.reset(new NSSCertDatabaseChromeOS( | |
| 68 crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()), | |
| 69 crypto::GetPrivateSlotForChromeOSUser( | |
| 70 user_2_.username_hash(), | |
| 71 base::Callback<void(crypto::ScopedPK11Slot)>()))); | |
| 72 db_2_->SetSlowTaskRunnerForTest(base::MessageLoopProxy::current()); | |
| 73 | |
| 74 // Add observer to CertDatabase for checking that notifications from | |
| 75 // NSSCertDatabaseChromeOS are proxied to the CertDatabase. | |
| 76 CertDatabase::GetInstance()->AddObserver(this); | |
| 77 observer_added_ = true; | |
| 78 } | |
| 79 | |
| 80 void TearDown() override { | |
| 81 if (observer_added_) | |
| 82 CertDatabase::GetInstance()->RemoveObserver(this); | |
| 83 } | |
| 84 | |
| 85 // CertDatabase::Observer: | |
| 86 void OnCertAdded(const X509Certificate* cert) override { | |
| 87 added_.push_back(cert ? cert->os_cert_handle() : NULL); | |
| 88 } | |
| 89 | |
| 90 void OnCertRemoved(const X509Certificate* cert) override {} | |
| 91 | |
| 92 void OnCACertChanged(const X509Certificate* cert) override { | |
| 93 added_ca_.push_back(cert ? cert->os_cert_handle() : NULL); | |
| 94 } | |
| 95 | |
| 96 protected: | |
| 97 bool observer_added_; | |
| 98 // Certificates that were passed to the CertDatabase observers. | |
| 99 std::vector<CERTCertificate*> added_ca_; | |
| 100 std::vector<CERTCertificate*> added_; | |
| 101 | |
| 102 crypto::ScopedTestNSSChromeOSUser user_1_; | |
| 103 crypto::ScopedTestNSSChromeOSUser user_2_; | |
| 104 crypto::ScopedTestNSSDB system_db_; | |
| 105 scoped_ptr<NSSCertDatabaseChromeOS> db_1_; | |
| 106 scoped_ptr<NSSCertDatabaseChromeOS> db_2_; | |
| 107 }; | |
| 108 | |
| 109 // Test that ListModules() on each user includes that user's NSS software slot, | |
| 110 // and does not include the software slot of the other user. (Does not check the | |
| 111 // private slot, since it is the same as the public slot in tests.) | |
| 112 TEST_F(NSSCertDatabaseChromeOSTest, ListModules) { | |
| 113 CryptoModuleList modules_1; | |
| 114 CryptoModuleList modules_2; | |
| 115 | |
| 116 db_1_->ListModules(&modules_1, false /* need_rw */); | |
| 117 db_2_->ListModules(&modules_2, false /* need_rw */); | |
| 118 | |
| 119 bool found_1 = false; | |
| 120 for (CryptoModuleList::iterator it = modules_1.begin(); it != modules_1.end(); | |
| 121 ++it) { | |
| 122 EXPECT_NE(db_2_->GetPublicSlot().get(), (*it)->os_module_handle()); | |
| 123 if ((*it)->os_module_handle() == db_1_->GetPublicSlot().get()) | |
| 124 found_1 = true; | |
| 125 } | |
| 126 EXPECT_TRUE(found_1); | |
| 127 | |
| 128 bool found_2 = false; | |
| 129 for (CryptoModuleList::iterator it = modules_2.begin(); it != modules_2.end(); | |
| 130 ++it) { | |
| 131 EXPECT_NE(db_1_->GetPublicSlot().get(), (*it)->os_module_handle()); | |
| 132 if ((*it)->os_module_handle() == db_2_->GetPublicSlot().get()) | |
| 133 found_2 = true; | |
| 134 } | |
| 135 EXPECT_TRUE(found_2); | |
| 136 } | |
| 137 | |
| 138 // Test that ImportCACerts imports the cert to the correct slot, and that | |
| 139 // ListCerts includes the added cert for the correct user, and does not include | |
| 140 // it for the other user. | |
| 141 TEST_F(NSSCertDatabaseChromeOSTest, ImportCACerts) { | |
| 142 // Load test certs from disk. | |
| 143 CertificateList certs_1 = | |
| 144 CreateCertificateListFromFile(GetTestCertsDirectory(), | |
| 145 "root_ca_cert.pem", | |
| 146 X509Certificate::FORMAT_AUTO); | |
| 147 ASSERT_EQ(1U, certs_1.size()); | |
| 148 | |
| 149 CertificateList certs_2 = | |
| 150 CreateCertificateListFromFile(GetTestCertsDirectory(), | |
| 151 "2048-rsa-root.pem", | |
| 152 X509Certificate::FORMAT_AUTO); | |
| 153 ASSERT_EQ(1U, certs_2.size()); | |
| 154 | |
| 155 // Import one cert for each user. | |
| 156 NSSCertDatabase::ImportCertFailureList failed; | |
| 157 EXPECT_TRUE( | |
| 158 db_1_->ImportCACerts(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed)); | |
| 159 EXPECT_EQ(0U, failed.size()); | |
| 160 failed.clear(); | |
| 161 EXPECT_TRUE( | |
| 162 db_2_->ImportCACerts(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed)); | |
| 163 EXPECT_EQ(0U, failed.size()); | |
| 164 | |
| 165 // Get cert list for each user. | |
| 166 CertificateList user_1_certlist; | |
| 167 CertificateList user_2_certlist; | |
| 168 db_1_->ListCertsSync(&user_1_certlist); | |
| 169 db_2_->ListCertsSync(&user_2_certlist); | |
| 170 | |
| 171 // Check that the imported certs only shows up in the list for the user that | |
| 172 // imported them. | |
| 173 EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist)); | |
| 174 EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist)); | |
| 175 | |
| 176 EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist)); | |
| 177 EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist)); | |
| 178 | |
| 179 // Run the message loop so the observer notifications get processed. | |
| 180 base::RunLoop().RunUntilIdle(); | |
| 181 // Should have gotten two OnCACertChanged notifications. | |
| 182 ASSERT_EQ(2U, added_ca_.size()); | |
| 183 // TODO(mattm): make NSSCertDatabase actually pass the cert to the callback, | |
| 184 // and enable these checks: | |
| 185 // EXPECT_EQ(certs_1[0]->os_cert_handle(), added_ca_[0]); | |
| 186 // EXPECT_EQ(certs_2[0]->os_cert_handle(), added_ca_[1]); | |
| 187 EXPECT_EQ(0U, added_.size()); | |
| 188 | |
| 189 // Tests that the new certs are loaded by async ListCerts method. | |
| 190 CertificateList user_1_certlist_async; | |
| 191 CertificateList user_2_certlist_async; | |
| 192 db_1_->ListCerts( | |
| 193 base::Bind(&SwapCertLists, base::Unretained(&user_1_certlist_async))); | |
| 194 db_2_->ListCerts( | |
| 195 base::Bind(&SwapCertLists, base::Unretained(&user_2_certlist_async))); | |
| 196 | |
| 197 base::RunLoop().RunUntilIdle(); | |
| 198 | |
| 199 EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist_async)); | |
| 200 EXPECT_FALSE( | |
| 201 IsCertInCertificateList(certs_1[0].get(), user_2_certlist_async)); | |
| 202 | |
| 203 EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist_async)); | |
| 204 EXPECT_FALSE( | |
| 205 IsCertInCertificateList(certs_2[0].get(), user_1_certlist_async)); | |
| 206 } | |
| 207 | |
| 208 // Test that ImportServerCerts imports the cert to the correct slot, and that | |
| 209 // ListCerts includes the added cert for the correct user, and does not include | |
| 210 // it for the other user. | |
| 211 TEST_F(NSSCertDatabaseChromeOSTest, ImportServerCert) { | |
| 212 // Load test certs from disk. | |
| 213 CertificateList certs_1 = CreateCertificateListFromFile( | |
| 214 GetTestCertsDirectory(), "ok_cert.pem", X509Certificate::FORMAT_AUTO); | |
| 215 ASSERT_EQ(1U, certs_1.size()); | |
| 216 | |
| 217 CertificateList certs_2 = | |
| 218 CreateCertificateListFromFile(GetTestCertsDirectory(), | |
| 219 "2048-rsa-ee-by-2048-rsa-intermediate.pem", | |
| 220 X509Certificate::FORMAT_AUTO); | |
| 221 ASSERT_EQ(1U, certs_2.size()); | |
| 222 | |
| 223 // Import one cert for each user. | |
| 224 NSSCertDatabase::ImportCertFailureList failed; | |
| 225 EXPECT_TRUE( | |
| 226 db_1_->ImportServerCert(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed)); | |
| 227 EXPECT_EQ(0U, failed.size()); | |
| 228 failed.clear(); | |
| 229 EXPECT_TRUE( | |
| 230 db_2_->ImportServerCert(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed)); | |
| 231 EXPECT_EQ(0U, failed.size()); | |
| 232 | |
| 233 // Get cert list for each user. | |
| 234 CertificateList user_1_certlist; | |
| 235 CertificateList user_2_certlist; | |
| 236 db_1_->ListCertsSync(&user_1_certlist); | |
| 237 db_2_->ListCertsSync(&user_2_certlist); | |
| 238 | |
| 239 // Check that the imported certs only shows up in the list for the user that | |
| 240 // imported them. | |
| 241 EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist)); | |
| 242 EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist)); | |
| 243 | |
| 244 EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist)); | |
| 245 EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist)); | |
| 246 | |
| 247 // Run the message loop so the observer notifications get processed. | |
| 248 base::RunLoop().RunUntilIdle(); | |
| 249 // TODO(mattm): ImportServerCert doesn't actually cause any observers to | |
| 250 // fire. Is that correct? | |
| 251 EXPECT_EQ(0U, added_ca_.size()); | |
| 252 EXPECT_EQ(0U, added_.size()); | |
| 253 | |
| 254 // Tests that the new certs are loaded by async ListCerts method. | |
| 255 CertificateList user_1_certlist_async; | |
| 256 CertificateList user_2_certlist_async; | |
| 257 db_1_->ListCerts( | |
| 258 base::Bind(&SwapCertLists, base::Unretained(&user_1_certlist_async))); | |
| 259 db_2_->ListCerts( | |
| 260 base::Bind(&SwapCertLists, base::Unretained(&user_2_certlist_async))); | |
| 261 | |
| 262 base::RunLoop().RunUntilIdle(); | |
| 263 | |
| 264 EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist_async)); | |
| 265 EXPECT_FALSE( | |
| 266 IsCertInCertificateList(certs_1[0].get(), user_2_certlist_async)); | |
| 267 | |
| 268 EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist_async)); | |
| 269 EXPECT_FALSE( | |
| 270 IsCertInCertificateList(certs_2[0].get(), user_1_certlist_async)); | |
| 271 } | |
| 272 | |
| 273 // Tests that There is no crash if the database is deleted while ListCerts | |
| 274 // is being processed on the worker pool. | |
| 275 TEST_F(NSSCertDatabaseChromeOSTest, NoCrashIfShutdownBeforeDoneOnWorkerPool) { | |
| 276 CertificateList certlist; | |
| 277 db_1_->ListCerts(base::Bind(&SwapCertLists, base::Unretained(&certlist))); | |
| 278 EXPECT_EQ(0U, certlist.size()); | |
| 279 | |
| 280 db_1_.reset(); | |
| 281 | |
| 282 base::RunLoop().RunUntilIdle(); | |
| 283 | |
| 284 EXPECT_LT(0U, certlist.size()); | |
| 285 } | |
| 286 | |
| 287 TEST_F(NSSCertDatabaseChromeOSTest, ListCertsReadsSystemSlot) { | |
| 288 scoped_refptr<X509Certificate> cert_1( | |
| 289 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(), | |
| 290 "client_1.pem", | |
| 291 "client_1.pk8", | |
| 292 db_1_->GetPublicSlot().get())); | |
| 293 | |
| 294 scoped_refptr<X509Certificate> cert_2( | |
| 295 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(), | |
| 296 "client_2.pem", | |
| 297 "client_2.pk8", | |
| 298 db_1_->GetSystemSlot().get())); | |
| 299 CertificateList certs; | |
| 300 db_1_->ListCertsSync(&certs); | |
| 301 EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs)); | |
| 302 EXPECT_TRUE(IsCertInCertificateList(cert_2.get(), certs)); | |
| 303 } | |
| 304 | |
| 305 TEST_F(NSSCertDatabaseChromeOSTest, ListCertsDoesNotCrossReadSystemSlot) { | |
| 306 scoped_refptr<X509Certificate> cert_1( | |
| 307 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(), | |
| 308 "client_1.pem", | |
| 309 "client_1.pk8", | |
| 310 db_2_->GetPublicSlot().get())); | |
| 311 | |
| 312 scoped_refptr<X509Certificate> cert_2( | |
| 313 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(), | |
| 314 "client_2.pem", | |
| 315 "client_2.pk8", | |
| 316 system_db_.slot())); | |
| 317 CertificateList certs; | |
| 318 db_2_->ListCertsSync(&certs); | |
| 319 EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs)); | |
| 320 EXPECT_FALSE(IsCertInCertificateList(cert_2.get(), certs)); | |
| 321 } | |
| 322 | |
| 323 } // namespace net | |
| OLD | NEW |