// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/network/network_cert_migrator.h" #include #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "chromeos/cert_loader.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_profile_client.h" #include "chromeos/dbus/shill_service_client.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/tpm/tpm_token_loader.h" #include "crypto/nss_util_internal.h" #include "crypto/scoped_test_nss_chromeos_user.h" #include "net/base/crypto_module.h" #include "net/base/net_errors.h" #include "net/base/test_data_directory.h" #include "net/cert/nss_cert_database_chromeos.h" #include "net/cert/x509_certificate.h" #include "net/test/cert_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" // http://crbug.com/418369 #ifdef NDEBUG namespace chromeos { namespace { const char* kWifiStub = "wifi_stub"; const char* kEthernetEapStub = "ethernet_eap_stub"; const char* kVPNStub = "vpn_stub"; const char* kNSSNickname = "nss_nickname"; const char* kFakePEM = "pem"; const char* kProfile = "/profile/profile1"; } // namespace class NetworkCertMigratorTest : public testing::Test { public: NetworkCertMigratorTest() : service_test_(NULL), user_("user_hash") { } virtual ~NetworkCertMigratorTest() {} virtual void SetUp() override { // Initialize NSS db for the user. ASSERT_TRUE(user_.constructed_successfully()); user_.FinishInit(); test_nssdb_.reset(new net::NSSCertDatabaseChromeOS( crypto::GetPublicSlotForChromeOSUser(user_.username_hash()), crypto::GetPrivateSlotForChromeOSUser( user_.username_hash(), base::Callback()))); test_nssdb_->SetSlowTaskRunnerForTest(message_loop_.message_loop_proxy()); DBusThreadManager::Initialize(); service_test_ = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); DBusThreadManager::Get() ->GetShillProfileClient() ->GetTestInterface() ->AddProfile(kProfile, "" /* userhash */); base::RunLoop().RunUntilIdle(); service_test_->ClearServices(); base::RunLoop().RunUntilIdle(); CertLoader::Initialize(); CertLoader* cert_loader_ = CertLoader::Get(); cert_loader_->StartWithNSSDB(test_nssdb_.get()); } virtual void TearDown() override { network_cert_migrator_.reset(); network_state_handler_.reset(); CertLoader::Shutdown(); DBusThreadManager::Shutdown(); CleanupTestCert(); } protected: void SetupTestCACert() { scoped_refptr cert_wo_nickname = net::CreateCertificateListFromFile(net::GetTestCertsDirectory(), "eku-test-root.pem", net::X509Certificate::FORMAT_AUTO) .back(); net::X509Certificate::GetPEMEncoded(cert_wo_nickname->os_cert_handle(), &test_ca_cert_pem_); std::string der_encoded; net::X509Certificate::GetDEREncoded(cert_wo_nickname->os_cert_handle(), &der_encoded); cert_wo_nickname = NULL; test_ca_cert_ = net::X509Certificate::CreateFromBytesWithNickname( der_encoded.data(), der_encoded.size(), kNSSNickname); net::CertificateList cert_list; cert_list.push_back(test_ca_cert_); net::NSSCertDatabase::ImportCertFailureList failures; EXPECT_TRUE(test_nssdb_->ImportCACerts( cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures)); ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error); } void SetupTestClientCert() { std::string pkcs12_data; ASSERT_TRUE(base::ReadFileToString( net::GetTestCertsDirectory().Append("websocket_client_cert.p12"), &pkcs12_data)); net::CertificateList client_cert_list; scoped_refptr module(net::CryptoModule::CreateFromHandle( test_nssdb_->GetPrivateSlot().get())); ASSERT_EQ(net::OK, test_nssdb_->ImportFromPKCS12(module.get(), pkcs12_data, base::string16(), false, &client_cert_list)); ASSERT_TRUE(!client_cert_list.empty()); test_client_cert_ = client_cert_list[0]; int slot_id = -1; test_client_cert_pkcs11_id_ = CertLoader::GetPkcs11IdAndSlotForCert( *test_client_cert_, &slot_id); ASSERT_FALSE(test_client_cert_pkcs11_id_.empty()); ASSERT_NE(-1, slot_id); test_client_cert_slot_id_ = base::IntToString(slot_id); } void SetupNetworkHandlers() { network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); network_cert_migrator_.reset(new NetworkCertMigrator); network_cert_migrator_->Init(network_state_handler_.get()); } void AddService(const std::string& network_id, const std::string& type, const std::string& state) { service_test_->AddService(network_id /* service_path */, network_id /* guid */, network_id /* name */, type, state, true /* add_to_visible */); // Ensure that the service appears as 'configured', i.e. is associated to a // Shill profile. service_test_->SetServiceProperty( network_id, shill::kProfileProperty, base::StringValue(kProfile)); } void SetupWifiWithNss() { AddService(kWifiStub, shill::kTypeWifi, shill::kStateOnline); service_test_->SetServiceProperty(kWifiStub, shill::kEapCaCertNssProperty, base::StringValue(kNSSNickname)); } void SetupNetworkWithEapCertId(bool wifi, const std::string& cert_id) { std::string type = wifi ? shill::kTypeWifi: shill::kTypeEthernetEap; std::string name = wifi ? kWifiStub : kEthernetEapStub; AddService(name, type, shill::kStateOnline); service_test_->SetServiceProperty( name, shill::kEapCertIdProperty, base::StringValue(cert_id)); service_test_->SetServiceProperty( name, shill::kEapKeyIdProperty, base::StringValue(cert_id)); if (wifi) { service_test_->SetServiceProperty( name, shill::kSecurityProperty, base::StringValue(shill::kSecurity8021x)); } } void GetEapCertId(bool wifi, std::string* cert_id) { cert_id->clear(); std::string name = wifi ? kWifiStub : kEthernetEapStub; const base::DictionaryValue* properties = service_test_->GetServiceProperties(name); properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty, cert_id); } void SetupVpnWithCertId(bool open_vpn, const std::string& slot_id, const std::string& pkcs11_id) { AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle); base::DictionaryValue provider; if (open_vpn) { provider.SetStringWithoutPathExpansion(shill::kTypeProperty, shill::kProviderOpenVpn); provider.SetStringWithoutPathExpansion( shill::kOpenVPNClientCertIdProperty, pkcs11_id); } else { provider.SetStringWithoutPathExpansion(shill::kTypeProperty, shill::kProviderL2tpIpsec); provider.SetStringWithoutPathExpansion( shill::kL2tpIpsecClientCertSlotProperty, slot_id); provider.SetStringWithoutPathExpansion( shill::kL2tpIpsecClientCertIdProperty, pkcs11_id); } service_test_->SetServiceProperty( kVPNStub, shill::kProviderProperty, provider); } void GetVpnCertId(bool open_vpn, std::string* slot_id, std::string* pkcs11_id) { slot_id->clear(); pkcs11_id->clear(); const base::DictionaryValue* properties = service_test_->GetServiceProperties(kVPNStub); ASSERT_TRUE(properties); const base::DictionaryValue* provider = NULL; properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty, &provider); if (!provider) return; if (open_vpn) { provider->GetStringWithoutPathExpansion( shill::kOpenVPNClientCertIdProperty, pkcs11_id); } else { provider->GetStringWithoutPathExpansion( shill::kL2tpIpsecClientCertSlotProperty, slot_id); provider->GetStringWithoutPathExpansion( shill::kL2tpIpsecClientCertIdProperty, pkcs11_id); } } void GetEapCACertProperties(std::string* nss_nickname, std::string* ca_pem) { nss_nickname->clear(); ca_pem->clear(); const base::DictionaryValue* properties = service_test_->GetServiceProperties(kWifiStub); properties->GetStringWithoutPathExpansion(shill::kEapCaCertNssProperty, nss_nickname); const base::ListValue* ca_pems = NULL; properties->GetListWithoutPathExpansion(shill::kEapCaCertPemProperty, &ca_pems); if (ca_pems && !ca_pems->empty()) ca_pems->GetString(0, ca_pem); } void SetupVpnWithNss(bool open_vpn) { AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle); base::DictionaryValue provider; const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty : shill::kL2tpIpsecCaCertNssProperty; provider.SetStringWithoutPathExpansion(nss_property, kNSSNickname); service_test_->SetServiceProperty( kVPNStub, shill::kProviderProperty, provider); } void GetVpnCACertProperties(bool open_vpn, std::string* nss_nickname, std::string* ca_pem) { nss_nickname->clear(); ca_pem->clear(); const base::DictionaryValue* properties = service_test_->GetServiceProperties(kVPNStub); const base::DictionaryValue* provider = NULL; properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty, &provider); if (!provider) return; const char* nss_property = open_vpn ? shill::kOpenVPNCaCertNSSProperty : shill::kL2tpIpsecCaCertNssProperty; provider->GetStringWithoutPathExpansion(nss_property, nss_nickname); const base::ListValue* ca_pems = NULL; const char* pem_property = open_vpn ? shill::kOpenVPNCaCertPemProperty : shill::kL2tpIpsecCaCertPemProperty; provider->GetListWithoutPathExpansion(pem_property, &ca_pems); if (ca_pems && !ca_pems->empty()) ca_pems->GetString(0, ca_pem); } ShillServiceClient::TestInterface* service_test_; scoped_refptr test_ca_cert_; scoped_refptr test_client_cert_; std::string test_client_cert_pkcs11_id_; std::string test_client_cert_slot_id_; std::string test_ca_cert_pem_; base::MessageLoop message_loop_; private: void CleanupTestCert() { if (test_ca_cert_.get()) ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_ca_cert_.get())); if (test_client_cert_.get()) ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_client_cert_.get())); } scoped_ptr network_state_handler_; scoped_ptr network_cert_migrator_; crypto::ScopedTestNSSChromeOSUser user_; scoped_ptr test_nssdb_; DISALLOW_COPY_AND_ASSIGN(NetworkCertMigratorTest); }; TEST_F(NetworkCertMigratorTest, MigrateNssOnInitialization) { // Add a new network for migration before the handlers are initialized. SetupWifiWithNss(); SetupTestCACert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); std::string nss_nickname, ca_pem; GetEapCACertProperties(&nss_nickname, &ca_pem); EXPECT_TRUE(nss_nickname.empty()); EXPECT_EQ(test_ca_cert_pem_, ca_pem); } TEST_F(NetworkCertMigratorTest, MigrateNssOnNetworkAppearance) { SetupTestCACert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupWifiWithNss(); base::RunLoop().RunUntilIdle(); std::string nss_nickname, ca_pem; GetEapCACertProperties(&nss_nickname, &ca_pem); EXPECT_TRUE(nss_nickname.empty()); EXPECT_EQ(test_ca_cert_pem_, ca_pem); } TEST_F(NetworkCertMigratorTest, DoNotMigrateNssIfPemSet) { // Add a new network with an already set PEM property. SetupWifiWithNss(); base::ListValue ca_pems; ca_pems.AppendString(kFakePEM); service_test_->SetServiceProperty( kWifiStub, shill::kEapCaCertPemProperty, ca_pems); SetupTestCACert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); std::string nss_nickname, ca_pem; GetEapCACertProperties(&nss_nickname, &ca_pem); EXPECT_TRUE(nss_nickname.empty()); EXPECT_EQ(kFakePEM, ca_pem); } TEST_F(NetworkCertMigratorTest, MigrateNssOpenVpn) { // Add a new network for migration before the handlers are initialized. SetupVpnWithNss(true /* OpenVPN */); SetupTestCACert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); std::string nss_nickname, ca_pem; GetVpnCACertProperties(true /* OpenVPN */, &nss_nickname, &ca_pem); EXPECT_TRUE(nss_nickname.empty()); EXPECT_EQ(test_ca_cert_pem_, ca_pem); } TEST_F(NetworkCertMigratorTest, MigrateNssIpsecVpn) { // Add a new network for migration before the handlers are initialized. SetupVpnWithNss(false /* not OpenVPN */); SetupTestCACert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); std::string nss_nickname, ca_pem; GetVpnCACertProperties(false /* not OpenVPN */, &nss_nickname, &ca_pem); EXPECT_TRUE(nss_nickname.empty()); EXPECT_EQ(test_ca_cert_pem_, ca_pem); } TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoMatchingCert) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupNetworkWithEapCertId(true /* wifi */, "unknown pkcs11 id"); base::RunLoop().RunUntilIdle(); // Since the PKCS11 ID is unknown, the certificate configuration will be // cleared. std::string cert_id; GetEapCertId(true /* wifi */, &cert_id); EXPECT_EQ(std::string(), cert_id); } TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoSlotId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupNetworkWithEapCertId(true /* wifi */, test_client_cert_pkcs11_id_); base::RunLoop().RunUntilIdle(); std::string cert_id; GetEapCertId(true /* wifi */, &cert_id); std::string expected_cert_id = test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; EXPECT_EQ(expected_cert_id, cert_id); } TEST_F(NetworkCertMigratorTest, MigrateWifiEapCertIdWrongSlotId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupNetworkWithEapCertId(true /* wifi */, "123:" + test_client_cert_pkcs11_id_); base::RunLoop().RunUntilIdle(); std::string cert_id; GetEapCertId(true /* wifi */, &cert_id); std::string expected_cert_id = test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; EXPECT_EQ(expected_cert_id, cert_id); } TEST_F(NetworkCertMigratorTest, DoNotChangeEapCertIdWithCorrectSlotId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); std::string expected_cert_id = test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; // Add a new network for migration after the handlers are initialized. SetupNetworkWithEapCertId(true /* wifi */, expected_cert_id); base::RunLoop().RunUntilIdle(); std::string cert_id; GetEapCertId(true /* wifi */, &cert_id); EXPECT_EQ(expected_cert_id, cert_id); } TEST_F(NetworkCertMigratorTest, IgnoreOpenVPNCertId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); const char kPkcs11Id[] = "any slot id"; // Add a new network for migration after the handlers are initialized. SetupVpnWithCertId( true /* OpenVPN */, std::string() /* no slot id */, kPkcs11Id); base::RunLoop().RunUntilIdle(); std::string pkcs11_id; std::string unused_slot_id; GetVpnCertId(true /* OpenVPN */, &unused_slot_id, &pkcs11_id); EXPECT_EQ(kPkcs11Id, pkcs11_id); } TEST_F(NetworkCertMigratorTest, MigrateEthernetEapCertIdWrongSlotId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupNetworkWithEapCertId( false /* ethernet */, "123:" + test_client_cert_pkcs11_id_); base::RunLoop().RunUntilIdle(); std::string cert_id; GetEapCertId(false /* ethernet */, &cert_id); std::string expected_cert_id = test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; EXPECT_EQ(expected_cert_id, cert_id); } TEST_F(NetworkCertMigratorTest, MigrateIpsecCertIdWrongSlotId) { SetupTestClientCert(); SetupNetworkHandlers(); base::RunLoop().RunUntilIdle(); // Add a new network for migration after the handlers are initialized. SetupVpnWithCertId(false /* IPsec */, "123", test_client_cert_pkcs11_id_); base::RunLoop().RunUntilIdle(); std::string pkcs11_id; std::string slot_id; GetVpnCertId(false /* IPsec */, &slot_id, &pkcs11_id); EXPECT_EQ(test_client_cert_pkcs11_id_, pkcs11_id); EXPECT_EQ(test_client_cert_slot_id_, slot_id); } } // namespace chromeos #endif