Some additional network settings cleanup
[chromium-blink-merge.git] / chromeos / network / onc / onc_certificate_importer_impl_unittest.cc
blob158b29bce3ac024c5bed5be768a860c882549c03
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"
7 #include <cert.h>
8 #include <certdb.h>
9 #include <keyhi.h>
10 #include <pk11pub.h>
11 #include <string>
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/values.h"
17 #include "chromeos/network/onc/onc_test_utils.h"
18 #include "components/onc/onc_constants.h"
19 #include "crypto/nss_util_internal.h"
20 #include "crypto/scoped_test_nss_chromeos_user.h"
21 #include "net/base/crypto_module.h"
22 #include "net/cert/cert_type.h"
23 #include "net/cert/nss_cert_database_chromeos.h"
24 #include "net/cert/x509_certificate.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 namespace chromeos {
28 namespace onc {
30 namespace {
32 #if defined(USE_NSS)
33 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
34 // the new name of the macro.
35 #if !defined(CERTDB_TERMINAL_RECORD)
36 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
37 #endif
39 net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) {
40 CERTCertTrust trust = {0};
41 CERT_GetCertTrust(cert, &trust);
43 unsigned all_flags = trust.sslFlags | trust.emailFlags |
44 trust.objectSigningFlags;
46 if (cert->nickname && (all_flags & CERTDB_USER))
47 return net::USER_CERT;
48 if ((all_flags & CERTDB_VALID_CA) || CERT_IsCACert(cert, NULL))
49 return net::CA_CERT;
50 // TODO(mattm): http://crbug.com/128633.
51 if (trust.sslFlags & CERTDB_TERMINAL_RECORD)
52 return net::SERVER_CERT;
53 return net::OTHER_CERT;
55 #else
56 net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) {
57 NOTIMPLEMENTED();
58 return net::OTHER_CERT;
60 #endif // USE_NSS
62 } // namespace
64 class ONCCertificateImporterImplTest : public testing::Test {
65 public:
66 ONCCertificateImporterImplTest() : user_("username_hash"),
67 private_user_("private_user_hash") {}
69 virtual void SetUp() {
70 ASSERT_TRUE(user_.constructed_successfully());
71 ASSERT_TRUE(private_user_.constructed_successfully());
73 // By default test user will have the same public and private slot.
74 // Unfortunatelly, ONC importer should care about which slot certificates
75 // get imported to. To work around this, we create another NSS user whose
76 // public slot will act as the private slot.
77 // TODO(tbarzic): See if there's a better way to achieve this.
78 test_nssdb_.reset(new net::NSSCertDatabaseChromeOS(
79 crypto::GetPublicSlotForChromeOSUser(user_.username_hash()),
80 crypto::GetPublicSlotForChromeOSUser(private_user_.username_hash())));
82 // Test db should be empty at start of test.
83 EXPECT_TRUE(ListCertsInPublicSlot().empty());
84 EXPECT_TRUE(ListCertsInPrivateSlot().empty());
87 virtual ~ONCCertificateImporterImplTest() {}
89 protected:
90 void AddCertificatesFromFile(std::string filename, bool expected_success) {
91 scoped_ptr<base::DictionaryValue> onc =
92 test_utils::ReadTestDictionary(filename);
93 scoped_ptr<base::Value> certificates_value;
94 base::ListValue* certificates = NULL;
95 onc->RemoveWithoutPathExpansion(::onc::toplevel_config::kCertificates,
96 &certificates_value);
97 certificates_value.release()->GetAsList(&certificates);
98 onc_certificates_.reset(certificates);
100 web_trust_certificates_.clear();
101 imported_server_and_ca_certs_.clear();
102 CertificateImporterImpl importer(test_nssdb_.get());
103 EXPECT_EQ(
104 expected_success,
105 importer.ParseAndStoreCertificates(true, // allow web trust
106 *certificates,
107 &web_trust_certificates_,
108 &imported_server_and_ca_certs_));
110 public_list_ = ListCertsInPublicSlot();
111 private_list_ = ListCertsInPrivateSlot();
114 void AddCertificateFromFile(std::string filename,
115 net::CertType expected_type,
116 std::string* guid) {
117 std::string guid_temporary;
118 if (!guid)
119 guid = &guid_temporary;
121 AddCertificatesFromFile(filename, true);
122 ASSERT_EQ(1ul, public_list_.size() + private_list_.size());
123 if (!public_list_.empty())
124 EXPECT_EQ(expected_type, GetCertType(public_list_[0]->os_cert_handle()));
125 if (!private_list_.empty())
126 EXPECT_EQ(expected_type, GetCertType(private_list_[0]->os_cert_handle()));
128 base::DictionaryValue* certificate = NULL;
129 onc_certificates_->GetDictionary(0, &certificate);
130 certificate->GetStringWithoutPathExpansion(::onc::certificate::kGUID, guid);
132 if (expected_type == net::SERVER_CERT || expected_type == net::CA_CERT) {
133 EXPECT_EQ(1u, imported_server_and_ca_certs_.size());
134 EXPECT_TRUE(
135 imported_server_and_ca_certs_[*guid]->Equals(public_list_[0].get()));
136 } else { // net::USER_CERT
137 EXPECT_TRUE(imported_server_and_ca_certs_.empty());
141 scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
142 scoped_ptr<base::ListValue> onc_certificates_;
143 // List of certs in the nssdb's public slot.
144 net::CertificateList public_list_;
145 // List of certs in the nssdb's "private" slot.
146 net::CertificateList private_list_;
147 net::CertificateList web_trust_certificates_;
148 CertificateImporterImpl::CertsByGUID imported_server_and_ca_certs_;
150 private:
151 net::CertificateList ListCertsInPublicSlot() {
152 return ListCertsInSlot(test_nssdb_->GetPublicSlot().get());
155 net::CertificateList ListCertsInPrivateSlot() {
156 return ListCertsInSlot(test_nssdb_->GetPrivateSlot().get());
159 net::CertificateList ListCertsInSlot(PK11SlotInfo* slot) {
160 net::CertificateList result;
161 CERTCertList* cert_list = PK11_ListCertsInSlot(slot);
162 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
163 !CERT_LIST_END(node, cert_list);
164 node = CERT_LIST_NEXT(node)) {
165 result.push_back(net::X509Certificate::CreateFromHandle(
166 node->cert, net::X509Certificate::OSCertHandles()));
168 CERT_DestroyCertList(cert_list);
170 // Sort the result so that test comparisons can be deterministic.
171 std::sort(result.begin(), result.end(), net::X509Certificate::LessThan());
172 return result;
175 crypto::ScopedTestNSSChromeOSUser user_;
176 crypto::ScopedTestNSSChromeOSUser private_user_;
179 TEST_F(ONCCertificateImporterImplTest, MultipleCertificates) {
180 AddCertificatesFromFile("managed_toplevel2.onc", true);
181 EXPECT_EQ(onc_certificates_->GetSize(), public_list_.size());
182 EXPECT_TRUE(private_list_.empty());
183 EXPECT_EQ(2ul, imported_server_and_ca_certs_.size());
186 TEST_F(ONCCertificateImporterImplTest, MultipleCertificatesWithFailures) {
187 AddCertificatesFromFile("toplevel_partially_invalid.onc", false);
188 EXPECT_EQ(3ul, onc_certificates_->GetSize());
189 EXPECT_EQ(1ul, private_list_.size());
190 EXPECT_TRUE(public_list_.empty());
191 EXPECT_TRUE(imported_server_and_ca_certs_.empty());
194 TEST_F(ONCCertificateImporterImplTest, AddClientCertificate) {
195 std::string guid;
196 AddCertificateFromFile("certificate-client.onc", net::USER_CERT, &guid);
197 EXPECT_TRUE(web_trust_certificates_.empty());
198 EXPECT_EQ(1ul, private_list_.size());
199 EXPECT_TRUE(public_list_.empty());
201 SECKEYPrivateKeyList* privkey_list =
202 PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
203 EXPECT_TRUE(privkey_list);
204 if (privkey_list) {
205 SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(privkey_list);
206 int count = 0;
207 while (!PRIVKEY_LIST_END(node, privkey_list)) {
208 char* name = PK11_GetPrivateKeyNickname(node->key);
209 EXPECT_STREQ(guid.c_str(), name);
210 PORT_Free(name);
211 count++;
212 node = PRIVKEY_LIST_NEXT(node);
214 EXPECT_EQ(1, count);
215 SECKEY_DestroyPrivateKeyList(privkey_list);
218 SECKEYPublicKeyList* pubkey_list =
219 PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
220 EXPECT_TRUE(pubkey_list);
221 if (pubkey_list) {
222 SECKEYPublicKeyListNode* node = PUBKEY_LIST_HEAD(pubkey_list);
223 int count = 0;
224 while (!PUBKEY_LIST_END(node, pubkey_list)) {
225 count++;
226 node = PUBKEY_LIST_NEXT(node);
228 EXPECT_EQ(1, count);
229 SECKEY_DestroyPublicKeyList(pubkey_list);
233 TEST_F(ONCCertificateImporterImplTest, AddServerCertificateWithWebTrust) {
234 AddCertificateFromFile("certificate-server.onc", net::SERVER_CERT, NULL);
236 SECKEYPrivateKeyList* privkey_list =
237 PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
238 EXPECT_FALSE(privkey_list);
240 SECKEYPublicKeyList* pubkey_list =
241 PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
242 EXPECT_FALSE(pubkey_list);
244 ASSERT_EQ(1u, web_trust_certificates_.size());
245 ASSERT_EQ(1u, public_list_.size());
246 EXPECT_TRUE(private_list_.empty());
247 EXPECT_TRUE(CERT_CompareCerts(public_list_[0]->os_cert_handle(),
248 web_trust_certificates_[0]->os_cert_handle()));
251 TEST_F(ONCCertificateImporterImplTest, AddWebAuthorityCertificateWithWebTrust) {
252 AddCertificateFromFile("certificate-web-authority.onc", net::CA_CERT, NULL);
254 SECKEYPrivateKeyList* privkey_list =
255 PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
256 EXPECT_FALSE(privkey_list);
258 SECKEYPublicKeyList* pubkey_list =
259 PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
260 EXPECT_FALSE(pubkey_list);
262 ASSERT_EQ(1u, web_trust_certificates_.size());
263 ASSERT_EQ(1u, public_list_.size());
264 EXPECT_TRUE(private_list_.empty());
265 EXPECT_TRUE(CERT_CompareCerts(public_list_[0]->os_cert_handle(),
266 web_trust_certificates_[0]->os_cert_handle()));
269 TEST_F(ONCCertificateImporterImplTest, AddAuthorityCertificateWithoutWebTrust) {
270 AddCertificateFromFile("certificate-authority.onc", net::CA_CERT, NULL);
271 EXPECT_TRUE(web_trust_certificates_.empty());
273 SECKEYPrivateKeyList* privkey_list =
274 PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
275 EXPECT_FALSE(privkey_list);
277 SECKEYPublicKeyList* pubkey_list =
278 PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
279 EXPECT_FALSE(pubkey_list);
282 struct CertParam {
283 CertParam(net::CertType certificate_type,
284 const char* original_filename,
285 const char* update_filename)
286 : cert_type(certificate_type),
287 original_file(original_filename),
288 update_file(update_filename) {}
290 net::CertType cert_type;
291 const char* original_file;
292 const char* update_file;
295 class ONCCertificateImporterImplTestWithParam :
296 public ONCCertificateImporterImplTest,
297 public testing::WithParamInterface<CertParam> {
300 TEST_P(ONCCertificateImporterImplTestWithParam, UpdateCertificate) {
301 // First we import a certificate.
303 SCOPED_TRACE("Import original certificate");
304 AddCertificateFromFile(GetParam().original_file, GetParam().cert_type,
305 NULL);
308 // Now we import the same certificate with a different GUID. In case of a
309 // client cert, the cert should be retrievable via the new GUID.
311 SCOPED_TRACE("Import updated certificate");
312 AddCertificateFromFile(GetParam().update_file, GetParam().cert_type, NULL);
316 TEST_P(ONCCertificateImporterImplTestWithParam, ReimportCertificate) {
317 // Verify that reimporting a client certificate works.
318 for (int i = 0; i < 2; ++i) {
319 SCOPED_TRACE("Import certificate, iteration " + base::IntToString(i));
320 AddCertificateFromFile(GetParam().original_file, GetParam().cert_type,
321 NULL);
325 INSTANTIATE_TEST_CASE_P(
326 ONCCertificateImporterImplTestWithParam,
327 ONCCertificateImporterImplTestWithParam,
328 ::testing::Values(
329 CertParam(net::USER_CERT,
330 "certificate-client.onc",
331 "certificate-client-update.onc"),
332 CertParam(net::SERVER_CERT,
333 "certificate-server.onc",
334 "certificate-server-update.onc"),
335 CertParam(net::CA_CERT,
336 "certificate-web-authority.onc",
337 "certificate-web-authority-update.onc")));
339 } // namespace onc
340 } // namespace chromeos