• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsCertInCertificateList(const X509Certificate * cert,const CertificateList & cert_list)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 
SwapCertLists(CertificateList * destination,scoped_ptr<CertificateList> source)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:
NSSCertDatabaseChromeOSTest()48   NSSCertDatabaseChromeOSTest()
49       : observer_added_(false), user_1_("user1"), user_2_("user2") {}
50 
SetUp()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 
TearDown()80   virtual void TearDown() OVERRIDE {
81     if (observer_added_)
82       CertDatabase::GetInstance()->RemoveObserver(this);
83   }
84 
85   // CertDatabase::Observer:
OnCertAdded(const X509Certificate * cert)86   virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
87     added_.push_back(cert ? cert->os_cert_handle() : NULL);
88   }
89 
OnCertRemoved(const X509Certificate * cert)90   virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {}
91 
OnCACertChanged(const X509Certificate * cert)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.)
TEST_F(NSSCertDatabaseChromeOSTest,ListModules)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.
TEST_F(NSSCertDatabaseChromeOSTest,ImportCACerts)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.
TEST_F(NSSCertDatabaseChromeOSTest,ImportServerCert)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.
TEST_F(NSSCertDatabaseChromeOSTest,NoCrashIfShutdownBeforeDoneOnWorkerPool)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 
TEST_F(NSSCertDatabaseChromeOSTest,ListCertsReadsSystemSlot)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 
TEST_F(NSSCertDatabaseChromeOSTest,ListCertsDoesNotCrossReadSystemSlot)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
324