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