| // 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 <cert.h> |
| #include <pk11pub.h> |
| #include <string> |
| |
| #include "base/files/file_path.h" |
| #include "base/macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/scoped_task_environment.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_cert_loader.h" |
| #include "chromeos/network/network_state_handler.h" |
| #include "crypto/scoped_nss_types.h" |
| #include "crypto/scoped_test_nss_db.h" |
| #include "net/cert/nss_cert_database_chromeos.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/cert/x509_util_nss.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| const char* kWifiStub = "wifi_stub"; |
| const char* kEthernetEapStub = "ethernet_eap_stub"; |
| const char* kVPNStub = "vpn_stub"; |
| const char* kUserShillProfile = "/profile/profile1"; |
| |
| } // namespace |
| |
| class NetworkCertMigratorTest : public testing::Test { |
| public: |
| NetworkCertMigratorTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::DEFAULT, |
| base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED), |
| service_test_(nullptr) {} |
| ~NetworkCertMigratorTest() override = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(test_system_nssdb_.is_open()); |
| ASSERT_TRUE(test_user_nssdb_.is_open()); |
| // Use the same DB for public and private slot. |
| test_system_nsscertdb_.reset(new net::NSSCertDatabaseChromeOS( |
| crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_system_nssdb_.slot())), |
| crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_system_nssdb_.slot())))); |
| test_user_nsscertdb_.reset(new net::NSSCertDatabaseChromeOS( |
| crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_user_nssdb_.slot())), |
| crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_user_nssdb_.slot())))); |
| |
| DBusThreadManager::Initialize(); |
| service_test_ = |
| DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); |
| DBusThreadManager::Get() |
| ->GetShillProfileClient() |
| ->GetTestInterface() |
| ->AddProfile(kUserShillProfile, "" /* userhash */); |
| scoped_task_environment_.RunUntilIdle(); |
| service_test_->ClearServices(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| NetworkCertLoader::Initialize(); |
| } |
| |
| void TearDown() override { |
| network_state_handler_->Shutdown(); |
| network_cert_migrator_.reset(); |
| network_state_handler_.reset(); |
| NetworkCertLoader::Shutdown(); |
| DBusThreadManager::Shutdown(); |
| } |
| |
| protected: |
| enum class ShillProfile { SHARED, USER }; |
| |
| void SetupTestClientCert(crypto::ScopedTestNSSDB* nssdb) { |
| net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(), |
| "client_1.pem", "client_1.pk8", |
| nssdb->slot(), &test_client_cert_); |
| ASSERT_TRUE(test_client_cert_.get()); |
| |
| int slot_id = -1; |
| test_client_cert_pkcs11_id_ = NetworkCertLoader::GetPkcs11IdAndSlotForCert( |
| test_client_cert_.get(), &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_ = NetworkStateHandler::InitializeForTest(); |
| network_cert_migrator_.reset(new NetworkCertMigrator); |
| network_cert_migrator_->Init(network_state_handler_.get()); |
| } |
| |
| void AddService(ShillProfile shill_profile, |
| 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. |
| std::string shill_profile_path = |
| shill_profile == ShillProfile::SHARED |
| ? ShillProfileClient::GetSharedProfilePath() |
| : std::string(kUserShillProfile); |
| service_test_->SetServiceProperty(network_id, shill::kProfileProperty, |
| base::Value(shill_profile_path)); |
| } |
| |
| void SetupNetworkWithEapCertId(ShillProfile shill_profile, |
| bool wifi, |
| const std::string& cert_id) { |
| std::string type = wifi ? shill::kTypeWifi: shill::kTypeEthernetEap; |
| std::string name = wifi ? kWifiStub : kEthernetEapStub; |
| AddService(shill_profile, name, type, shill::kStateOnline); |
| service_test_->SetServiceProperty(name, shill::kEapCertIdProperty, |
| base::Value(cert_id)); |
| service_test_->SetServiceProperty(name, shill::kEapKeyIdProperty, |
| base::Value(cert_id)); |
| |
| if (wifi) { |
| service_test_->SetServiceProperty(name, shill::kSecurityClassProperty, |
| base::Value(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(ShillProfile shill_profile, |
| bool open_vpn, |
| const std::string& slot_id, |
| const std::string& pkcs11_id) { |
| AddService(shill_profile, kVPNStub, shill::kTypeVPN, shill::kStateIdle); |
| base::DictionaryValue provider; |
| if (open_vpn) { |
| provider.SetKey(shill::kTypeProperty, |
| base::Value(shill::kProviderOpenVpn)); |
| provider.SetKey(shill::kOpenVPNClientCertIdProperty, |
| base::Value(pkcs11_id)); |
| } else { |
| provider.SetKey(shill::kTypeProperty, |
| base::Value(shill::kProviderL2tpIpsec)); |
| provider.SetKey(shill::kL2tpIpsecClientCertSlotProperty, |
| base::Value(slot_id)); |
| provider.SetKey(shill::kL2tpIpsecClientCertIdProperty, |
| base::Value(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 = nullptr; |
| 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); |
| } |
| } |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| ShillServiceClient::TestInterface* service_test_; |
| net::ScopedCERTCertificate test_client_cert_; |
| std::string test_client_cert_pkcs11_id_; |
| std::string test_client_cert_slot_id_; |
| crypto::ScopedTestNSSDB test_system_nssdb_; |
| crypto::ScopedTestNSSDB test_user_nssdb_; |
| std::unique_ptr<net::NSSCertDatabaseChromeOS> test_system_nsscertdb_; |
| std::unique_ptr<net::NSSCertDatabaseChromeOS> test_user_nsscertdb_; |
| |
| private: |
| std::unique_ptr<NetworkStateHandler> network_state_handler_; |
| std::unique_ptr<NetworkCertMigrator> network_cert_migrator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NetworkCertMigratorTest); |
| }; |
| |
| // Test that migration of user profile networks is deferred until the user's NSS |
| // Database has been loaded. |
| // See crbug.com/774745 |
| TEST_F(NetworkCertMigratorTest, DeferUserNetworkMigrationToUserCertDbLoad) { |
| SetupNetworkWithEapCertId(ShillProfile::USER, true /* wifi */, "123:12345"); |
| // Load the system NSSDB only first |
| NetworkCertLoader::Get()->SetSystemNSSDB(test_system_nsscertdb_.get()); |
| |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Migration should not have been performed on the user profile network, |
| // because the user NSSDB has not been loaded yet. |
| std::string cert_id; |
| GetEapCertId(true /* wifi */, &cert_id); |
| std::string expected_cert_id = "123:12345"; |
| EXPECT_EQ(expected_cert_id, cert_id); |
| |
| // Load the user NSSDB now |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Since the PKCS11 ID is unknown, the certificate configuration of the shared |
| // profile network will be cleared. |
| GetEapCertId(true /* wifi */, &cert_id); |
| EXPECT_EQ(std::string(), cert_id); |
| } |
| |
| // Test that migration of shared profile networks is done on first NSS database |
| // load. |
| TEST_F(NetworkCertMigratorTest, RunSharedNetworkMigrationOnFirstCertDbLoad) { |
| SetupNetworkWithEapCertId(ShillProfile::SHARED, true /* wifi */, "123:12345"); |
| // Load the system NSSDB only first |
| NetworkCertLoader::Get()->SetSystemNSSDB(test_system_nsscertdb_.get()); |
| |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Since the PKCS11 ID is unknown, the certificate configuration of the shared |
| // profile network will be cleared. |
| std::string cert_id; |
| GetEapCertId(true /* wifi */, &cert_id); |
| EXPECT_EQ(std::string(), cert_id); |
| } |
| |
| TEST_F(NetworkCertMigratorTest, MigrateOnInitialization) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| // Add a network for migration before the handlers are initialized. |
| SetupNetworkWithEapCertId(ShillProfile::USER, true /* wifi */, |
| "123:" + test_client_cert_pkcs11_id_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.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, MigrateEapCertIdNoMatchingCert) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupNetworkWithEapCertId(ShillProfile::USER, true /* wifi */, |
| "unknown pkcs11 id"); |
| |
| scoped_task_environment_.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) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupNetworkWithEapCertId(ShillProfile::USER, true /* wifi */, |
| test_client_cert_pkcs11_id_); |
| |
| scoped_task_environment_.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) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupNetworkWithEapCertId(ShillProfile::USER, true /* wifi */, |
| "123:" + test_client_cert_pkcs11_id_); |
| |
| scoped_task_environment_.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) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.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(ShillProfile::USER, true /* wifi */, |
| expected_cert_id); |
| |
| scoped_task_environment_.RunUntilIdle(); |
| |
| std::string cert_id; |
| GetEapCertId(true /* wifi */, &cert_id); |
| EXPECT_EQ(expected_cert_id, cert_id); |
| } |
| |
| TEST_F(NetworkCertMigratorTest, IgnoreOpenVPNCertId) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| const char kPkcs11Id[] = "any slot id"; |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupVpnWithCertId(ShillProfile::USER, |
| |
| true /* OpenVPN */, std::string() /* no slot id */, |
| kPkcs11Id); |
| |
| scoped_task_environment_.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) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupNetworkWithEapCertId(ShillProfile::USER, |
| |
| false /* ethernet */, |
| "123:" + test_client_cert_pkcs11_id_); |
| |
| scoped_task_environment_.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) { |
| NetworkCertLoader::Get()->SetUserNSSDB(test_user_nsscertdb_.get()); |
| |
| SetupTestClientCert(&test_user_nssdb_); |
| SetupNetworkHandlers(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| // Add a new network for migration after the handlers are initialized. |
| SetupVpnWithCertId(ShillProfile::USER, false /* IPsec */, "123", |
| test_client_cert_pkcs11_id_); |
| |
| scoped_task_environment_.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 |