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 19namespace net { 20 21namespace { 22 23bool 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 35void 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 45class NSSCertDatabaseChromeOSTest : public testing::Test, 46 public CertDatabase::Observer { 47 public: 48 NSSCertDatabaseChromeOSTest() 49 : observer_added_(false), user_1_("user1"), user_2_("user2") {} 50 51 virtual 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 virtual void TearDown() OVERRIDE { 81 if (observer_added_) 82 CertDatabase::GetInstance()->RemoveObserver(this); 83 } 84 85 // CertDatabase::Observer: 86 virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE { 87 added_.push_back(cert ? cert->os_cert_handle() : NULL); 88 } 89 90 virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {} 91 92 virtual 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.) 112TEST_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. 141TEST_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. 211TEST_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. 275TEST_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 287TEST_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 305TEST_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 324