bb05d14b48b869c25a9bb3da12a152fe033db707
[platform/framework/web/crosswalk.git] / src / chromeos / network / onc / onc_certificate_importer_impl_unittest.cc
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 "chromeos/network/onc/onc_certificate_importer_impl.h"
6
7 #include <cert.h>
8 #include <certdb.h>
9 #include <keyhi.h>
10 #include <pk11pub.h>
11 #include <string>
12
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"
26
27 namespace chromeos {
28 namespace onc {
29
30 namespace {
31
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
38
39 net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) {
40   CERTCertTrust trust = {0};
41   CERT_GetCertTrust(cert, &trust);
42
43   unsigned all_flags = trust.sslFlags | trust.emailFlags |
44       trust.objectSigningFlags;
45
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;
54 }
55 #else
56 net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) {
57   NOTIMPLEMENTED();
58   return net::OTHER_CERT;
59 }
60 #endif  // USE_NSS
61
62 }  // namespace
63
64 class ONCCertificateImporterImplTest : public testing::Test {
65  public:
66   ONCCertificateImporterImplTest() : user_("username_hash"),
67                                      private_user_("private_user_hash") {}
68
69   virtual void SetUp() {
70     ASSERT_TRUE(user_.constructed_successfully());
71     ASSERT_TRUE(private_user_.constructed_successfully());
72
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())));
81
82     // Test db should be empty at start of test.
83     EXPECT_TRUE(ListCertsInPublicSlot().empty());
84     EXPECT_TRUE(ListCertsInPrivateSlot().empty());
85   }
86
87   virtual ~ONCCertificateImporterImplTest() {}
88
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);
99
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_));
109
110     public_list_ = ListCertsInPublicSlot();
111     private_list_ = ListCertsInPrivateSlot();
112   }
113
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;
120
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()));
127
128     base::DictionaryValue* certificate = NULL;
129     onc_certificates_->GetDictionary(0, &certificate);
130     certificate->GetStringWithoutPathExpansion(::onc::certificate::kGUID, guid);
131
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(imported_server_and_ca_certs_[*guid]->Equals(
135           public_list_[0]));
136     } else {  // net::USER_CERT
137       EXPECT_TRUE(imported_server_and_ca_certs_.empty());
138     }
139   }
140
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_;
149
150  private:
151   net::CertificateList ListCertsInPublicSlot() {
152     return ListCertsInSlot(test_nssdb_->GetPublicSlot().get());
153   }
154
155   net::CertificateList ListCertsInPrivateSlot() {
156     return ListCertsInSlot(test_nssdb_->GetPrivateSlot().get());
157   }
158
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()));
167     }
168     CERT_DestroyCertList(cert_list);
169
170     // Sort the result so that test comparisons can be deterministic.
171     std::sort(result.begin(), result.end(), net::X509Certificate::LessThan());
172     return result;
173   }
174
175   crypto::ScopedTestNSSChromeOSUser user_;
176   crypto::ScopedTestNSSChromeOSUser private_user_;
177 };
178
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());
184 }
185
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());
192 }
193
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());
200
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);
213     }
214     EXPECT_EQ(1, count);
215     SECKEY_DestroyPrivateKeyList(privkey_list);
216   }
217
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);
227     }
228     EXPECT_EQ(1, count);
229     SECKEY_DestroyPublicKeyList(pubkey_list);
230   }
231 }
232
233 TEST_F(ONCCertificateImporterImplTest, AddServerCertificateWithWebTrust) {
234   AddCertificateFromFile("certificate-server.onc", net::SERVER_CERT, NULL);
235
236   SECKEYPrivateKeyList* privkey_list =
237       PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
238   EXPECT_FALSE(privkey_list);
239
240   SECKEYPublicKeyList* pubkey_list =
241       PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
242   EXPECT_FALSE(pubkey_list);
243
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()));
249 }
250
251 TEST_F(ONCCertificateImporterImplTest, AddWebAuthorityCertificateWithWebTrust) {
252   AddCertificateFromFile("certificate-web-authority.onc", net::CA_CERT, NULL);
253
254   SECKEYPrivateKeyList* privkey_list =
255       PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
256   EXPECT_FALSE(privkey_list);
257
258   SECKEYPublicKeyList* pubkey_list =
259       PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
260   EXPECT_FALSE(pubkey_list);
261
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()));
267 }
268
269 TEST_F(ONCCertificateImporterImplTest, AddAuthorityCertificateWithoutWebTrust) {
270   AddCertificateFromFile("certificate-authority.onc", net::CA_CERT, NULL);
271   EXPECT_TRUE(web_trust_certificates_.empty());
272
273   SECKEYPrivateKeyList* privkey_list =
274       PK11_ListPrivKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL, NULL);
275   EXPECT_FALSE(privkey_list);
276
277   SECKEYPublicKeyList* pubkey_list =
278       PK11_ListPublicKeysInSlot(test_nssdb_->GetPrivateSlot().get(), NULL);
279   EXPECT_FALSE(pubkey_list);
280 }
281
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) {}
289
290   net::CertType cert_type;
291   const char* original_file;
292   const char* update_file;
293 };
294
295 class ONCCertificateImporterImplTestWithParam :
296       public ONCCertificateImporterImplTest,
297       public testing::WithParamInterface<CertParam> {
298 };
299
300 TEST_P(ONCCertificateImporterImplTestWithParam, UpdateCertificate) {
301   // First we import a certificate.
302   {
303     SCOPED_TRACE("Import original certificate");
304     AddCertificateFromFile(GetParam().original_file, GetParam().cert_type,
305                            NULL);
306   }
307
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.
310   {
311     SCOPED_TRACE("Import updated certificate");
312     AddCertificateFromFile(GetParam().update_file, GetParam().cert_type, NULL);
313   }
314 }
315
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);
322   }
323 }
324
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")));
338
339 }  // namespace onc
340 }  // namespace chromeos