onc_certificate_importer_impl_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/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" 24 25namespace chromeos { 26namespace onc { 27 28#if defined(USE_NSS) 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 33#endif 34 35net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) { 36 CERTCertTrust trust = {0}; 37 CERT_GetCertTrust(cert, &trust); 38 39 unsigned all_flags = trust.sslFlags | trust.emailFlags | 40 trust.objectSigningFlags; 41 42 if (cert->nickname && (all_flags & CERTDB_USER)) 43 return net::USER_CERT; 44 if ((all_flags & CERTDB_VALID_CA) || CERT_IsCACert(cert, NULL)) 45 return net::CA_CERT; 46 // TODO(mattm): http://crbug.com/128633. 47 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) 48 return net::SERVER_CERT; 49 return net::OTHER_CERT; 50} 51#else 52net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) { 53 NOTIMPLEMENTED(); 54 return net::OTHER_CERT; 55} 56#endif // USE_NSS 57 58class ONCCertificateImporterImplTest : public testing::Test { 59 public: 60 virtual void SetUp() { 61 ASSERT_TRUE(test_nssdb_.is_open()); 62 63 slot_ = net::NSSCertDatabase::GetInstance()->GetPublicModule(); 64 65 // Don't run the test if the setup failed. 66 ASSERT_TRUE(slot_->os_module_handle()); 67 68 // Test db should be empty at start of test. 69 EXPECT_EQ(0ul, ListCertsInSlot().size()); 70 } 71 72 virtual void TearDown() { 73 EXPECT_TRUE(CleanupSlotContents()); 74 EXPECT_EQ(0ul, ListCertsInSlot().size()); 75 } 76 77 virtual ~ONCCertificateImporterImplTest() {} 78 79 protected: 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, 86 &certificates_value); 87 certificates_value.release()->GetAsList(&certificates); 88 onc_certificates_.reset(certificates); 89 90 web_trust_certificates_.clear(); 91 imported_server_and_ca_certs_.clear(); 92 CertificateImporterImpl importer; 93 EXPECT_EQ( 94 expected_success, 95 importer.ParseAndStoreCertificates(true, // allow web trust 96 *certificates, 97 &web_trust_certificates_, 98 &imported_server_and_ca_certs_)); 99 100 result_list_.clear(); 101 result_list_ = ListCertsInSlot(); 102 } 103 104 void AddCertificateFromFile(std::string filename, 105 net::CertType expected_type, 106 std::string* guid) { 107 std::string guid_temporary; 108 if (!guid) 109 guid = &guid_temporary; 110 111 AddCertificatesFromFile(filename, true); 112 ASSERT_EQ(1ul, result_list_.size()); 113 EXPECT_EQ(expected_type, GetCertType(result_list_[0]->os_cert_handle())); 114 115 base::DictionaryValue* certificate = NULL; 116 onc_certificates_->GetDictionary(0, &certificate); 117 certificate->GetStringWithoutPathExpansion(certificate::kGUID, guid); 118 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( 122 result_list_[0])); 123 } else { // net::USER_CERT 124 EXPECT_TRUE(imported_server_and_ca_certs_.empty()); 125 CertificateImporterImpl::ListCertsWithNickname(*guid, &result_list_); 126 } 127 } 128 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_; 134 135 private: 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())); 144 } 145 CERT_DestroyCertList(cert_list); 146 147 // Sort the result so that test comparisons can be deterministic. 148 std::sort(result.begin(), result.end(), net::X509Certificate::LessThan()); 149 return result; 150 } 151 152 bool CleanupSlotContents() { 153 bool ok = true; 154 net::CertificateList certs = ListCertsInSlot(); 155 for (size_t i = 0; i < certs.size(); ++i) { 156 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(certs[i] 157 .get())) 158 ok = false; 159 } 160 return ok; 161 } 162 163 crypto::ScopedTestNSSDB test_nssdb_; 164}; 165 166TEST_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()); 170} 171 172TEST_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()); 177} 178 179TEST_F(ONCCertificateImporterImplTest, AddClientCertificate) { 180 std::string guid; 181 AddCertificateFromFile("certificate-client.onc", net::USER_CERT, &guid); 182 EXPECT_TRUE(web_trust_certificates_.empty()); 183 184 SECKEYPrivateKeyList* privkey_list = 185 PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); 186 EXPECT_TRUE(privkey_list); 187 if (privkey_list) { 188 SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(privkey_list); 189 int count = 0; 190 while (!PRIVKEY_LIST_END(node, privkey_list)) { 191 char* name = PK11_GetPrivateKeyNickname(node->key); 192 EXPECT_STREQ(guid.c_str(), name); 193 PORT_Free(name); 194 count++; 195 node = PRIVKEY_LIST_NEXT(node); 196 } 197 EXPECT_EQ(1, count); 198 SECKEY_DestroyPrivateKeyList(privkey_list); 199 } 200 201 SECKEYPublicKeyList* pubkey_list = 202 PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); 203 EXPECT_TRUE(pubkey_list); 204 if (pubkey_list) { 205 SECKEYPublicKeyListNode* node = PUBKEY_LIST_HEAD(pubkey_list); 206 int count = 0; 207 while (!PUBKEY_LIST_END(node, pubkey_list)) { 208 count++; 209 node = PUBKEY_LIST_NEXT(node); 210 } 211 EXPECT_EQ(1, count); 212 SECKEY_DestroyPublicKeyList(pubkey_list); 213 } 214} 215 216TEST_F(ONCCertificateImporterImplTest, AddServerCertificateWithWebTrust) { 217 AddCertificateFromFile("certificate-server.onc", net::SERVER_CERT, NULL); 218 219 SECKEYPrivateKeyList* privkey_list = 220 PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); 221 EXPECT_FALSE(privkey_list); 222 223 SECKEYPublicKeyList* pubkey_list = 224 PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); 225 EXPECT_FALSE(pubkey_list); 226 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())); 231} 232 233TEST_F(ONCCertificateImporterImplTest, AddWebAuthorityCertificateWithWebTrust) { 234 AddCertificateFromFile("certificate-web-authority.onc", net::CA_CERT, NULL); 235 236 SECKEYPrivateKeyList* privkey_list = 237 PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); 238 EXPECT_FALSE(privkey_list); 239 240 SECKEYPublicKeyList* pubkey_list = 241 PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); 242 EXPECT_FALSE(pubkey_list); 243 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())); 248} 249 250TEST_F(ONCCertificateImporterImplTest, AddAuthorityCertificateWithoutWebTrust) { 251 AddCertificateFromFile("certificate-authority.onc", net::CA_CERT, NULL); 252 EXPECT_TRUE(web_trust_certificates_.empty()); 253 254 SECKEYPrivateKeyList* privkey_list = 255 PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); 256 EXPECT_FALSE(privkey_list); 257 258 SECKEYPublicKeyList* pubkey_list = 259 PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); 260 EXPECT_FALSE(pubkey_list); 261} 262 263struct CertParam { 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) {} 270 271 net::CertType cert_type; 272 const char* original_file; 273 const char* update_file; 274}; 275 276class ONCCertificateImporterImplTestWithParam : 277 public ONCCertificateImporterImplTest, 278 public testing::WithParamInterface<CertParam> { 279}; 280 281TEST_P(ONCCertificateImporterImplTestWithParam, UpdateCertificate) { 282 // First we import a certificate. 283 { 284 SCOPED_TRACE("Import original certificate"); 285 AddCertificateFromFile(GetParam().original_file, GetParam().cert_type, 286 NULL); 287 } 288 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. 291 { 292 SCOPED_TRACE("Import updated certificate"); 293 AddCertificateFromFile(GetParam().update_file, GetParam().cert_type, NULL); 294 } 295} 296 297TEST_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, 302 NULL); 303 } 304} 305 306INSTANTIATE_TEST_CASE_P( 307 ONCCertificateImporterImplTestWithParam, 308 ONCCertificateImporterImplTestWithParam, 309 ::testing::Values( 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"))); 319 320} // namespace onc 321} // namespace chromeos 322