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.
5 #include "chromeos/network/onc/onc_certificate_importer_impl.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/values.h"
16 #include "chromeos/network/onc/onc_constants.h"
17 #include "chromeos/network/onc/onc_test_utils.h"
18 #include "crypto/nss_util.h"
19 #include "net/base/crypto_module.h"
20 #include "net/cert/cert_type.h"
21 #include "net/cert/nss_cert_database.h"
22 #include "net/cert/x509_certificate.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
30 // the new name of the macro.
31 #if !defined(CERTDB_TERMINAL_RECORD)
32 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
35 net::CertType
GetCertType(net::X509Certificate::OSCertHandle cert
) {
36 CERTCertTrust trust
= {0};
37 CERT_GetCertTrust(cert
, &trust
);
39 unsigned all_flags
= trust
.sslFlags
| trust
.emailFlags
|
40 trust
.objectSigningFlags
;
42 if (cert
->nickname
&& (all_flags
& CERTDB_USER
))
43 return net::USER_CERT
;
44 if ((all_flags
& CERTDB_VALID_CA
) || CERT_IsCACert(cert
, NULL
))
46 // TODO(mattm): http://crbug.com/128633.
47 if (trust
.sslFlags
& CERTDB_TERMINAL_RECORD
)
48 return net::SERVER_CERT
;
49 return net::UNKNOWN_CERT
;
52 net::CertType
GetCertType(net::X509Certificate::OSCertHandle cert
) {
54 return net::UNKNOWN_CERT
;
58 class ONCCertificateImporterImplTest
: public testing::Test
{
60 virtual void SetUp() {
61 ASSERT_TRUE(test_nssdb_
.is_open());
63 slot_
= net::NSSCertDatabase::GetInstance()->GetPublicModule();
65 // Don't run the test if the setup failed.
66 ASSERT_TRUE(slot_
->os_module_handle());
68 // Test db should be empty at start of test.
69 EXPECT_EQ(0ul, ListCertsInSlot().size());
72 virtual void TearDown() {
73 EXPECT_TRUE(CleanupSlotContents());
74 EXPECT_EQ(0ul, ListCertsInSlot().size());
77 virtual ~ONCCertificateImporterImplTest() {}
80 void AddCertificatesFromFile(std::string filename
, bool expected_success
) {
81 scoped_ptr
<base::DictionaryValue
> onc
=
82 test_utils::ReadTestDictionary(filename
);
83 scoped_ptr
<base::Value
> certificates_value
;
84 base::ListValue
* certificates
= NULL
;
85 onc
->RemoveWithoutPathExpansion(toplevel_config::kCertificates
,
87 certificates_value
.release()->GetAsList(&certificates
);
88 onc_certificates_
.reset(certificates
);
90 web_trust_certificates_
.clear();
91 imported_server_and_ca_certs_
.clear();
92 CertificateImporterImpl importer
;
95 importer
.ParseAndStoreCertificates(true, // allow web trust
97 &web_trust_certificates_
,
98 &imported_server_and_ca_certs_
));
100 result_list_
.clear();
101 result_list_
= ListCertsInSlot();
104 void AddCertificateFromFile(std::string filename
,
105 net::CertType expected_type
,
107 std::string guid_temporary
;
109 guid
= &guid_temporary
;
111 AddCertificatesFromFile(filename
, true);
112 ASSERT_EQ(1ul, result_list_
.size());
113 EXPECT_EQ(expected_type
, GetCertType(result_list_
[0]->os_cert_handle()));
115 base::DictionaryValue
* certificate
= NULL
;
116 onc_certificates_
->GetDictionary(0, &certificate
);
117 certificate
->GetStringWithoutPathExpansion(certificate::kGUID
, guid
);
119 if (expected_type
== net::SERVER_CERT
|| expected_type
== net::CA_CERT
) {
120 EXPECT_EQ(1u, imported_server_and_ca_certs_
.size());
121 EXPECT_TRUE(imported_server_and_ca_certs_
[*guid
]->Equals(
123 } else { // net::USER_CERT
124 EXPECT_TRUE(imported_server_and_ca_certs_
.empty());
125 CertificateImporterImpl::ListCertsWithNickname(*guid
, &result_list_
);
129 scoped_ptr
<base::ListValue
> onc_certificates_
;
130 scoped_refptr
<net::CryptoModule
> slot_
;
131 net::CertificateList result_list_
;
132 net::CertificateList web_trust_certificates_
;
133 CertificateImporterImpl::CertsByGUID imported_server_and_ca_certs_
;
136 net::CertificateList
ListCertsInSlot() {
137 net::CertificateList result
;
138 CERTCertList
* cert_list
= PK11_ListCertsInSlot(slot_
->os_module_handle());
139 for (CERTCertListNode
* node
= CERT_LIST_HEAD(cert_list
);
140 !CERT_LIST_END(node
, cert_list
);
141 node
= CERT_LIST_NEXT(node
)) {
142 result
.push_back(net::X509Certificate::CreateFromHandle(
143 node
->cert
, net::X509Certificate::OSCertHandles()));
145 CERT_DestroyCertList(cert_list
);
147 // Sort the result so that test comparisons can be deterministic.
148 std::sort(result
.begin(), result
.end(), net::X509Certificate::LessThan());
152 bool CleanupSlotContents() {
154 net::CertificateList certs
= ListCertsInSlot();
155 for (size_t i
= 0; i
< certs
.size(); ++i
) {
156 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(certs
[i
]
163 crypto::ScopedTestNSSDB test_nssdb_
;
166 TEST_F(ONCCertificateImporterImplTest
, MultipleCertificates
) {
167 AddCertificatesFromFile("managed_toplevel2.onc", true);
168 EXPECT_EQ(onc_certificates_
->GetSize(), result_list_
.size());
169 EXPECT_EQ(2ul, imported_server_and_ca_certs_
.size());
172 TEST_F(ONCCertificateImporterImplTest
, MultipleCertificatesWithFailures
) {
173 AddCertificatesFromFile("toplevel_partially_invalid.onc", false);
174 EXPECT_EQ(3ul, onc_certificates_
->GetSize());
175 EXPECT_EQ(1ul, result_list_
.size());
176 EXPECT_TRUE(imported_server_and_ca_certs_
.empty());
179 TEST_F(ONCCertificateImporterImplTest
, AddClientCertificate
) {
181 AddCertificateFromFile("certificate-client.onc", net::USER_CERT
, &guid
);
182 EXPECT_TRUE(web_trust_certificates_
.empty());
184 SECKEYPrivateKeyList
* privkey_list
=
185 PK11_ListPrivKeysInSlot(slot_
->os_module_handle(), NULL
, NULL
);
186 EXPECT_TRUE(privkey_list
);
188 SECKEYPrivateKeyListNode
* node
= PRIVKEY_LIST_HEAD(privkey_list
);
190 while (!PRIVKEY_LIST_END(node
, privkey_list
)) {
191 char* name
= PK11_GetPrivateKeyNickname(node
->key
);
192 EXPECT_STREQ(guid
.c_str(), name
);
195 node
= PRIVKEY_LIST_NEXT(node
);
198 SECKEY_DestroyPrivateKeyList(privkey_list
);
201 SECKEYPublicKeyList
* pubkey_list
=
202 PK11_ListPublicKeysInSlot(slot_
->os_module_handle(), NULL
);
203 EXPECT_TRUE(pubkey_list
);
205 SECKEYPublicKeyListNode
* node
= PUBKEY_LIST_HEAD(pubkey_list
);
207 while (!PUBKEY_LIST_END(node
, pubkey_list
)) {
209 node
= PUBKEY_LIST_NEXT(node
);
212 SECKEY_DestroyPublicKeyList(pubkey_list
);
216 TEST_F(ONCCertificateImporterImplTest
, AddServerCertificateWithWebTrust
) {
217 AddCertificateFromFile("certificate-server.onc", net::SERVER_CERT
, NULL
);
219 SECKEYPrivateKeyList
* privkey_list
=
220 PK11_ListPrivKeysInSlot(slot_
->os_module_handle(), NULL
, NULL
);
221 EXPECT_FALSE(privkey_list
);
223 SECKEYPublicKeyList
* pubkey_list
=
224 PK11_ListPublicKeysInSlot(slot_
->os_module_handle(), NULL
);
225 EXPECT_FALSE(pubkey_list
);
227 ASSERT_EQ(1u, web_trust_certificates_
.size());
228 ASSERT_EQ(1u, result_list_
.size());
229 EXPECT_TRUE(CERT_CompareCerts(result_list_
[0]->os_cert_handle(),
230 web_trust_certificates_
[0]->os_cert_handle()));
233 TEST_F(ONCCertificateImporterImplTest
, AddWebAuthorityCertificateWithWebTrust
) {
234 AddCertificateFromFile("certificate-web-authority.onc", net::CA_CERT
, NULL
);
236 SECKEYPrivateKeyList
* privkey_list
=
237 PK11_ListPrivKeysInSlot(slot_
->os_module_handle(), NULL
, NULL
);
238 EXPECT_FALSE(privkey_list
);
240 SECKEYPublicKeyList
* pubkey_list
=
241 PK11_ListPublicKeysInSlot(slot_
->os_module_handle(), NULL
);
242 EXPECT_FALSE(pubkey_list
);
244 ASSERT_EQ(1u, web_trust_certificates_
.size());
245 ASSERT_EQ(1u, result_list_
.size());
246 EXPECT_TRUE(CERT_CompareCerts(result_list_
[0]->os_cert_handle(),
247 web_trust_certificates_
[0]->os_cert_handle()));
250 TEST_F(ONCCertificateImporterImplTest
, AddAuthorityCertificateWithoutWebTrust
) {
251 AddCertificateFromFile("certificate-authority.onc", net::CA_CERT
, NULL
);
252 EXPECT_TRUE(web_trust_certificates_
.empty());
254 SECKEYPrivateKeyList
* privkey_list
=
255 PK11_ListPrivKeysInSlot(slot_
->os_module_handle(), NULL
, NULL
);
256 EXPECT_FALSE(privkey_list
);
258 SECKEYPublicKeyList
* pubkey_list
=
259 PK11_ListPublicKeysInSlot(slot_
->os_module_handle(), NULL
);
260 EXPECT_FALSE(pubkey_list
);
264 CertParam(net::CertType certificate_type
,
265 const char* original_filename
,
266 const char* update_filename
)
267 : cert_type(certificate_type
),
268 original_file(original_filename
),
269 update_file(update_filename
) {}
271 net::CertType cert_type
;
272 const char* original_file
;
273 const char* update_file
;
276 class ONCCertificateImporterImplTestWithParam
:
277 public ONCCertificateImporterImplTest
,
278 public testing::WithParamInterface
<CertParam
> {
281 TEST_P(ONCCertificateImporterImplTestWithParam
, UpdateCertificate
) {
282 // First we import a certificate.
284 SCOPED_TRACE("Import original certificate");
285 AddCertificateFromFile(GetParam().original_file
, GetParam().cert_type
,
289 // Now we import the same certificate with a different GUID. In case of a
290 // client cert, the cert should be retrievable via the new GUID.
292 SCOPED_TRACE("Import updated certificate");
293 AddCertificateFromFile(GetParam().update_file
, GetParam().cert_type
, NULL
);
297 TEST_P(ONCCertificateImporterImplTestWithParam
, ReimportCertificate
) {
298 // Verify that reimporting a client certificate works.
299 for (int i
= 0; i
< 2; ++i
) {
300 SCOPED_TRACE("Import certificate, iteration " + base::IntToString(i
));
301 AddCertificateFromFile(GetParam().original_file
, GetParam().cert_type
,
306 INSTANTIATE_TEST_CASE_P(
307 ONCCertificateImporterImplTestWithParam
,
308 ONCCertificateImporterImplTestWithParam
,
310 CertParam(net::USER_CERT
,
311 "certificate-client.onc",
312 "certificate-client-update.onc"),
313 CertParam(net::SERVER_CERT
,
314 "certificate-server.onc",
315 "certificate-server-update.onc"),
316 CertParam(net::CA_CERT
,
317 "certificate-web-authority.onc",
318 "certificate-web-authority-update.onc")));
321 } // namespace chromeos