OLD | NEW |
| (Empty) |
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 | |
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 | |
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: | |
48 NSSCertDatabaseChromeOSTest() | |
49 : observer_added_(false), user_1_("user1"), user_2_("user2") {} | |
50 | |
51 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 | |
80 void TearDown() override { | |
81 if (observer_added_) | |
82 CertDatabase::GetInstance()->RemoveObserver(this); | |
83 } | |
84 | |
85 // CertDatabase::Observer: | |
86 void OnCertAdded(const X509Certificate* cert) override { | |
87 added_.push_back(cert ? cert->os_cert_handle() : NULL); | |
88 } | |
89 | |
90 void OnCertRemoved(const X509Certificate* cert) override {} | |
91 | |
92 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.) | |
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. | |
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. | |
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. | |
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 | |
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 | |
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 | |
OLD | NEW |