Home | History | Annotate | Download | only in chromeos
      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 
      7 #include "base/bind.h"
      8 #include "base/files/file_util.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/run_loop.h"
     12 #include "crypto/nss_util_internal.h"
     13 #include "crypto/scoped_nss_types.h"
     14 #include "crypto/scoped_test_nss_chromeos_user.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/base/test_data_directory.h"
     17 #include "net/cert/nss_cert_database_chromeos.h"
     18 #include "net/cert/x509_certificate.h"
     19 #include "net/test/cert_test_util.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace chromeos {
     23 namespace {
     24 
     25 bool IsCertInCertificateList(const net::X509Certificate* cert,
     26                              const net::CertificateList& cert_list) {
     27   for (net::CertificateList::const_iterator it = cert_list.begin();
     28        it != cert_list.end();
     29        ++it) {
     30     if (net::X509Certificate::IsSameOSCert((*it)->os_cert_handle(),
     31                                             cert->os_cert_handle())) {
     32       return true;
     33     }
     34   }
     35   return false;
     36 }
     37 
     38 void FailOnPrivateSlotCallback(crypto::ScopedPK11Slot slot) {
     39   EXPECT_FALSE(true) << "GetPrivateSlotForChromeOSUser callback called even "
     40                      << "though the private slot had been initialized.";
     41 }
     42 
     43 class CertLoaderTest : public testing::Test,
     44                        public CertLoader::Observer {
     45  public:
     46   CertLoaderTest() : cert_loader_(NULL),
     47                      primary_user_("primary"),
     48                      certificates_loaded_events_count_(0U) {
     49   }
     50 
     51   virtual ~CertLoaderTest() {}
     52 
     53   virtual void SetUp() OVERRIDE {
     54     ASSERT_TRUE(primary_user_.constructed_successfully());
     55     ASSERT_TRUE(
     56         crypto::GetPublicSlotForChromeOSUser(primary_user_.username_hash()));
     57 
     58     CertLoader::Initialize();
     59     cert_loader_ = CertLoader::Get();
     60     cert_loader_->AddObserver(this);
     61   }
     62 
     63   virtual void TearDown() {
     64     cert_loader_->RemoveObserver(this);
     65     CertLoader::Shutdown();
     66   }
     67 
     68  protected:
     69   void StartCertLoaderWithPrimaryUser() {
     70     FinishUserInitAndGetDatabase(&primary_user_, &primary_db_);
     71     cert_loader_->StartWithNSSDB(primary_db_.get());
     72 
     73     base::RunLoop().RunUntilIdle();
     74     GetAndResetCertificatesLoadedEventsCount();
     75   }
     76 
     77   // CertLoader::Observer:
     78   // The test keeps count of times the observer method was called.
     79   virtual void OnCertificatesLoaded(const net::CertificateList& cert_list,
     80                                     bool initial_load) OVERRIDE {
     81     EXPECT_TRUE(certificates_loaded_events_count_ == 0 || !initial_load);
     82     certificates_loaded_events_count_++;
     83   }
     84 
     85   // Returns the number of |OnCertificatesLoaded| calls observed since the
     86   // last call to this method equals |value|.
     87   size_t GetAndResetCertificatesLoadedEventsCount() {
     88     size_t result = certificates_loaded_events_count_;
     89     certificates_loaded_events_count_ = 0;
     90     return result;
     91   }
     92 
     93   // Finishes initialization for the |user| and returns a user's NSS database
     94   // instance.
     95   void FinishUserInitAndGetDatabase(
     96       crypto::ScopedTestNSSChromeOSUser* user,
     97       scoped_ptr<net::NSSCertDatabaseChromeOS>* database) {
     98     ASSERT_TRUE(user->constructed_successfully());
     99 
    100     user->FinishInit();
    101 
    102     crypto::ScopedPK11Slot private_slot(
    103         crypto::GetPrivateSlotForChromeOSUser(
    104             user->username_hash(),
    105             base::Bind(&FailOnPrivateSlotCallback)));
    106     ASSERT_TRUE(private_slot);
    107 
    108     database->reset(new net::NSSCertDatabaseChromeOS(
    109         crypto::GetPublicSlotForChromeOSUser(user->username_hash()),
    110         private_slot.Pass()));
    111     (*database)->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy());
    112   }
    113 
    114   int GetDbPrivateSlotId(net::NSSCertDatabase* db) {
    115     return static_cast<int>(PK11_GetSlotID(db->GetPrivateSlot().get()));
    116   }
    117 
    118   void ImportCACert(const std::string& cert_file,
    119                     net::NSSCertDatabase* database,
    120                     net::CertificateList* imported_certs) {
    121     ASSERT_TRUE(database);
    122     ASSERT_TRUE(imported_certs);
    123 
    124     // Add a certificate to the user's db.
    125     *imported_certs = net::CreateCertificateListFromFile(
    126         net::GetTestCertsDirectory(),
    127         cert_file,
    128         net::X509Certificate::FORMAT_AUTO);
    129     ASSERT_EQ(1U, imported_certs->size());
    130 
    131     net::NSSCertDatabase::ImportCertFailureList failed;
    132     ASSERT_TRUE(database->ImportCACerts(*imported_certs,
    133                                         net::NSSCertDatabase::TRUST_DEFAULT,
    134                                         &failed));
    135     ASSERT_TRUE(failed.empty());
    136   }
    137 
    138   void ImportClientCertAndKey(const std::string& pkcs12_file,
    139                               net::NSSCertDatabase* database,
    140                               net::CertificateList* imported_certs) {
    141     ASSERT_TRUE(database);
    142     ASSERT_TRUE(imported_certs);
    143 
    144     std::string pkcs12_data;
    145     base::FilePath pkcs12_file_path =
    146         net::GetTestCertsDirectory().Append(pkcs12_file);
    147     ASSERT_TRUE(base::ReadFileToString(pkcs12_file_path, &pkcs12_data));
    148 
    149     net::CertificateList client_cert_list;
    150     scoped_refptr<net::CryptoModule> module(net::CryptoModule::CreateFromHandle(
    151         database->GetPrivateSlot().get()));
    152     ASSERT_EQ(net::OK,
    153               database->ImportFromPKCS12(module.get(),
    154                                          pkcs12_data,
    155                                          base::string16(),
    156                                          false,
    157                                          imported_certs));
    158     ASSERT_EQ(1U, imported_certs->size());
    159   }
    160 
    161   CertLoader* cert_loader_;
    162 
    163   // The user is primary as the one whose certificates CertLoader handles, it
    164   // has nothing to do with crypto::InitializeNSSForChromeOSUser is_primary_user
    165   // parameter (which is irrelevant for these tests).
    166   crypto::ScopedTestNSSChromeOSUser primary_user_;
    167   scoped_ptr<net::NSSCertDatabaseChromeOS> primary_db_;
    168 
    169   base::MessageLoop message_loop_;
    170 
    171  private:
    172   size_t certificates_loaded_events_count_;
    173 };
    174 
    175 TEST_F(CertLoaderTest, Basic) {
    176   EXPECT_FALSE(cert_loader_->CertificatesLoading());
    177   EXPECT_FALSE(cert_loader_->certificates_loaded());
    178   EXPECT_FALSE(cert_loader_->IsHardwareBacked());
    179 
    180   FinishUserInitAndGetDatabase(&primary_user_, &primary_db_);
    181 
    182   cert_loader_->StartWithNSSDB(primary_db_.get());
    183 
    184   EXPECT_FALSE(cert_loader_->certificates_loaded());
    185   EXPECT_TRUE(cert_loader_->CertificatesLoading());
    186   EXPECT_TRUE(cert_loader_->cert_list().empty());
    187 
    188   ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
    189   base::RunLoop().RunUntilIdle();
    190   EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    191 
    192   EXPECT_TRUE(cert_loader_->certificates_loaded());
    193   EXPECT_FALSE(cert_loader_->CertificatesLoading());
    194 
    195   // Default CA cert roots should get loaded.
    196   EXPECT_FALSE(cert_loader_->cert_list().empty());
    197 }
    198 
    199 TEST_F(CertLoaderTest, CertLoaderUpdatesCertListOnNewCert) {
    200   StartCertLoaderWithPrimaryUser();
    201 
    202   net::CertificateList certs;
    203   ImportCACert("root_ca_cert.pem", primary_db_.get(), &certs);
    204 
    205   // Certs are loaded asynchronously, so the new cert should not yet be in the
    206   // cert list.
    207   EXPECT_FALSE(
    208       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    209 
    210   ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
    211   base::RunLoop().RunUntilIdle();
    212   EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    213 
    214   // The certificate list should be updated now, as the message loop's been run.
    215   EXPECT_TRUE(
    216       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    217 }
    218 
    219 TEST_F(CertLoaderTest, CertLoaderNoUpdateOnSecondaryDbChanges) {
    220   crypto::ScopedTestNSSChromeOSUser secondary_user("secondary");
    221   scoped_ptr<net::NSSCertDatabaseChromeOS> secondary_db;
    222 
    223   StartCertLoaderWithPrimaryUser();
    224   FinishUserInitAndGetDatabase(&secondary_user, &secondary_db);
    225 
    226   net::CertificateList certs;
    227   ImportCACert("root_ca_cert.pem", secondary_db.get(), &certs);
    228 
    229   base::RunLoop().RunUntilIdle();
    230 
    231   EXPECT_FALSE(
    232       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    233 }
    234 
    235 TEST_F(CertLoaderTest, ClientLoaderUpdateOnNewClientCert) {
    236   StartCertLoaderWithPrimaryUser();
    237 
    238   net::CertificateList certs;
    239   ImportClientCertAndKey("websocket_client_cert.p12",
    240                          primary_db_.get(),
    241                          &certs);
    242 
    243   ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
    244   base::RunLoop().RunUntilIdle();
    245   EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    246 
    247   EXPECT_TRUE(
    248       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    249 }
    250 
    251 TEST_F(CertLoaderTest, CertLoaderNoUpdateOnNewClientCertInSecondaryDb) {
    252   crypto::ScopedTestNSSChromeOSUser secondary_user("secondary");
    253   scoped_ptr<net::NSSCertDatabaseChromeOS> secondary_db;
    254 
    255   StartCertLoaderWithPrimaryUser();
    256   FinishUserInitAndGetDatabase(&secondary_user, &secondary_db);
    257 
    258   net::CertificateList certs;
    259   ImportClientCertAndKey("websocket_client_cert.p12",
    260                          secondary_db.get(),
    261                          &certs);
    262 
    263   base::RunLoop().RunUntilIdle();
    264 
    265   EXPECT_FALSE(
    266       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    267 }
    268 
    269 TEST_F(CertLoaderTest, UpdatedOnCertRemoval) {
    270   StartCertLoaderWithPrimaryUser();
    271 
    272   net::CertificateList certs;
    273   ImportClientCertAndKey("websocket_client_cert.p12",
    274                          primary_db_.get(),
    275                          &certs);
    276 
    277   base::RunLoop().RunUntilIdle();
    278 
    279   ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    280   ASSERT_TRUE(
    281       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    282 
    283   primary_db_->DeleteCertAndKey(certs[0].get());
    284 
    285   ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
    286   base::RunLoop().RunUntilIdle();
    287   EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    288 
    289   ASSERT_FALSE(
    290       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    291 }
    292 
    293 TEST_F(CertLoaderTest, UpdatedOnCACertTrustChange) {
    294   StartCertLoaderWithPrimaryUser();
    295 
    296   net::CertificateList certs;
    297   ImportCACert("root_ca_cert.pem", primary_db_.get(), &certs);
    298 
    299   base::RunLoop().RunUntilIdle();
    300   ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    301   ASSERT_TRUE(
    302       IsCertInCertificateList(certs[0].get(), cert_loader_->cert_list()));
    303 
    304   // The value that should have been set by |ImportCACert|.
    305   ASSERT_EQ(net::NSSCertDatabase::TRUST_DEFAULT,
    306             primary_db_->GetCertTrust(certs[0].get(), net::CA_CERT));
    307   ASSERT_TRUE(primary_db_->SetCertTrust(
    308       certs[0].get(), net::CA_CERT, net::NSSCertDatabase::TRUSTED_SSL));
    309 
    310   // Cert trust change should trigger certificate reload in cert_loader_.
    311   ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
    312   base::RunLoop().RunUntilIdle();
    313   EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
    314 }
    315 
    316 }  // namespace
    317 }  // namespace chromeos
    318