Home | History | Annotate | Download | only in ssl
      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