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/ssl/client_cert_store_chromeos.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/files/file_util.h" 12 #include "base/run_loop.h" 13 #include "crypto/nss_util_internal.h" 14 #include "crypto/rsa_private_key.h" 15 #include "crypto/scoped_test_nss_chromeos_user.h" 16 #include "crypto/scoped_test_system_nss_key_slot.h" 17 #include "net/base/test_data_directory.h" 18 #include "net/cert/x509_certificate.h" 19 #include "net/ssl/client_cert_store_unittest-inl.h" 20 #include "net/test/cert_test_util.h" 21 22 namespace net { 23 24 namespace { 25 26 enum ReadFromSlot { 27 READ_FROM_SLOT_USER, 28 READ_FROM_SLOT_SYSTEM 29 }; 30 31 enum SystemSlotAvailability { 32 SYSTEM_SLOT_AVAILABILITY_ENABLED, 33 SYSTEM_SLOT_AVAILABILITY_DISABLED 34 }; 35 36 } // namespace 37 38 // Define a delegate to be used for instantiating the parameterized test set 39 // ClientCertStoreTest. 40 template <ReadFromSlot read_from, 41 SystemSlotAvailability system_slot_availability> 42 class ClientCertStoreChromeOSTestDelegate { 43 public: 44 ClientCertStoreChromeOSTestDelegate() 45 : user_("scopeduser"), 46 store_(system_slot_availability == SYSTEM_SLOT_AVAILABILITY_ENABLED, 47 user_.username_hash(), 48 ClientCertStoreChromeOS::PasswordDelegateFactory()) { 49 // Defer futher initialization and checks to SelectClientCerts, because the 50 // constructor doesn't allow us to return an initialization result. Could be 51 // cleaned up by adding an Init() function. 52 } 53 54 // Called by the ClientCertStoreTest tests. 55 // |inpurt_certs| contains certificates to select from. Because 56 // ClientCertStoreChromeOS filters also for the right slot, we have to import 57 // the certs at first. 58 // Since the certs are imported, the store can be tested by using its public 59 // interface (GetClientCerts), which will read the certs from NSS. 60 bool SelectClientCerts(const CertificateList& input_certs, 61 const SSLCertRequestInfo& cert_request_info, 62 CertificateList* selected_certs) { 63 if (!user_.constructed_successfully()) { 64 LOG(ERROR) << "Scoped test user DB could not be constructed."; 65 return false; 66 } 67 user_.FinishInit(); 68 69 crypto::ScopedPK11Slot slot; 70 switch (read_from) { 71 case READ_FROM_SLOT_USER: 72 slot = crypto::GetPublicSlotForChromeOSUser(user_.username_hash()); 73 break; 74 case READ_FROM_SLOT_SYSTEM: 75 slot.reset(PK11_ReferenceSlot(system_db_.slot())); 76 break; 77 default: 78 CHECK(false); 79 } 80 if (!slot) { 81 LOG(ERROR) << "Could not get the NSS key slot"; 82 return false; 83 } 84 85 // Only user certs are considered for the cert request, which means that the 86 // private key must be known to NSS. Import all private keys for certs that 87 // are used througout the test. 88 if (!ImportSensitiveKeyFromFile( 89 GetTestCertsDirectory(), "client_1.pk8", slot.get()) || 90 !ImportSensitiveKeyFromFile( 91 GetTestCertsDirectory(), "client_2.pk8", slot.get())) { 92 return false; 93 } 94 95 for (CertificateList::const_iterator it = input_certs.begin(); 96 it != input_certs.end(); 97 ++it) { 98 if (!ImportClientCertToSlot(*it, slot.get())) 99 return false; 100 } 101 base::RunLoop run_loop; 102 store_.GetClientCerts( 103 cert_request_info, selected_certs, run_loop.QuitClosure()); 104 run_loop.Run(); 105 return true; 106 } 107 108 private: 109 crypto::ScopedTestNSSChromeOSUser user_; 110 crypto::ScopedTestSystemNSSKeySlot system_db_; 111 ClientCertStoreChromeOS store_; 112 }; 113 114 // ClientCertStoreChromeOS derives from ClientCertStoreNSS and delegates the 115 // filtering by issuer to that base class. 116 // To verify that this delegation is functional, run the same filtering tests as 117 // for the other implementations. These tests are defined in 118 // client_cert_store_unittest-inl.h and are instantiated for each platform. 119 120 // In this case, all requested certs are read from the user's slot and the 121 // system slot is not enabled in the store. 122 typedef ClientCertStoreChromeOSTestDelegate<READ_FROM_SLOT_USER, 123 SYSTEM_SLOT_AVAILABILITY_DISABLED> 124 DelegateReadUserDisableSystem; 125 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadUserDisableSystem, 126 ClientCertStoreTest, 127 DelegateReadUserDisableSystem); 128 129 // In this case, all requested certs are read from the user's slot and the 130 // system slot is enabled in the store. 131 typedef ClientCertStoreChromeOSTestDelegate<READ_FROM_SLOT_USER, 132 SYSTEM_SLOT_AVAILABILITY_ENABLED> 133 DelegateReadUserEnableSystem; 134 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadUserEnableSystem, 135 ClientCertStoreTest, 136 DelegateReadUserEnableSystem); 137 138 // In this case, all requested certs are read from the system slot, therefore 139 // the system slot is enabled in the store. 140 typedef ClientCertStoreChromeOSTestDelegate<READ_FROM_SLOT_SYSTEM, 141 SYSTEM_SLOT_AVAILABILITY_ENABLED> 142 DelegateReadSystem; 143 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadSystem, 144 ClientCertStoreTest, 145 DelegateReadSystem); 146 147 class ClientCertStoreChromeOSTest : public ::testing::Test { 148 public: 149 scoped_refptr<X509Certificate> ImportCertForUser( 150 const std::string& username_hash, 151 const std::string& cert_filename, 152 const std::string& key_filename) { 153 crypto::ScopedPK11Slot slot( 154 crypto::GetPublicSlotForChromeOSUser(username_hash)); 155 if (!slot) { 156 LOG(ERROR) << "No slot for user " << username_hash; 157 return NULL; 158 } 159 160 return ImportClientCertAndKeyFromFile( 161 GetTestCertsDirectory(), cert_filename, key_filename, slot.get()); 162 } 163 164 }; 165 166 // Ensure that cert requests, that are started before the user's NSS DB is 167 // initialized, will wait for the initialization and succeed afterwards. 168 TEST_F(ClientCertStoreChromeOSTest, RequestWaitsForNSSInitAndSucceeds) { 169 crypto::ScopedTestNSSChromeOSUser user("scopeduser"); 170 ASSERT_TRUE(user.constructed_successfully()); 171 172 crypto::ScopedTestSystemNSSKeySlot system_slot; 173 174 ClientCertStoreChromeOS store( 175 true /* use system slot */, 176 user.username_hash(), 177 ClientCertStoreChromeOS::PasswordDelegateFactory()); 178 scoped_refptr<X509Certificate> cert_1( 179 ImportCertForUser(user.username_hash(), "client_1.pem", "client_1.pk8")); 180 ASSERT_TRUE(cert_1.get()); 181 182 // Request any client certificate, which is expected to match client_1. 183 scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); 184 185 base::RunLoop run_loop; 186 store.GetClientCerts( 187 *request_all, &request_all->client_certs, run_loop.QuitClosure()); 188 189 { 190 base::RunLoop run_loop_inner; 191 run_loop_inner.RunUntilIdle(); 192 // GetClientCerts should wait for the initialization of the user's DB to 193 // finish. 194 ASSERT_EQ(0u, request_all->client_certs.size()); 195 } 196 // This should trigger the GetClientCerts operation to finish and to call 197 // back. 198 user.FinishInit(); 199 200 run_loop.Run(); 201 202 ASSERT_EQ(1u, request_all->client_certs.size()); 203 } 204 205 // Ensure that cert requests, that are started after the user's NSS DB was 206 // initialized, will succeed. 207 TEST_F(ClientCertStoreChromeOSTest, RequestsAfterNSSInitSucceed) { 208 crypto::ScopedTestNSSChromeOSUser user("scopeduser"); 209 ASSERT_TRUE(user.constructed_successfully()); 210 user.FinishInit(); 211 212 crypto::ScopedTestSystemNSSKeySlot system_slot; 213 214 ClientCertStoreChromeOS store( 215 true /* use system slot */, 216 user.username_hash(), 217 ClientCertStoreChromeOS::PasswordDelegateFactory()); 218 scoped_refptr<X509Certificate> cert_1( 219 ImportCertForUser(user.username_hash(), "client_1.pem", "client_1.pk8")); 220 ASSERT_TRUE(cert_1.get()); 221 222 scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); 223 224 base::RunLoop run_loop; 225 store.GetClientCerts( 226 *request_all, &request_all->client_certs, run_loop.QuitClosure()); 227 run_loop.Run(); 228 229 ASSERT_EQ(1u, request_all->client_certs.size()); 230 } 231 232 // This verifies that a request in the context of User1 doesn't see certificates 233 // of User2, and the other way round. We check both directions, to ensure that 234 // the behavior doesn't depend on initialization order of the DBs, for example. 235 TEST_F(ClientCertStoreChromeOSTest, RequestDoesCrossReadOtherUserDB) { 236 crypto::ScopedTestNSSChromeOSUser user1("scopeduser1"); 237 ASSERT_TRUE(user1.constructed_successfully()); 238 crypto::ScopedTestNSSChromeOSUser user2("scopeduser2"); 239 ASSERT_TRUE(user2.constructed_successfully()); 240 241 user1.FinishInit(); 242 user2.FinishInit(); 243 244 crypto::ScopedTestSystemNSSKeySlot system_slot; 245 246 ClientCertStoreChromeOS store1( 247 true /* use system slot */, 248 user1.username_hash(), 249 ClientCertStoreChromeOS::PasswordDelegateFactory()); 250 ClientCertStoreChromeOS store2( 251 true /* use system slot */, 252 user2.username_hash(), 253 ClientCertStoreChromeOS::PasswordDelegateFactory()); 254 255 scoped_refptr<X509Certificate> cert_1( 256 ImportCertForUser(user1.username_hash(), "client_1.pem", "client_1.pk8")); 257 ASSERT_TRUE(cert_1.get()); 258 scoped_refptr<X509Certificate> cert_2( 259 ImportCertForUser(user2.username_hash(), "client_2.pem", "client_2.pk8")); 260 ASSERT_TRUE(cert_2.get()); 261 262 scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); 263 264 base::RunLoop run_loop_1; 265 base::RunLoop run_loop_2; 266 267 CertificateList selected_certs1, selected_certs2; 268 store1.GetClientCerts( 269 *request_all, &selected_certs1, run_loop_1.QuitClosure()); 270 store2.GetClientCerts( 271 *request_all, &selected_certs2, run_loop_2.QuitClosure()); 272 273 run_loop_1.Run(); 274 run_loop_2.Run(); 275 276 // store1 should only return certs of user1, namely cert_1. 277 ASSERT_EQ(1u, selected_certs1.size()); 278 EXPECT_TRUE(cert_1->Equals(selected_certs1[0].get())); 279 280 // store2 should only return certs of user2, namely cert_2. 281 ASSERT_EQ(1u, selected_certs2.size()); 282 EXPECT_TRUE(cert_2->Equals(selected_certs2[0].get())); 283 } 284 285 // This verifies that a request in the context of User1 doesn't see certificates 286 // of the system store if the system store is disabled. 287 TEST_F(ClientCertStoreChromeOSTest, RequestDoesCrossReadSystemDB) { 288 crypto::ScopedTestNSSChromeOSUser user1("scopeduser1"); 289 ASSERT_TRUE(user1.constructed_successfully()); 290 291 user1.FinishInit(); 292 293 crypto::ScopedTestSystemNSSKeySlot system_slot; 294 295 ClientCertStoreChromeOS store( 296 false /* do not use system slot */, 297 user1.username_hash(), 298 ClientCertStoreChromeOS::PasswordDelegateFactory()); 299 300 scoped_refptr<X509Certificate> cert_1( 301 ImportCertForUser(user1.username_hash(), "client_1.pem", "client_1.pk8")); 302 ASSERT_TRUE(cert_1.get()); 303 scoped_refptr<X509Certificate> cert_2( 304 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(), 305 "client_2.pem", 306 "client_2.pk8", 307 system_slot.slot())); 308 ASSERT_TRUE(cert_2.get()); 309 310 scoped_refptr<SSLCertRequestInfo> request_all(new SSLCertRequestInfo()); 311 312 base::RunLoop run_loop; 313 314 CertificateList selected_certs; 315 store.GetClientCerts(*request_all, &selected_certs, run_loop.QuitClosure()); 316 317 run_loop.Run(); 318 319 // store should only return certs of the user, namely cert_1. 320 ASSERT_EQ(1u, selected_certs.size()); 321 EXPECT_TRUE(cert_1->Equals(selected_certs[0].get())); 322 } 323 324 } // namespace net 325