network_cert_migrator_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/network_cert_migrator.h"
6
7#include <cert.h>
8
9#include "base/file_util.h"
10#include "base/files/file_path.h"
11#include "base/run_loop.h"
12#include "base/strings/string_number_conversions.h"
13#include "chromeos/cert_loader.h"
14#include "chromeos/dbus/dbus_thread_manager.h"
15#include "chromeos/dbus/shill_profile_client.h"
16#include "chromeos/dbus/shill_service_client.h"
17#include "chromeos/network/network_state_handler.h"
18#include "chromeos/tpm_token_loader.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/base/net_errors.h"
23#include "net/base/test_data_directory.h"
24#include "net/cert/nss_cert_database_chromeos.h"
25#include "net/cert/x509_certificate.h"
26#include "net/test/cert_test_util.h"
27#include "testing/gtest/include/gtest/gtest.h"
28#include "third_party/cros_system_api/dbus/service_constants.h"
29
30namespace chromeos {
31
32namespace {
33
34const char* kWifiStub = "wifi_stub";
35const char* kEthernetEapStub = "ethernet_eap_stub";
36const char* kVPNStub = "vpn_stub";
37const char* kNSSNickname = "nss_nickname";
38const char* kFakePEM = "pem";
39const char* kProfile = "/profile/profile1";
40
41}  // namespace
42
43class NetworkCertMigratorTest : public testing::Test {
44 public:
45  NetworkCertMigratorTest() : service_test_(NULL),
46                              user_("user_hash") {
47  }
48  virtual ~NetworkCertMigratorTest() {}
49
50  virtual void SetUp() OVERRIDE {
51    // Initialize NSS db for the user.
52    ASSERT_TRUE(user_.constructed_successfully());
53    user_.FinishInit();
54    test_nssdb_.reset(new net::NSSCertDatabaseChromeOS(
55        crypto::GetPublicSlotForChromeOSUser(user_.username_hash()),
56        crypto::GetPrivateSlotForChromeOSUser(
57            user_.username_hash(),
58            base::Callback<void(crypto::ScopedPK11Slot)>())));
59    test_nssdb_->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy());
60
61    DBusThreadManager::InitializeWithStub();
62    service_test_ =
63        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
64    DBusThreadManager::Get()
65        ->GetShillProfileClient()
66        ->GetTestInterface()
67        ->AddProfile(kProfile, "" /* userhash */);
68    base::RunLoop().RunUntilIdle();
69    service_test_->ClearServices();
70    base::RunLoop().RunUntilIdle();
71
72    CertLoader::Initialize();
73    CertLoader* cert_loader_ = CertLoader::Get();
74    cert_loader_->StartWithNSSDB(test_nssdb_.get());
75  }
76
77  virtual void TearDown() OVERRIDE {
78    network_cert_migrator_.reset();
79    network_state_handler_.reset();
80    CertLoader::Shutdown();
81    DBusThreadManager::Shutdown();
82    CleanupTestCert();
83  }
84
85 protected:
86  void SetupTestCACert() {
87    scoped_refptr<net::X509Certificate> cert_wo_nickname =
88        net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
89                                           "eku-test-root.pem",
90                                           net::X509Certificate::FORMAT_AUTO)
91            .back();
92    net::X509Certificate::GetPEMEncoded(cert_wo_nickname->os_cert_handle(),
93                                        &test_ca_cert_pem_);
94    std::string der_encoded;
95    net::X509Certificate::GetDEREncoded(cert_wo_nickname->os_cert_handle(),
96                                        &der_encoded);
97    cert_wo_nickname = NULL;
98
99    test_ca_cert_ = net::X509Certificate::CreateFromBytesWithNickname(
100        der_encoded.data(), der_encoded.size(), kNSSNickname);
101    net::CertificateList cert_list;
102    cert_list.push_back(test_ca_cert_);
103    net::NSSCertDatabase::ImportCertFailureList failures;
104    EXPECT_TRUE(test_nssdb_->ImportCACerts(
105        cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures));
106    ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error);
107  }
108
109  void SetupTestClientCert() {
110    std::string pkcs12_data;
111    ASSERT_TRUE(base::ReadFileToString(
112        net::GetTestCertsDirectory().Append("websocket_client_cert.p12"),
113        &pkcs12_data));
114
115    net::CertificateList client_cert_list;
116    scoped_refptr<net::CryptoModule> module(net::CryptoModule::CreateFromHandle(
117        test_nssdb_->GetPrivateSlot().get()));
118    ASSERT_EQ(
119        net::OK,
120        test_nssdb_->ImportFromPKCS12(
121            module, pkcs12_data, base::string16(), false, &client_cert_list));
122    ASSERT_TRUE(!client_cert_list.empty());
123    test_client_cert_ = client_cert_list[0];
124
125    int slot_id = -1;
126    test_client_cert_pkcs11_id_ = CertLoader::GetPkcs11IdAndSlotForCert(
127        *test_client_cert_, &slot_id);
128    ASSERT_FALSE(test_client_cert_pkcs11_id_.empty());
129    ASSERT_NE(-1, slot_id);
130    test_client_cert_slot_id_ = base::IntToString(slot_id);
131  }
132
133  void SetupNetworkHandlers() {
134    network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
135    network_cert_migrator_.reset(new NetworkCertMigrator);
136    network_cert_migrator_->Init(network_state_handler_.get());
137  }
138
139  void AddService(const std::string& network_id,
140                  const std::string& type,
141                  const std::string& state) {
142    service_test_->AddService(network_id /* service_path */,
143                              network_id /* guid */,
144                              network_id /* name */,
145                              type,
146                              state,
147                              true /* add_to_visible */);
148
149    // Ensure that the service appears as 'configured', i.e. is associated to a
150    // Shill profile.
151    service_test_->SetServiceProperty(
152        network_id, shill::kProfileProperty, base::StringValue(kProfile));
153  }
154
155  void SetupWifiWithNss() {
156    AddService(kWifiStub, shill::kTypeWifi, shill::kStateOnline);
157    service_test_->SetServiceProperty(kWifiStub,
158                                      shill::kEapCaCertNssProperty,
159                                      base::StringValue(kNSSNickname));
160  }
161
162  void SetupNetworkWithEapCertId(bool wifi, const std::string& cert_id) {
163    std::string type = wifi ? shill::kTypeWifi: shill::kTypeEthernetEap;
164    std::string name = wifi ? kWifiStub : kEthernetEapStub;
165    AddService(name, type, shill::kStateOnline);
166    service_test_->SetServiceProperty(
167        name, shill::kEapCertIdProperty, base::StringValue(cert_id));
168    service_test_->SetServiceProperty(
169        name, shill::kEapKeyIdProperty, base::StringValue(cert_id));
170
171    if (wifi) {
172      service_test_->SetServiceProperty(
173          name,
174          shill::kSecurityProperty,
175          base::StringValue(shill::kSecurity8021x));
176    }
177  }
178
179  void GetEapCertId(bool wifi, std::string* cert_id) {
180    cert_id->clear();
181
182    std::string name = wifi ? kWifiStub : kEthernetEapStub;
183    const base::DictionaryValue* properties =
184        service_test_->GetServiceProperties(name);
185    properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
186                                              cert_id);
187  }
188
189  void SetupVpnWithCertId(bool open_vpn,
190                          const std::string& slot_id,
191                          const std::string& pkcs11_id) {
192    AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle);
193    base::DictionaryValue provider;
194    if (open_vpn) {
195      provider.SetStringWithoutPathExpansion(shill::kTypeProperty,
196                                             shill::kProviderOpenVpn);
197      provider.SetStringWithoutPathExpansion(
198          shill::kOpenVPNClientCertIdProperty, pkcs11_id);
199    } else {
200      provider.SetStringWithoutPathExpansion(shill::kTypeProperty,
201                                             shill::kProviderL2tpIpsec);
202      provider.SetStringWithoutPathExpansion(
203          shill::kL2tpIpsecClientCertSlotProperty, slot_id);
204      provider.SetStringWithoutPathExpansion(
205          shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
206    }
207    service_test_->SetServiceProperty(
208        kVPNStub, shill::kProviderProperty, provider);
209  }
210
211  void GetVpnCertId(bool open_vpn,
212                    std::string* slot_id,
213                    std::string* pkcs11_id) {
214    slot_id->clear();
215    pkcs11_id->clear();
216
217    const base::DictionaryValue* properties =
218        service_test_->GetServiceProperties(kVPNStub);
219    ASSERT_TRUE(properties);
220    const base::DictionaryValue* provider = NULL;
221    properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty,
222                                                  &provider);
223    if (!provider)
224      return;
225    if (open_vpn) {
226      provider->GetStringWithoutPathExpansion(
227          shill::kOpenVPNClientCertIdProperty, pkcs11_id);
228    } else {
229      provider->GetStringWithoutPathExpansion(
230          shill::kL2tpIpsecClientCertSlotProperty, slot_id);
231      provider->GetStringWithoutPathExpansion(
232          shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
233    }
234  }
235
236  void GetEapCACertProperties(std::string* nss_nickname, std::string* ca_pem) {
237    nss_nickname->clear();
238    ca_pem->clear();
239    const base::DictionaryValue* properties =
240        service_test_->GetServiceProperties(kWifiStub);
241    properties->GetStringWithoutPathExpansion(shill::kEapCaCertNssProperty,
242                                              nss_nickname);
243    const base::ListValue* ca_pems = NULL;
244    properties->GetListWithoutPathExpansion(shill::kEapCaCertPemProperty,
245                                            &ca_pems);
246    if (ca_pems && !ca_pems->empty())
247      ca_pems->GetString(0, ca_pem);
248  }
249
250  void SetupVpnWithNss(bool open_vpn) {
251    AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle);
252    base::DictionaryValue provider;
253    const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty
254                                        : shill::kL2tpIpsecCaCertNssProperty;
255    provider.SetStringWithoutPathExpansion(nss_property, kNSSNickname);
256    service_test_->SetServiceProperty(
257        kVPNStub, shill::kProviderProperty, provider);
258  }
259
260  void GetVpnCACertProperties(bool open_vpn,
261                              std::string* nss_nickname,
262                              std::string* ca_pem) {
263    nss_nickname->clear();
264    ca_pem->clear();
265    const base::DictionaryValue* properties =
266        service_test_->GetServiceProperties(kVPNStub);
267    const base::DictionaryValue* provider = NULL;
268    properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty,
269                                                  &provider);
270    if (!provider)
271      return;
272    const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty
273                                        : shill::kL2tpIpsecCaCertNssProperty;
274    provider->GetStringWithoutPathExpansion(nss_property, nss_nickname);
275    const base::ListValue* ca_pems = NULL;
276    const char* pem_property = open_vpn ? shill::kOpenVPNCaCertPemProperty
277                                        : shill::kL2tpIpsecCaCertPemProperty;
278    provider->GetListWithoutPathExpansion(pem_property, &ca_pems);
279    if (ca_pems && !ca_pems->empty())
280      ca_pems->GetString(0, ca_pem);
281  }
282
283  ShillServiceClient::TestInterface* service_test_;
284  scoped_refptr<net::X509Certificate> test_ca_cert_;
285  scoped_refptr<net::X509Certificate> test_client_cert_;
286  std::string test_client_cert_pkcs11_id_;
287  std::string test_client_cert_slot_id_;
288  std::string test_ca_cert_pem_;
289  base::MessageLoop message_loop_;
290
291 private:
292  void CleanupTestCert() {
293    if (test_ca_cert_)
294      ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_ca_cert_.get()));
295
296    if (test_client_cert_)
297      ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_client_cert_.get()));
298  }
299
300  scoped_ptr<NetworkStateHandler> network_state_handler_;
301  scoped_ptr<NetworkCertMigrator> network_cert_migrator_;
302  crypto::ScopedTestNSSChromeOSUser user_;
303  scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
304
305  DISALLOW_COPY_AND_ASSIGN(NetworkCertMigratorTest);
306};
307
308TEST_F(NetworkCertMigratorTest, MigrateNssOnInitialization) {
309  // Add a new network for migration before the handlers are initialized.
310  SetupWifiWithNss();
311  SetupTestCACert();
312  SetupNetworkHandlers();
313
314  base::RunLoop().RunUntilIdle();
315  std::string nss_nickname, ca_pem;
316  GetEapCACertProperties(&nss_nickname, &ca_pem);
317  EXPECT_TRUE(nss_nickname.empty());
318  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
319}
320
321TEST_F(NetworkCertMigratorTest, MigrateNssOnNetworkAppearance) {
322  SetupTestCACert();
323  SetupNetworkHandlers();
324  base::RunLoop().RunUntilIdle();
325
326  // Add a new network for migration after the handlers are initialized.
327  SetupWifiWithNss();
328
329  base::RunLoop().RunUntilIdle();
330  std::string nss_nickname, ca_pem;
331  GetEapCACertProperties(&nss_nickname, &ca_pem);
332  EXPECT_TRUE(nss_nickname.empty());
333  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
334}
335
336TEST_F(NetworkCertMigratorTest, DoNotMigrateNssIfPemSet) {
337  // Add a new network with an already set PEM property.
338  SetupWifiWithNss();
339  base::ListValue ca_pems;
340  ca_pems.AppendString(kFakePEM);
341  service_test_->SetServiceProperty(
342      kWifiStub, shill::kEapCaCertPemProperty, ca_pems);
343
344  SetupTestCACert();
345  SetupNetworkHandlers();
346  base::RunLoop().RunUntilIdle();
347
348  std::string nss_nickname, ca_pem;
349  GetEapCACertProperties(&nss_nickname, &ca_pem);
350  EXPECT_TRUE(nss_nickname.empty());
351  EXPECT_EQ(kFakePEM, ca_pem);
352}
353
354TEST_F(NetworkCertMigratorTest, MigrateNssOpenVpn) {
355  // Add a new network for migration before the handlers are initialized.
356  SetupVpnWithNss(true /* OpenVPN */);
357
358  SetupTestCACert();
359  SetupNetworkHandlers();
360
361  base::RunLoop().RunUntilIdle();
362  std::string nss_nickname, ca_pem;
363  GetVpnCACertProperties(true /* OpenVPN */, &nss_nickname, &ca_pem);
364  EXPECT_TRUE(nss_nickname.empty());
365  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
366}
367
368TEST_F(NetworkCertMigratorTest, MigrateNssIpsecVpn) {
369  // Add a new network for migration before the handlers are initialized.
370  SetupVpnWithNss(false /* not OpenVPN */);
371
372  SetupTestCACert();
373  SetupNetworkHandlers();
374
375  base::RunLoop().RunUntilIdle();
376  std::string nss_nickname, ca_pem;
377  GetVpnCACertProperties(false /* not OpenVPN */, &nss_nickname, &ca_pem);
378  EXPECT_TRUE(nss_nickname.empty());
379  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
380}
381
382TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoMatchingCert) {
383  SetupTestClientCert();
384  SetupNetworkHandlers();
385  base::RunLoop().RunUntilIdle();
386
387  // Add a new network for migration after the handlers are initialized.
388  SetupNetworkWithEapCertId(true /* wifi */, "unknown pkcs11 id");
389
390  base::RunLoop().RunUntilIdle();
391  // Since the PKCS11 ID is unknown, the certificate configuration will be
392  // cleared.
393  std::string cert_id;
394  GetEapCertId(true /* wifi */, &cert_id);
395  EXPECT_EQ(std::string(), cert_id);
396}
397
398TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoSlotId) {
399  SetupTestClientCert();
400  SetupNetworkHandlers();
401  base::RunLoop().RunUntilIdle();
402
403  // Add a new network for migration after the handlers are initialized.
404  SetupNetworkWithEapCertId(true /* wifi */, test_client_cert_pkcs11_id_);
405
406  base::RunLoop().RunUntilIdle();
407
408  std::string cert_id;
409  GetEapCertId(true /* wifi */, &cert_id);
410  std::string expected_cert_id =
411      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
412  EXPECT_EQ(expected_cert_id, cert_id);
413}
414
415TEST_F(NetworkCertMigratorTest, MigrateWifiEapCertIdWrongSlotId) {
416  SetupTestClientCert();
417  SetupNetworkHandlers();
418  base::RunLoop().RunUntilIdle();
419
420  // Add a new network for migration after the handlers are initialized.
421  SetupNetworkWithEapCertId(true /* wifi */,
422                            "123:" + test_client_cert_pkcs11_id_);
423
424  base::RunLoop().RunUntilIdle();
425
426  std::string cert_id;
427  GetEapCertId(true /* wifi */, &cert_id);
428  std::string expected_cert_id =
429      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
430  EXPECT_EQ(expected_cert_id, cert_id);
431}
432
433TEST_F(NetworkCertMigratorTest, DoNotChangeEapCertIdWithCorrectSlotId) {
434  SetupTestClientCert();
435  SetupNetworkHandlers();
436  base::RunLoop().RunUntilIdle();
437
438  std::string expected_cert_id =
439      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
440
441  // Add a new network for migration after the handlers are initialized.
442  SetupNetworkWithEapCertId(true /* wifi */, expected_cert_id);
443
444  base::RunLoop().RunUntilIdle();
445
446  std::string cert_id;
447  GetEapCertId(true /* wifi */, &cert_id);
448  EXPECT_EQ(expected_cert_id, cert_id);
449}
450
451TEST_F(NetworkCertMigratorTest, IgnoreOpenVPNCertId) {
452  SetupTestClientCert();
453  SetupNetworkHandlers();
454  base::RunLoop().RunUntilIdle();
455
456  const char kPkcs11Id[] = "any slot id";
457
458  // Add a new network for migration after the handlers are initialized.
459  SetupVpnWithCertId(
460      true /* OpenVPN */, std::string() /* no slot id */, kPkcs11Id);
461
462  base::RunLoop().RunUntilIdle();
463
464  std::string pkcs11_id;
465  std::string unused_slot_id;
466  GetVpnCertId(true /* OpenVPN */, &unused_slot_id, &pkcs11_id);
467  EXPECT_EQ(kPkcs11Id, pkcs11_id);
468}
469
470TEST_F(NetworkCertMigratorTest, MigrateEthernetEapCertIdWrongSlotId) {
471  SetupTestClientCert();
472  SetupNetworkHandlers();
473  base::RunLoop().RunUntilIdle();
474
475  // Add a new network for migration after the handlers are initialized.
476  SetupNetworkWithEapCertId(
477      false /* ethernet */, "123:" + test_client_cert_pkcs11_id_);
478
479  base::RunLoop().RunUntilIdle();
480
481  std::string cert_id;
482  GetEapCertId(false /* ethernet */, &cert_id);
483  std::string expected_cert_id =
484      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
485  EXPECT_EQ(expected_cert_id, cert_id);
486}
487
488TEST_F(NetworkCertMigratorTest, MigrateIpsecCertIdWrongSlotId) {
489  SetupTestClientCert();
490  SetupNetworkHandlers();
491  base::RunLoop().RunUntilIdle();
492
493  // Add a new network for migration after the handlers are initialized.
494  SetupVpnWithCertId(false /* IPsec */, "123", test_client_cert_pkcs11_id_);
495
496  base::RunLoop().RunUntilIdle();
497
498  std::string pkcs11_id;
499  std::string slot_id;
500  GetVpnCertId(false /* IPsec */, &slot_id, &pkcs11_id);
501  EXPECT_EQ(test_client_cert_pkcs11_id_, pkcs11_id);
502  EXPECT_EQ(test_client_cert_slot_id_, slot_id);
503}
504
505}  // namespace chromeos
506