blob: bc1bb374cd1adea6b518b1bddef938c982a6f0fe [file] [log] [blame]
// 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/shill/shill_clients.h"
#include "chromeos/dbus/shill/shill_profile_client.h"
#include "chromeos/dbus/shill/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::ThreadPoolExecutionMode::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 slot as public and private slot for the user's
// NSSCertDatabse for testing.
test_user_nsscertdb_ = std::make_unique<net::NSSCertDatabaseChromeOS>(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_user_nssdb_.slot())),
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_user_nssdb_.slot())));
// Create a NSSCertDatabase for the system slot. While NetworkCertLoader
// does not care about the public slot in this database, NSSCertDatabase
// requires a public slot. Pass the system slot there for testing.
test_system_nsscertdb_ = std::make_unique<net::NSSCertDatabaseChromeOS>(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_system_nssdb_.slot())),
crypto::ScopedPK11Slot() /* private_slot */);
test_system_nsscertdb_->SetSystemSlot(
crypto::ScopedPK11Slot(PK11_ReferenceSlot(test_system_nssdb_.slot())));
shill_clients::InitializeFakes();
service_test_ = ShillServiceClient::Get()->GetTestInterface();
ShillProfileClient::Get()->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();
shill_clients::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::NumberToString(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