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/files/file_path.h"
10#include "base/files/file_util.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::Initialize();
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(net::OK,
119              test_nssdb_->ImportFromPKCS12(module.get(),
120                                            pkcs12_data,
121                                            base::string16(),
122                                            false,
123                                            &client_cert_list));
124    ASSERT_TRUE(!client_cert_list.empty());
125    test_client_cert_ = client_cert_list[0];
126
127    int slot_id = -1;
128    test_client_cert_pkcs11_id_ = CertLoader::GetPkcs11IdAndSlotForCert(
129        *test_client_cert_, &slot_id);
130    ASSERT_FALSE(test_client_cert_pkcs11_id_.empty());
131    ASSERT_NE(-1, slot_id);
132    test_client_cert_slot_id_ = base::IntToString(slot_id);
133  }
134
135  void SetupNetworkHandlers() {
136    network_state_handler_.reset(NetworkStateHandler::InitializeForTest());
137    network_cert_migrator_.reset(new NetworkCertMigrator);
138    network_cert_migrator_->Init(network_state_handler_.get());
139  }
140
141  void AddService(const std::string& network_id,
142                  const std::string& type,
143                  const std::string& state) {
144    service_test_->AddService(network_id /* service_path */,
145                              network_id /* guid */,
146                              network_id /* name */,
147                              type,
148                              state,
149                              true /* add_to_visible */);
150
151    // Ensure that the service appears as 'configured', i.e. is associated to a
152    // Shill profile.
153    service_test_->SetServiceProperty(
154        network_id, shill::kProfileProperty, base::StringValue(kProfile));
155  }
156
157  void SetupWifiWithNss() {
158    AddService(kWifiStub, shill::kTypeWifi, shill::kStateOnline);
159    service_test_->SetServiceProperty(kWifiStub,
160                                      shill::kEapCaCertNssProperty,
161                                      base::StringValue(kNSSNickname));
162  }
163
164  void SetupNetworkWithEapCertId(bool wifi, const std::string& cert_id) {
165    std::string type = wifi ? shill::kTypeWifi: shill::kTypeEthernetEap;
166    std::string name = wifi ? kWifiStub : kEthernetEapStub;
167    AddService(name, type, shill::kStateOnline);
168    service_test_->SetServiceProperty(
169        name, shill::kEapCertIdProperty, base::StringValue(cert_id));
170    service_test_->SetServiceProperty(
171        name, shill::kEapKeyIdProperty, base::StringValue(cert_id));
172
173    if (wifi) {
174      service_test_->SetServiceProperty(
175          name,
176          shill::kSecurityProperty,
177          base::StringValue(shill::kSecurity8021x));
178    }
179  }
180
181  void GetEapCertId(bool wifi, std::string* cert_id) {
182    cert_id->clear();
183
184    std::string name = wifi ? kWifiStub : kEthernetEapStub;
185    const base::DictionaryValue* properties =
186        service_test_->GetServiceProperties(name);
187    properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
188                                              cert_id);
189  }
190
191  void SetupVpnWithCertId(bool open_vpn,
192                          const std::string& slot_id,
193                          const std::string& pkcs11_id) {
194    AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle);
195    base::DictionaryValue provider;
196    if (open_vpn) {
197      provider.SetStringWithoutPathExpansion(shill::kTypeProperty,
198                                             shill::kProviderOpenVpn);
199      provider.SetStringWithoutPathExpansion(
200          shill::kOpenVPNClientCertIdProperty, pkcs11_id);
201    } else {
202      provider.SetStringWithoutPathExpansion(shill::kTypeProperty,
203                                             shill::kProviderL2tpIpsec);
204      provider.SetStringWithoutPathExpansion(
205          shill::kL2tpIpsecClientCertSlotProperty, slot_id);
206      provider.SetStringWithoutPathExpansion(
207          shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
208    }
209    service_test_->SetServiceProperty(
210        kVPNStub, shill::kProviderProperty, provider);
211  }
212
213  void GetVpnCertId(bool open_vpn,
214                    std::string* slot_id,
215                    std::string* pkcs11_id) {
216    slot_id->clear();
217    pkcs11_id->clear();
218
219    const base::DictionaryValue* properties =
220        service_test_->GetServiceProperties(kVPNStub);
221    ASSERT_TRUE(properties);
222    const base::DictionaryValue* provider = NULL;
223    properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty,
224                                                  &provider);
225    if (!provider)
226      return;
227    if (open_vpn) {
228      provider->GetStringWithoutPathExpansion(
229          shill::kOpenVPNClientCertIdProperty, pkcs11_id);
230    } else {
231      provider->GetStringWithoutPathExpansion(
232          shill::kL2tpIpsecClientCertSlotProperty, slot_id);
233      provider->GetStringWithoutPathExpansion(
234          shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
235    }
236  }
237
238  void GetEapCACertProperties(std::string* nss_nickname, std::string* ca_pem) {
239    nss_nickname->clear();
240    ca_pem->clear();
241    const base::DictionaryValue* properties =
242        service_test_->GetServiceProperties(kWifiStub);
243    properties->GetStringWithoutPathExpansion(shill::kEapCaCertNssProperty,
244                                              nss_nickname);
245    const base::ListValue* ca_pems = NULL;
246    properties->GetListWithoutPathExpansion(shill::kEapCaCertPemProperty,
247                                            &ca_pems);
248    if (ca_pems && !ca_pems->empty())
249      ca_pems->GetString(0, ca_pem);
250  }
251
252  void SetupVpnWithNss(bool open_vpn) {
253    AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle);
254    base::DictionaryValue provider;
255    const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty
256                                        : shill::kL2tpIpsecCaCertNssProperty;
257    provider.SetStringWithoutPathExpansion(nss_property, kNSSNickname);
258    service_test_->SetServiceProperty(
259        kVPNStub, shill::kProviderProperty, provider);
260  }
261
262  void GetVpnCACertProperties(bool open_vpn,
263                              std::string* nss_nickname,
264                              std::string* ca_pem) {
265    nss_nickname->clear();
266    ca_pem->clear();
267    const base::DictionaryValue* properties =
268        service_test_->GetServiceProperties(kVPNStub);
269    const base::DictionaryValue* provider = NULL;
270    properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty,
271                                                  &provider);
272    if (!provider)
273      return;
274    const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty
275                                        : shill::kL2tpIpsecCaCertNssProperty;
276    provider->GetStringWithoutPathExpansion(nss_property, nss_nickname);
277    const base::ListValue* ca_pems = NULL;
278    const char* pem_property = open_vpn ? shill::kOpenVPNCaCertPemProperty
279                                        : shill::kL2tpIpsecCaCertPemProperty;
280    provider->GetListWithoutPathExpansion(pem_property, &ca_pems);
281    if (ca_pems && !ca_pems->empty())
282      ca_pems->GetString(0, ca_pem);
283  }
284
285  ShillServiceClient::TestInterface* service_test_;
286  scoped_refptr<net::X509Certificate> test_ca_cert_;
287  scoped_refptr<net::X509Certificate> test_client_cert_;
288  std::string test_client_cert_pkcs11_id_;
289  std::string test_client_cert_slot_id_;
290  std::string test_ca_cert_pem_;
291  base::MessageLoop message_loop_;
292
293 private:
294  void CleanupTestCert() {
295    if (test_ca_cert_.get())
296      ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_ca_cert_.get()));
297
298    if (test_client_cert_.get())
299      ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_client_cert_.get()));
300  }
301
302  scoped_ptr<NetworkStateHandler> network_state_handler_;
303  scoped_ptr<NetworkCertMigrator> network_cert_migrator_;
304  crypto::ScopedTestNSSChromeOSUser user_;
305  scoped_ptr<net::NSSCertDatabaseChromeOS> test_nssdb_;
306
307  DISALLOW_COPY_AND_ASSIGN(NetworkCertMigratorTest);
308};
309
310TEST_F(NetworkCertMigratorTest, MigrateNssOnInitialization) {
311  // Add a new network for migration before the handlers are initialized.
312  SetupWifiWithNss();
313  SetupTestCACert();
314  SetupNetworkHandlers();
315
316  base::RunLoop().RunUntilIdle();
317  std::string nss_nickname, ca_pem;
318  GetEapCACertProperties(&nss_nickname, &ca_pem);
319  EXPECT_TRUE(nss_nickname.empty());
320  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
321}
322
323TEST_F(NetworkCertMigratorTest, MigrateNssOnNetworkAppearance) {
324  SetupTestCACert();
325  SetupNetworkHandlers();
326  base::RunLoop().RunUntilIdle();
327
328  // Add a new network for migration after the handlers are initialized.
329  SetupWifiWithNss();
330
331  base::RunLoop().RunUntilIdle();
332  std::string nss_nickname, ca_pem;
333  GetEapCACertProperties(&nss_nickname, &ca_pem);
334  EXPECT_TRUE(nss_nickname.empty());
335  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
336}
337
338TEST_F(NetworkCertMigratorTest, DoNotMigrateNssIfPemSet) {
339  // Add a new network with an already set PEM property.
340  SetupWifiWithNss();
341  base::ListValue ca_pems;
342  ca_pems.AppendString(kFakePEM);
343  service_test_->SetServiceProperty(
344      kWifiStub, shill::kEapCaCertPemProperty, ca_pems);
345
346  SetupTestCACert();
347  SetupNetworkHandlers();
348  base::RunLoop().RunUntilIdle();
349
350  std::string nss_nickname, ca_pem;
351  GetEapCACertProperties(&nss_nickname, &ca_pem);
352  EXPECT_TRUE(nss_nickname.empty());
353  EXPECT_EQ(kFakePEM, ca_pem);
354}
355
356TEST_F(NetworkCertMigratorTest, MigrateNssOpenVpn) {
357  // Add a new network for migration before the handlers are initialized.
358  SetupVpnWithNss(true /* OpenVPN */);
359
360  SetupTestCACert();
361  SetupNetworkHandlers();
362
363  base::RunLoop().RunUntilIdle();
364  std::string nss_nickname, ca_pem;
365  GetVpnCACertProperties(true /* OpenVPN */, &nss_nickname, &ca_pem);
366  EXPECT_TRUE(nss_nickname.empty());
367  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
368}
369
370TEST_F(NetworkCertMigratorTest, MigrateNssIpsecVpn) {
371  // Add a new network for migration before the handlers are initialized.
372  SetupVpnWithNss(false /* not OpenVPN */);
373
374  SetupTestCACert();
375  SetupNetworkHandlers();
376
377  base::RunLoop().RunUntilIdle();
378  std::string nss_nickname, ca_pem;
379  GetVpnCACertProperties(false /* not OpenVPN */, &nss_nickname, &ca_pem);
380  EXPECT_TRUE(nss_nickname.empty());
381  EXPECT_EQ(test_ca_cert_pem_, ca_pem);
382}
383
384TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoMatchingCert) {
385  SetupTestClientCert();
386  SetupNetworkHandlers();
387  base::RunLoop().RunUntilIdle();
388
389  // Add a new network for migration after the handlers are initialized.
390  SetupNetworkWithEapCertId(true /* wifi */, "unknown pkcs11 id");
391
392  base::RunLoop().RunUntilIdle();
393  // Since the PKCS11 ID is unknown, the certificate configuration will be
394  // cleared.
395  std::string cert_id;
396  GetEapCertId(true /* wifi */, &cert_id);
397  EXPECT_EQ(std::string(), cert_id);
398}
399
400TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoSlotId) {
401  SetupTestClientCert();
402  SetupNetworkHandlers();
403  base::RunLoop().RunUntilIdle();
404
405  // Add a new network for migration after the handlers are initialized.
406  SetupNetworkWithEapCertId(true /* wifi */, test_client_cert_pkcs11_id_);
407
408  base::RunLoop().RunUntilIdle();
409
410  std::string cert_id;
411  GetEapCertId(true /* wifi */, &cert_id);
412  std::string expected_cert_id =
413      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
414  EXPECT_EQ(expected_cert_id, cert_id);
415}
416
417TEST_F(NetworkCertMigratorTest, MigrateWifiEapCertIdWrongSlotId) {
418  SetupTestClientCert();
419  SetupNetworkHandlers();
420  base::RunLoop().RunUntilIdle();
421
422  // Add a new network for migration after the handlers are initialized.
423  SetupNetworkWithEapCertId(true /* wifi */,
424                            "123:" + test_client_cert_pkcs11_id_);
425
426  base::RunLoop().RunUntilIdle();
427
428  std::string cert_id;
429  GetEapCertId(true /* wifi */, &cert_id);
430  std::string expected_cert_id =
431      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
432  EXPECT_EQ(expected_cert_id, cert_id);
433}
434
435TEST_F(NetworkCertMigratorTest, DoNotChangeEapCertIdWithCorrectSlotId) {
436  SetupTestClientCert();
437  SetupNetworkHandlers();
438  base::RunLoop().RunUntilIdle();
439
440  std::string expected_cert_id =
441      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
442
443  // Add a new network for migration after the handlers are initialized.
444  SetupNetworkWithEapCertId(true /* wifi */, expected_cert_id);
445
446  base::RunLoop().RunUntilIdle();
447
448  std::string cert_id;
449  GetEapCertId(true /* wifi */, &cert_id);
450  EXPECT_EQ(expected_cert_id, cert_id);
451}
452
453TEST_F(NetworkCertMigratorTest, IgnoreOpenVPNCertId) {
454  SetupTestClientCert();
455  SetupNetworkHandlers();
456  base::RunLoop().RunUntilIdle();
457
458  const char kPkcs11Id[] = "any slot id";
459
460  // Add a new network for migration after the handlers are initialized.
461  SetupVpnWithCertId(
462      true /* OpenVPN */, std::string() /* no slot id */, kPkcs11Id);
463
464  base::RunLoop().RunUntilIdle();
465
466  std::string pkcs11_id;
467  std::string unused_slot_id;
468  GetVpnCertId(true /* OpenVPN */, &unused_slot_id, &pkcs11_id);
469  EXPECT_EQ(kPkcs11Id, pkcs11_id);
470}
471
472TEST_F(NetworkCertMigratorTest, MigrateEthernetEapCertIdWrongSlotId) {
473  SetupTestClientCert();
474  SetupNetworkHandlers();
475  base::RunLoop().RunUntilIdle();
476
477  // Add a new network for migration after the handlers are initialized.
478  SetupNetworkWithEapCertId(
479      false /* ethernet */, "123:" + test_client_cert_pkcs11_id_);
480
481  base::RunLoop().RunUntilIdle();
482
483  std::string cert_id;
484  GetEapCertId(false /* ethernet */, &cert_id);
485  std::string expected_cert_id =
486      test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_;
487  EXPECT_EQ(expected_cert_id, cert_id);
488}
489
490TEST_F(NetworkCertMigratorTest, MigrateIpsecCertIdWrongSlotId) {
491  SetupTestClientCert();
492  SetupNetworkHandlers();
493  base::RunLoop().RunUntilIdle();
494
495  // Add a new network for migration after the handlers are initialized.
496  SetupVpnWithCertId(false /* IPsec */, "123", test_client_cert_pkcs11_id_);
497
498  base::RunLoop().RunUntilIdle();
499
500  std::string pkcs11_id;
501  std::string slot_id;
502  GetVpnCertId(false /* IPsec */, &slot_id, &pkcs11_id);
503  EXPECT_EQ(test_client_cert_pkcs11_id_, pkcs11_id);
504  EXPECT_EQ(test_client_cert_slot_id_, slot_id);
505}
506
507}  // namespace chromeos
508