xref: /aosp_15_r20/external/cronet/net/cert/nss_cert_database_chromeos_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
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 <memory>
8 
9 #include "base/functional/bind.h"
10 #include "base/functional/callback.h"
11 #include "base/run_loop.h"
12 #include "crypto/nss_util_internal.h"
13 #include "crypto/scoped_test_nss_chromeos_user.h"
14 #include "crypto/scoped_test_nss_db.h"
15 #include "net/cert/cert_database.h"
16 #include "net/cert/x509_util_nss.h"
17 #include "net/test/cert_builder.h"
18 #include "net/test/cert_test_util.h"
19 #include "net/test/test_data_directory.h"
20 #include "net/test/test_with_task_environment.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 namespace net {
24 
25 namespace {
26 
IsCertInCertificateList(const X509Certificate * cert,const ScopedCERTCertificateList & cert_list)27 bool IsCertInCertificateList(const X509Certificate* cert,
28                              const ScopedCERTCertificateList& cert_list) {
29   for (const auto& other : cert_list) {
30     if (x509_util::IsSameCertificate(other.get(), cert))
31       return true;
32   }
33   return false;
34 }
35 
IsCertInCertificateList(CERTCertificate * cert,const ScopedCERTCertificateList & cert_list)36 bool IsCertInCertificateList(CERTCertificate* cert,
37                              const ScopedCERTCertificateList& cert_list) {
38   for (const auto& other : cert_list) {
39     if (x509_util::IsSameCertificate(other.get(), cert))
40       return true;
41   }
42   return false;
43 }
44 
SwapCertLists(ScopedCERTCertificateList * destination,ScopedCERTCertificateList source)45 void SwapCertLists(ScopedCERTCertificateList* destination,
46                    ScopedCERTCertificateList source) {
47   ASSERT_TRUE(destination);
48 
49   destination->swap(source);
50 }
51 
52 }  // namespace
53 
54 class NSSCertDatabaseChromeOSTest : public TestWithTaskEnvironment,
55                                     public CertDatabase::Observer {
56  public:
NSSCertDatabaseChromeOSTest()57   NSSCertDatabaseChromeOSTest() : user_1_("user1"), user_2_("user2") {}
58 
SetUp()59   void SetUp() override {
60     // Initialize nss_util slots.
61     ASSERT_TRUE(user_1_.constructed_successfully());
62     ASSERT_TRUE(user_2_.constructed_successfully());
63     user_1_.FinishInit();
64     user_2_.FinishInit();
65 
66     // Create NSSCertDatabaseChromeOS for each user.
67     db_1_ = std::make_unique<NSSCertDatabaseChromeOS>(
68         crypto::GetPublicSlotForChromeOSUser(user_1_.username_hash()),
69         crypto::GetPrivateSlotForChromeOSUser(
70             user_1_.username_hash(),
71             base::OnceCallback<void(crypto::ScopedPK11Slot)>()));
72     db_1_->SetSystemSlot(
73         crypto::ScopedPK11Slot(PK11_ReferenceSlot(system_db_.slot())));
74     db_2_ = std::make_unique<NSSCertDatabaseChromeOS>(
75         crypto::GetPublicSlotForChromeOSUser(user_2_.username_hash()),
76         crypto::GetPrivateSlotForChromeOSUser(
77             user_2_.username_hash(),
78             base::OnceCallback<void(crypto::ScopedPK11Slot)>()));
79 
80     // Add observer to CertDatabase for checking that notifications from
81     // NSSCertDatabaseChromeOS are proxied to the CertDatabase.
82     CertDatabase::GetInstance()->AddObserver(this);
83     observer_added_ = true;
84   }
85 
TearDown()86   void TearDown() override {
87     if (observer_added_)
88       CertDatabase::GetInstance()->RemoveObserver(this);
89   }
90 
91   // CertDatabase::Observer:
OnTrustStoreChanged()92   void OnTrustStoreChanged() override { trust_store_changed_count_++; }
OnClientCertStoreChanged()93   void OnClientCertStoreChanged() override { client_cert_changed_count_++; }
94 
95  protected:
96   bool observer_added_ = false;
97   int trust_store_changed_count_ = 0;
98   int client_cert_changed_count_ = 0;
99 
100   crypto::ScopedTestNSSChromeOSUser user_1_;
101   crypto::ScopedTestNSSChromeOSUser user_2_;
102   crypto::ScopedTestNSSDB system_db_;
103   std::unique_ptr<NSSCertDatabaseChromeOS> db_1_;
104   std::unique_ptr<NSSCertDatabaseChromeOS> db_2_;
105 };
106 
107 // Test that ListModules() on each user includes that user's NSS software slot,
108 // and does not include the software slot of the other user. (Does not check the
109 // private slot, since it is the same as the public slot in tests.)
TEST_F(NSSCertDatabaseChromeOSTest,ListModules)110 TEST_F(NSSCertDatabaseChromeOSTest, ListModules) {
111   std::vector<crypto::ScopedPK11Slot> modules_1;
112   std::vector<crypto::ScopedPK11Slot> modules_2;
113 
114   db_1_->ListModules(&modules_1, false /* need_rw */);
115   db_2_->ListModules(&modules_2, false /* need_rw */);
116 
117   bool found_1 = false;
118   for (std::vector<crypto::ScopedPK11Slot>::iterator it = modules_1.begin();
119        it != modules_1.end(); ++it) {
120     EXPECT_NE(db_2_->GetPublicSlot().get(), (*it).get());
121     if ((*it).get() == db_1_->GetPublicSlot().get())
122       found_1 = true;
123   }
124   EXPECT_TRUE(found_1);
125 
126   bool found_2 = false;
127   for (std::vector<crypto::ScopedPK11Slot>::iterator it = modules_2.begin();
128        it != modules_2.end(); ++it) {
129     EXPECT_NE(db_1_->GetPublicSlot().get(), (*it).get());
130     if ((*it).get() == db_2_->GetPublicSlot().get())
131       found_2 = true;
132   }
133   EXPECT_TRUE(found_2);
134 }
135 
136 // Test that ImportCACerts imports the cert to the correct slot, and that
137 // ListCerts includes the added cert for the correct user, and does not include
138 // it for the other user.
TEST_F(NSSCertDatabaseChromeOSTest,ImportCACerts)139 TEST_F(NSSCertDatabaseChromeOSTest, ImportCACerts) {
140   // Load test certs from disk.
141   ScopedCERTCertificateList certs_1 = CreateCERTCertificateListFromFile(
142       GetTestCertsDirectory(), "root_ca_cert.pem",
143       X509Certificate::FORMAT_AUTO);
144   ASSERT_EQ(1U, certs_1.size());
145 
146   auto [leaf2, root2] = CertBuilder::CreateSimpleChain2();
147   ScopedCERTCertificateList certs_2 =
148       x509_util::CreateCERTCertificateListFromX509Certificate(
149           root2->GetX509Certificate().get());
150   ASSERT_EQ(1U, certs_2.size());
151 
152   // Import one cert for each user.
153   NSSCertDatabase::ImportCertFailureList failed;
154   EXPECT_TRUE(
155       db_1_->ImportCACerts(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
156   EXPECT_EQ(0U, failed.size());
157   failed.clear();
158   EXPECT_TRUE(
159       db_2_->ImportCACerts(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
160   EXPECT_EQ(0U, failed.size());
161 
162   // Get cert list for each user.
163   ScopedCERTCertificateList user_1_certlist;
164   ScopedCERTCertificateList user_2_certlist;
165   db_1_->ListCerts(
166       base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist)));
167   db_2_->ListCerts(
168       base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist)));
169 
170   // Run the message loop so the observer notifications get processed and
171   // lookups are completed.
172   RunUntilIdle();
173   // Should have gotten two OnTrustStoreChanged notifications.
174   EXPECT_EQ(2, trust_store_changed_count_);
175   EXPECT_EQ(0, client_cert_changed_count_);
176 
177   EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist));
178   EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist));
179 
180   EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist));
181   EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist));
182 }
183 
184 // Test that ImportServerCerts imports the cert to the correct slot, and that
185 // ListCerts includes the added cert for the correct user, and does not include
186 // it for the other user.
TEST_F(NSSCertDatabaseChromeOSTest,ImportServerCert)187 TEST_F(NSSCertDatabaseChromeOSTest, ImportServerCert) {
188   // Load test certs from disk.
189   ScopedCERTCertificateList certs_1 = CreateCERTCertificateListFromFile(
190       GetTestCertsDirectory(), "ok_cert.pem", X509Certificate::FORMAT_AUTO);
191   ASSERT_EQ(1U, certs_1.size());
192 
193   auto [leaf2, root2] = CertBuilder::CreateSimpleChain2();
194   ScopedCERTCertificateList certs_2 =
195       x509_util::CreateCERTCertificateListFromX509Certificate(
196           leaf2->GetX509Certificate().get());
197   ASSERT_EQ(1U, certs_2.size());
198 
199   // Import one cert for each user.
200   NSSCertDatabase::ImportCertFailureList failed;
201   EXPECT_TRUE(
202       db_1_->ImportServerCert(certs_1, NSSCertDatabase::TRUSTED_SSL, &failed));
203   EXPECT_EQ(0U, failed.size());
204   failed.clear();
205   EXPECT_TRUE(
206       db_2_->ImportServerCert(certs_2, NSSCertDatabase::TRUSTED_SSL, &failed));
207   EXPECT_EQ(0U, failed.size());
208 
209   // Get cert list for each user.
210   ScopedCERTCertificateList user_1_certlist;
211   ScopedCERTCertificateList user_2_certlist;
212   db_1_->ListCerts(
213       base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist)));
214   db_2_->ListCerts(
215       base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist)));
216 
217   // Run the message loop so the observer notifications get processed and
218   // lookups are completed.
219   RunUntilIdle();
220   // TODO(mattm): this should be 2, but ImportServerCert doesn't currently
221   // generate notifications.
222   EXPECT_EQ(0, trust_store_changed_count_);
223   EXPECT_EQ(0, client_cert_changed_count_);
224 
225   EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist));
226   EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist));
227 
228   EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist));
229   EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist));
230 }
231 
232 // Tests that There is no crash if the database is deleted while ListCerts
233 // is being processed on the worker pool.
TEST_F(NSSCertDatabaseChromeOSTest,NoCrashIfShutdownBeforeDoneOnWorkerPool)234 TEST_F(NSSCertDatabaseChromeOSTest, NoCrashIfShutdownBeforeDoneOnWorkerPool) {
235   ScopedCERTCertificateList certlist;
236   db_1_->ListCerts(base::BindOnce(&SwapCertLists, base::Unretained(&certlist)));
237   EXPECT_EQ(0U, certlist.size());
238 
239   db_1_.reset();
240 
241   RunUntilIdle();
242 
243   EXPECT_LT(0U, certlist.size());
244 }
245 
TEST_F(NSSCertDatabaseChromeOSTest,ListCertsReadsSystemSlot)246 TEST_F(NSSCertDatabaseChromeOSTest, ListCertsReadsSystemSlot) {
247   scoped_refptr<X509Certificate> cert_1(
248       ImportClientCertAndKeyFromFile(GetTestCertsDirectory(),
249                                      "client_1.pem",
250                                      "client_1.pk8",
251                                      db_1_->GetPublicSlot().get()));
252 
253   scoped_refptr<X509Certificate> cert_2(
254       ImportClientCertAndKeyFromFile(GetTestCertsDirectory(),
255                                      "client_2.pem",
256                                      "client_2.pk8",
257                                      db_1_->GetSystemSlot().get()));
258 
259   ScopedCERTCertificateList certs;
260   db_1_->ListCerts(base::BindOnce(&SwapCertLists, base::Unretained(&certs)));
261   RunUntilIdle();
262   EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs));
263   EXPECT_TRUE(IsCertInCertificateList(cert_2.get(), certs));
264 }
265 
TEST_F(NSSCertDatabaseChromeOSTest,ListCertsDoesNotCrossReadSystemSlot)266 TEST_F(NSSCertDatabaseChromeOSTest, ListCertsDoesNotCrossReadSystemSlot) {
267   scoped_refptr<X509Certificate> cert_1(
268       ImportClientCertAndKeyFromFile(GetTestCertsDirectory(),
269                                      "client_1.pem",
270                                      "client_1.pk8",
271                                      db_2_->GetPublicSlot().get()));
272 
273   scoped_refptr<X509Certificate> cert_2(
274       ImportClientCertAndKeyFromFile(GetTestCertsDirectory(),
275                                      "client_2.pem",
276                                      "client_2.pk8",
277                                      system_db_.slot()));
278   ScopedCERTCertificateList certs;
279   db_2_->ListCerts(base::BindOnce(&SwapCertLists, base::Unretained(&certs)));
280   RunUntilIdle();
281   EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs));
282   EXPECT_FALSE(IsCertInCertificateList(cert_2.get(), certs));
283 }
284 
TEST_F(NSSCertDatabaseChromeOSTest,SetCertTrustCertIsAlreadyOnPublicSlot)285 TEST_F(NSSCertDatabaseChromeOSTest, SetCertTrustCertIsAlreadyOnPublicSlot) {
286   // Import a certificate onto the public slot (and safety check that it ended
287   // up there).
288   ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
289       GetTestCertsDirectory(), "root_ca_cert.pem",
290       X509Certificate::FORMAT_AUTO);
291   ASSERT_EQ(1U, certs.size());
292 
293   NSSCertDatabase::ImportCertFailureList failed;
294   EXPECT_TRUE(
295       db_1_->ImportCACerts(certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
296   EXPECT_EQ(0U, failed.size());
297 
298   ASSERT_TRUE(NSSCertDatabase::IsCertificateOnSlot(
299       certs[0].get(), db_1_->GetPublicSlot().get()));
300 
301   // Check that trust settings modification works.
302   EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
303             db_1_->GetCertTrust(certs[0].get(), CA_CERT));
304 
305   EXPECT_TRUE(db_1_->SetCertTrust(certs[0].get(), CA_CERT,
306                                   NSSCertDatabase::TRUSTED_SSL));
307 
308   EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
309             db_1_->GetCertTrust(certs[0].get(), CA_CERT));
310 }
311 
TEST_F(NSSCertDatabaseChromeOSTest,SetCertTrustCertIsOnlyOnOtherSlot)312 TEST_F(NSSCertDatabaseChromeOSTest, SetCertTrustCertIsOnlyOnOtherSlot) {
313   crypto::ScopedTestNSSDB other_slot;
314 
315   // Import a certificate onto a slot known by NSS which is not the
316   // NSSCertDatabase's public slot.
317   ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
318       GetTestCertsDirectory(), "root_ca_cert.pem",
319       X509Certificate::FORMAT_AUTO);
320   ASSERT_EQ(1U, certs.size());
321   ASSERT_EQ(SECSuccess, PK11_ImportCert(other_slot.slot(), certs[0].get(),
322                                         CK_INVALID_HANDLE, "cert0",
323                                         PR_FALSE /* includeTrust (unused) */));
324   ASSERT_FALSE(NSSCertDatabase::IsCertificateOnSlot(
325       certs[0].get(), db_1_->GetPublicSlot().get()));
326 
327   // Check that trust settings modification works.
328   EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
329             db_1_->GetCertTrust(certs[0].get(), CA_CERT));
330 
331   EXPECT_TRUE(db_1_->SetCertTrust(certs[0].get(), CA_CERT,
332                                   NSSCertDatabase::TRUSTED_SSL));
333 
334   EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
335             db_1_->GetCertTrust(certs[0].get(), CA_CERT));
336 
337   // Check that the certificate has been put onto the public slot as a side
338   // effect of changing trust.
339   EXPECT_TRUE(NSSCertDatabase::IsCertificateOnSlot(
340       certs[0].get(), db_1_->GetPublicSlot().get()));
341 }
342 
TEST_F(NSSCertDatabaseChromeOSTest,SetCertTrustPublicSlotIsSystemSlot)343 TEST_F(NSSCertDatabaseChromeOSTest, SetCertTrustPublicSlotIsSystemSlot) {
344   // Create a NSSCertDatabase with |public_slot|==|system_slot|.
345   NSSCertDatabaseChromeOS test_db_for_system_slot(
346       /*public_slot=*/crypto::ScopedPK11Slot(
347           PK11_ReferenceSlot(system_db_.slot())),
348       /*private_slot=*/{});
349   test_db_for_system_slot.SetSystemSlot(
350       crypto::ScopedPK11Slot(PK11_ReferenceSlot(system_db_.slot())));
351 
352   // Import a certificate onto a slot known by NSS which is not the
353   // NSSCertDatabase's public slot.
354   crypto::ScopedTestNSSDB other_slot;
355   ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
356       GetTestCertsDirectory(), "root_ca_cert.pem",
357       X509Certificate::FORMAT_AUTO);
358   ASSERT_EQ(1U, certs.size());
359   ASSERT_EQ(SECSuccess, PK11_ImportCert(other_slot.slot(), certs[0].get(),
360                                         CK_INVALID_HANDLE, "cert0",
361                                         PR_FALSE /* includeTrust (unused) */));
362   ASSERT_FALSE(NSSCertDatabase::IsCertificateOnSlot(
363       certs[0].get(), test_db_for_system_slot.GetPublicSlot().get()));
364 
365   // Changing trust through |test_db_for_system_slot| should fail and not do
366   // anything, because the database is not allowed to put the certificate onto
367   // its public slot (because it is also the system slot).
368   EXPECT_FALSE(test_db_for_system_slot.SetCertTrust(
369       certs[0].get(), CA_CERT, NSSCertDatabase::TRUSTED_SSL));
370   EXPECT_FALSE(NSSCertDatabase::IsCertificateOnSlot(
371       certs[0].get(), test_db_for_system_slot.GetPublicSlot().get()));
372 }
373 
374 }  // namespace net
375