diff options
author | pneubeck@chromium.org <pneubeck@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-06 17:15:48 +0000 |
---|---|---|
committer | pneubeck@chromium.org <pneubeck@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-06 17:15:48 +0000 |
commit | 18e8d54f5e70ed8576fdb97aaed5de00ad663e72 (patch) | |
tree | f3b3710eadd94552bf4ef30471bd2a9aaa3366da /chromeos | |
parent | d8f1fa0dbe1507b1330c19315ddcfb7c6ef68e70 (diff) | |
download | chromium_src-18e8d54f5e70ed8576fdb97aaed5de00ad663e72.zip chromium_src-18e8d54f5e70ed8576fdb97aaed5de00ad663e72.tar.gz chromium_src-18e8d54f5e70ed8576fdb97aaed5de00ad663e72.tar.bz2 |
Add migration from CaCert NSS nicknames to PEM.
BUG=263044
Review URL: https://chromiumcodereview.appspot.com/20087002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215914 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos')
-rw-r--r-- | chromeos/cert_loader.cc | 23 | ||||
-rw-r--r-- | chromeos/cert_loader.h | 10 | ||||
-rw-r--r-- | chromeos/chromeos.gyp | 4 | ||||
-rw-r--r-- | chromeos/dbus/shill_service_client_stub.cc | 23 | ||||
-rw-r--r-- | chromeos/network/network_cert_migrator.cc | 259 | ||||
-rw-r--r-- | chromeos/network/network_cert_migrator.h | 50 | ||||
-rw-r--r-- | chromeos/network/network_cert_migrator_unittest.cc | 264 | ||||
-rw-r--r-- | chromeos/network/network_handler.cc | 6 | ||||
-rw-r--r-- | chromeos/network/network_handler.h | 2 | ||||
-rw-r--r-- | chromeos/network/network_state.cc | 33 | ||||
-rw-r--r-- | chromeos/network/network_state.h | 7 |
11 files changed, 670 insertions, 11 deletions
diff --git a/chromeos/cert_loader.cc b/chromeos/cert_loader.cc index 5d76711..799815d 100644 --- a/chromeos/cert_loader.cc +++ b/chromeos/cert_loader.cc @@ -59,6 +59,7 @@ void CallOpenPersistentNSSDB() { } // namespace static CertLoader* g_cert_loader = NULL; + // static void CertLoader::Initialize() { CHECK(!g_cert_loader); @@ -108,6 +109,11 @@ void CertLoader::SetCryptoTaskRunner( MaybeRequestCertificates(); } +void CertLoader::SetSlowTaskRunnerForTest( + const scoped_refptr<base::SequencedTaskRunner>& task_runner) { + slow_task_runner_for_test_ = task_runner; +} + CertLoader::~CertLoader() { net::CertDatabase::GetInstance()->RemoveObserver(this); if (LoginState::IsInitialized()) @@ -330,13 +336,16 @@ void CertLoader::StartLoadCertificates() { net::CertificateList* cert_list = new net::CertificateList; certificates_update_running_ = true; certificates_update_required_ = false; - base::WorkerPool::GetTaskRunner(true /* task_is_slow */)-> - PostTaskAndReply( - FROM_HERE, - base::Bind(LoadNSSCertificates, cert_list), - base::Bind(&CertLoader::UpdateCertificates, - update_certificates_factory_.GetWeakPtr(), - base::Owned(cert_list))); + + base::TaskRunner* task_runner = slow_task_runner_for_test_.get(); + if (!task_runner) + task_runner = base::WorkerPool::GetTaskRunner(true /* task is slow */); + task_runner->PostTaskAndReply( + FROM_HERE, + base::Bind(LoadNSSCertificates, cert_list), + base::Bind(&CertLoader::UpdateCertificates, + update_certificates_factory_.GetWeakPtr(), + base::Owned(cert_list))); } void CertLoader::UpdateCertificates(net::CertificateList* cert_list) { diff --git a/chromeos/cert_loader.h b/chromeos/cert_loader.h index 69e3191..ec2d7ce 100644 --- a/chromeos/cert_loader.h +++ b/chromeos/cert_loader.h @@ -66,12 +66,17 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer, static bool IsInitialized(); // |crypto_task_runner| is the task runner that any synchronous crypto calls - // should be made from. e.g. in Chrome this is the IO thread. Must be called + // should be made from, e.g. in Chrome this is the IO thread. Must be called // after the thread is started. Certificate loading will not happen unless // this is set. void SetCryptoTaskRunner( const scoped_refptr<base::SequencedTaskRunner>& crypto_task_runner); + // Sets the task runner that any slow calls will be made from, e.g. calls + // to the NSS database. If not set, uses base::WorkerPool. + void SetSlowTaskRunnerForTest( + const scoped_refptr<base::SequencedTaskRunner>& task_runner); + void AddObserver(CertLoader::Observer* observer); void RemoveObserver(CertLoader::Observer* observer); @@ -167,6 +172,9 @@ class CHROMEOS_EXPORT CertLoader : public net::CertDatabase::Observer, // TaskRunner for crypto calls. scoped_refptr<base::SequencedTaskRunner> crypto_task_runner_; + // TaskRunner for other slow tasks. May be set in tests. + scoped_refptr<base::TaskRunner> slow_task_runner_for_test_; + // This factory should be used only for callbacks during TPMToken // initialization. base::WeakPtrFactory<CertLoader> initialize_token_factory_; diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index 6ae7624..32691f1 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -228,6 +228,8 @@ 'network/managed_network_configuration_handler.h', 'network/managed_state.cc', 'network/managed_state.h', + 'network/network_cert_migrator.cc', + 'network/network_cert_migrator.h', 'network/network_change_notifier_chromeos.cc', 'network/network_change_notifier_chromeos.h', 'network/network_change_notifier_factory_chromeos.cc', @@ -447,6 +449,7 @@ '../crypto/crypto.gyp:crypto', '../dbus/dbus.gyp:dbus_test_support', '../net/net.gyp:net', + '../net/net.gyp:net_test_support', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', '../url/url.gyp:url_lib', @@ -494,6 +497,7 @@ 'network/cros_network_functions_unittest.cc', 'network/geolocation_handler_unittest.cc', 'network/managed_network_configuration_handler_unittest.cc', + 'network/network_cert_migrator_unittest.cc', 'network/network_change_notifier_chromeos_unittest.cc', 'network/network_configuration_handler_unittest.cc', 'network/network_connection_handler_unittest.cc', diff --git a/chromeos/dbus/shill_service_client_stub.cc b/chromeos/dbus/shill_service_client_stub.cc index 197e19b..f46d2d5 100644 --- a/chromeos/dbus/shill_service_client_stub.cc +++ b/chromeos/dbus/shill_service_client_stub.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" +#include "base/strings/string_util.h" #include "base/values.h" #include "chromeos/chromeos_switches.h" #include "chromeos/dbus/dbus_thread_manager.h" @@ -356,12 +357,30 @@ bool ShillServiceClientStub::SetServiceProperty(const std::string& service_path, MoveServiceToIndex(service_path, 0, true); } } - dict->SetWithoutPathExpansion(property, value.DeepCopy()); + base::DictionaryValue new_properties; + std::string changed_property; + bool case_sensitive = true; + if (StartsWithASCII(property, "Provider.", case_sensitive) || + StartsWithASCII(property, "OpenVPN.", case_sensitive) || + StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) { + // These properties are only nested within the Provider dictionary if read + // from Shill. + base::DictionaryValue* provider = new base::DictionaryValue; + provider->SetWithoutPathExpansion(property, value.DeepCopy()); + new_properties.SetWithoutPathExpansion(flimflam::kProviderProperty, + provider); + changed_property = flimflam::kProviderProperty; + } else { + new_properties.SetWithoutPathExpansion(property, value.DeepCopy()); + changed_property = property; + } + + dict->MergeDictionary(&new_properties); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&ShillServiceClientStub::NotifyObserversPropertyChanged, weak_ptr_factory_.GetWeakPtr(), - dbus::ObjectPath(service_path), property)); + dbus::ObjectPath(service_path), changed_property)); return true; } diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc new file mode 100644 index 0000000..baca3c0 --- /dev/null +++ b/chromeos/network/network_cert_migrator.cc @@ -0,0 +1,259 @@ +// 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 <string> + +#include "base/location.h" +#include "base/metrics/histogram.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/shill_service_client.h" +#include "chromeos/network/network_handler_callbacks.h" +#include "chromeos/network/network_state.h" +#include "chromeos/network/network_state_handler.h" +#include "dbus/object_path.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +enum UMANetworkType { + UMA_NETWORK_TYPE_EAP, + UMA_NETWORK_TYPE_OPENVPN, + UMA_NETWORK_TYPE_IPSEC, + UMA_NETWORK_TYPE_SIZE, +}; + +// Copied from x509_certificate_model_nss.cc +std::string GetNickname(const net::X509Certificate& cert) { + if (!cert.os_cert_handle()->nickname) + return std::string(); + std::string name = cert.os_cert_handle()->nickname; + // Hack copied from mozilla: Cut off text before first :, which seems to + // just be the token name. + size_t colon_pos = name.find(':'); + if (colon_pos != std::string::npos) + name = name.substr(colon_pos + 1); + return name; +} + +} // namespace + +// Checks which of the given |networks| has one of the deprecated +// CaCertNssProperties set. If such a network already has a CaCertPEM property, +// then the NssProperty is cleared. Otherwise, the NssProperty is compared with +// the nickname of each certificate of |certs|. If a match is found, then the +// CaCertPemProperty is set and the NssProperty is cleared. Otherwise, the +// network is not modified. +class NetworkCertMigrator::MigrationTask + : public base::RefCounted<MigrationTask> { + public: + MigrationTask(const net::CertificateList& certs, + const base::WeakPtr<NetworkCertMigrator>& cert_migrator) + : certs_(certs), + cert_migrator_(cert_migrator) { + } + + void Run(const NetworkStateHandler::NetworkStateList& networks) { + // Request properties for each network that has a CaCertNssProperty set + // according to the NetworkStateHandler. + for (NetworkStateHandler::NetworkStateList::const_iterator it = + networks.begin(); it != networks.end(); ++it) { + if (!(*it)->HasCACertNSS()) + continue; + const std::string& service_path = (*it)->path(); + DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( + dbus::ObjectPath(service_path), + base::Bind(&network_handler::GetPropertiesCallback, + base::Bind(&MigrationTask::MigrateNetwork, this), + network_handler::ErrorCallback(), + service_path)); + } + } + + void MigrateNetwork(const std::string& service_path, + const base::DictionaryValue& properties) { + if (!cert_migrator_) { + VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration."; + return; + } + + std::string nss_key, pem_key, nickname; + const base::ListValue* pem_property = NULL; + UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE; + + GetNssAndPemProperties( + properties, &nss_key, &pem_key, &pem_property, &nickname, &uma_type); + if (nickname.empty()) + return; // Didn't find any nickname. + + VLOG(2) << "Found NSS nickname to migrate. Property: " << nss_key + << ", network: " << service_path; + UMA_HISTOGRAM_ENUMERATION( + "Network.MigrationNssToPem", uma_type, UMA_NETWORK_TYPE_SIZE); + + if (pem_property && !pem_property->empty()) { + VLOG(2) << "PEM already exists, clearing NSS property."; + ClearNssProperty(service_path, nss_key); + return; + } + + scoped_refptr<net::X509Certificate> cert = + FindCertificateWithNickname(nickname); + if (!cert) { + VLOG(2) << "No matching cert found."; + return; + } + + std::string pem_encoded; + if (!net::X509Certificate::GetPEMEncoded(cert->os_cert_handle(), + &pem_encoded)) { + LOG(ERROR) << "PEM encoding failed."; + return; + } + + SetNssAndPemProperties(service_path, nss_key, pem_key, pem_encoded); + } + + void GetNssAndPemProperties(const base::DictionaryValue& shill_properties, + std::string* nss_key, + std::string* pem_key, + const base::ListValue** pem_property, + std::string* nickname, + UMANetworkType* uma_type) { + struct NssPem { + const char* read_prefix; + const char* nss_key; + const char* pem_key; + UMANetworkType uma_type; + } const kNssPemMap[] = { + { NULL, flimflam::kEapCaCertNssProperty, shill::kEapCaCertPemProperty, + UMA_NETWORK_TYPE_EAP }, + { flimflam::kProviderProperty, flimflam::kL2tpIpsecCaCertNssProperty, + shill::kL2tpIpsecCaCertPemProperty, UMA_NETWORK_TYPE_IPSEC }, + { flimflam::kProviderProperty, flimflam::kOpenVPNCaCertNSSProperty, + shill::kOpenVPNCaCertPemProperty, UMA_NETWORK_TYPE_OPENVPN }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNssPemMap); ++i) { + const base::DictionaryValue* dict = &shill_properties; + if (kNssPemMap[i].read_prefix) { + shill_properties.GetDictionaryWithoutPathExpansion( + kNssPemMap[i].read_prefix, &dict); + if (!dict) + continue; + } + dict->GetStringWithoutPathExpansion(kNssPemMap[i].nss_key, nickname); + if (!nickname->empty()) { + *nss_key = kNssPemMap[i].nss_key; + *pem_key = kNssPemMap[i].pem_key; + *uma_type = kNssPemMap[i].uma_type; + dict->GetListWithoutPathExpansion(kNssPemMap[i].pem_key, pem_property); + return; + } + } + } + + void ClearNssProperty(const std::string& service_path, + const std::string& nss_key) { + DBusThreadManager::Get()->GetShillServiceClient() + ->SetProperty(dbus::ObjectPath(service_path), + nss_key, + base::StringValue(std::string()), + base::Bind(&base::DoNothing), + base::Bind(&network_handler::ShillErrorCallbackFunction, + "MigrationTask.SetProperty failed", + service_path, + network_handler::ErrorCallback())); + cert_migrator_->network_state_handler_ + ->RequestUpdateForNetwork(service_path); + } + + scoped_refptr<net::X509Certificate> FindCertificateWithNickname( + const std::string& nickname) { + for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end(); + ++it) { + if (nickname == GetNickname(**it)) + return *it; + } + return NULL; + } + + void SetNssAndPemProperties(const std::string& service_path, + const std::string& nss_key, + const std::string& pem_key, + const std::string& pem_encoded_cert) { + base::DictionaryValue new_properties; + new_properties.SetStringWithoutPathExpansion(nss_key, std::string()); + scoped_ptr<base::ListValue> ca_cert_pems(new base::ListValue); + ca_cert_pems->AppendString(pem_encoded_cert); + new_properties.SetWithoutPathExpansion(pem_key, ca_cert_pems.release()); + + DBusThreadManager::Get()->GetShillServiceClient() + ->SetProperties(dbus::ObjectPath(service_path), + new_properties, + base::Bind(&base::DoNothing), + base::Bind(&network_handler::ShillErrorCallbackFunction, + "MigrationTask.SetProperties failed", + service_path, + network_handler::ErrorCallback())); + cert_migrator_->network_state_handler_ + ->RequestUpdateForNetwork(service_path); + } + + private: + friend class base::RefCounted<MigrationTask>; + virtual ~MigrationTask() { + } + + net::CertificateList certs_; + base::WeakPtr<NetworkCertMigrator> cert_migrator_; +}; + +NetworkCertMigrator::NetworkCertMigrator() + : network_state_handler_(NULL), + weak_ptr_factory_(this) { +} + +NetworkCertMigrator::~NetworkCertMigrator() { + network_state_handler_->RemoveObserver(this, FROM_HERE); + if (CertLoader::IsInitialized()) + CertLoader::Get()->RemoveObserver(this); +} + +void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) { + DCHECK(network_state_handler); + network_state_handler_ = network_state_handler; + network_state_handler_->AddObserver(this, FROM_HERE); + + DCHECK(CertLoader::IsInitialized()); + CertLoader::Get()->AddObserver(this); +} + +void NetworkCertMigrator::NetworkListChanged() { + if (!CertLoader::Get()->certificates_loaded()) { + VLOG(2) << "Certs not loaded yet."; + return; + } + // Run the migration process from deprecated CaCertNssProperties to CaCertPem. + VLOG(2) << "Start NSS nickname to PEM migration."; + scoped_refptr<MigrationTask> helper(new MigrationTask( + CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); + NetworkStateHandler::NetworkStateList networks; + network_state_handler_->GetNetworkList(&networks); + helper->Run(networks); +} + +void NetworkCertMigrator::OnCertificatesLoaded( + const net::CertificateList& cert_list, + bool initial_load) { + // Maybe there are networks referring to certs (by NSS nickname) that were not + // loaded before but are now. + NetworkListChanged(); +} + +} // namespace chromeos diff --git a/chromeos/network/network_cert_migrator.h b/chromeos/network/network_cert_migrator.h new file mode 100644 index 0000000..05b69e6 --- /dev/null +++ b/chromeos/network/network_cert_migrator.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_NETWORK_CERT_MIGRATOR_H_ +#define CHROMEOS_NETWORK_NETWORK_CERT_MIGRATOR_H_ + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "chromeos/cert_loader.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/network/network_state_handler_observer.h" + +namespace chromeos { + +class NetworkStateHandler; + +// Migrates network configurations from deprecated CaCertNSS properties to +// CaCertPEM. +class CHROMEOS_EXPORT NetworkCertMigrator : public NetworkStateHandlerObserver, + public CertLoader::Observer { + public: + virtual ~NetworkCertMigrator(); + + private: + friend class NetworkHandler; + friend class NetworkCertMigratorTest; + class MigrationTask; + + NetworkCertMigrator(); + void Init(NetworkStateHandler* network_state_handler); + + // NetworkStateHandlerObserver overrides + virtual void NetworkListChanged() OVERRIDE; + + // CertLoader::Observer overrides + virtual void OnCertificatesLoaded(const net::CertificateList& cert_list, + bool initial_load) OVERRIDE; + + // Unowned associated NetworkStateHandler* (global or test instance). + NetworkStateHandler* network_state_handler_; + + base::WeakPtrFactory<NetworkCertMigrator> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(NetworkCertMigrator); +}; + +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_NETWORK_CERT_MIGRATOR_H_ diff --git a/chromeos/network/network_cert_migrator_unittest.cc b/chromeos/network/network_cert_migrator_unittest.cc new file mode 100644 index 0000000..ee2dcdf --- /dev/null +++ b/chromeos/network/network_cert_migrator_unittest.cc @@ -0,0 +1,264 @@ +// 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 "base/file_util.h" +#include "base/files/file_path.h" +#include "base/run_loop.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/shill_service_client.h" +#include "chromeos/login/login_state.h" +#include "chromeos/network/network_state_handler.h" +#include "crypto/nss_util.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.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" + +namespace chromeos { + +namespace { + +const char* kWifiStub = "wifi_stub"; +const char* kVPNStub = "vpn_stub"; +const char* kNSSNickname = "nss_nickname"; +const char* kFakePEM = "pem"; + +} // namespace + +class NetworkCertMigratorTest : public testing::Test { + public: + NetworkCertMigratorTest() {} + virtual ~NetworkCertMigratorTest() {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(test_nssdb_.is_open()); + slot_ = net::NSSCertDatabase::GetInstance()->GetPublicModule(); + ASSERT_TRUE(slot_->os_module_handle()); + + LoginState::Initialize(); + + DBusThreadManager::InitializeWithStub(); + service_test_ = + DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); + message_loop_.RunUntilIdle(); + service_test_->ClearServices(); + message_loop_.RunUntilIdle(); + + CertLoader::Initialize(); + CertLoader::Get()->SetSlowTaskRunnerForTest( + message_loop_.message_loop_proxy()); + CertLoader::Get()->SetCryptoTaskRunner(message_loop_.message_loop_proxy()); + } + + virtual void TearDown() OVERRIDE { + network_cert_migrator_.reset(); + network_state_handler_.reset(); + CertLoader::Shutdown(); + DBusThreadManager::Shutdown(); + LoginState::Shutdown(); + CleanupTestCert(); + } + + protected: + void SetupTestCACert() { + scoped_refptr<net::X509Certificate> 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::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance(); + net::CertificateList cert_list; + cert_list.push_back(test_ca_cert_); + net::NSSCertDatabase::ImportCertFailureList failures; + EXPECT_TRUE(cert_database->ImportCACerts( + cert_list, net::NSSCertDatabase::TRUST_DEFAULT, &failures)); + ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error); + } + + void SetupNetworkHandlers() { + network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); + network_cert_migrator_.reset(new NetworkCertMigrator); + network_cert_migrator_->Init(network_state_handler_.get()); + } + + void SetupWifiWithNss() { + const bool add_to_visible = true; + const bool add_to_watchlist = true; + service_test_->AddService(kWifiStub, + kWifiStub, + flimflam::kTypeWifi, + flimflam::kStateOnline, + add_to_visible, + add_to_watchlist); + service_test_->SetServiceProperty(kWifiStub, + flimflam::kEapCaCertNssProperty, + base::StringValue(kNSSNickname)); + } + + 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(flimflam::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) { + const bool add_to_visible = true; + const bool add_to_watchlist = true; + service_test_->AddService(kVPNStub, + kVPNStub, + flimflam::kTypeVPN, + flimflam::kStateIdle, + add_to_visible, + add_to_watchlist); + base::DictionaryValue provider; + const char* nss_property = open_vpn ? flimflam::kOpenVPNCaCertNSSProperty + : flimflam::kL2tpIpsecCaCertNssProperty; + provider.SetStringWithoutPathExpansion(nss_property, kNSSNickname); + service_test_->SetServiceProperty( + kVPNStub, flimflam::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(flimflam::kProviderProperty, + &provider); + if (!provider) + return; + const char* nss_property = open_vpn ? flimflam::kOpenVPNCaCertNSSProperty + : flimflam::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<net::X509Certificate> test_ca_cert_; + std::string test_ca_cert_pem_; + base::MessageLoop message_loop_; + + private: + void CleanupTestCert() { + ASSERT_TRUE(net::NSSCertDatabase::GetInstance()->DeleteCertAndKey( + test_ca_cert_.get())); + } + + scoped_ptr<NetworkStateHandler> network_state_handler_; + scoped_ptr<NetworkCertMigrator> network_cert_migrator_; + scoped_refptr<net::CryptoModule> slot_; + crypto::ScopedTestNSSDB 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(); + + message_loop_.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(); + message_loop_.RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupWifiWithNss(); + + message_loop_.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(); + message_loop_.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, MigrateOpenVpn) { + // Add a new network for migration before the handlers are initialized. + SetupVpnWithNss(true /* OpenVPN */); + + SetupTestCACert(); + SetupNetworkHandlers(); + + message_loop_.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, MigrateIpsecVpn) { + // Add a new network for migration before the handlers are initialized. + SetupVpnWithNss(false /* not OpenVPN */); + + SetupTestCACert(); + SetupNetworkHandlers(); + + message_loop_.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); +} + + +} // namespace chromeos diff --git a/chromeos/network/network_handler.cc b/chromeos/network/network_handler.cc index fc6896b..ae272fa 100644 --- a/chromeos/network/network_handler.cc +++ b/chromeos/network/network_handler.cc @@ -4,9 +4,11 @@ #include "chromeos/network/network_handler.h" +#include "base/threading/worker_pool.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/network/geolocation_handler.h" #include "chromeos/network/managed_network_configuration_handler.h" +#include "chromeos/network/network_cert_migrator.h" #include "chromeos/network/network_configuration_handler.h" #include "chromeos/network/network_connection_handler.h" #include "chromeos/network/network_device_handler.h" @@ -29,6 +31,8 @@ NetworkHandler::NetworkHandler() { network_state_handler_.reset(new NetworkStateHandler()); network_device_handler_.reset(new NetworkDeviceHandler()); network_profile_handler_.reset(new NetworkProfileHandler()); + if (CertLoader::IsInitialized()) + network_cert_migrator_.reset(new NetworkCertMigrator()); network_configuration_handler_.reset(new NetworkConfigurationHandler()); managed_network_configuration_handler_.reset( new ManagedNetworkConfigurationHandler()); @@ -44,6 +48,8 @@ NetworkHandler::~NetworkHandler() { void NetworkHandler::Init() { network_state_handler_->InitShillPropertyHandler(); network_profile_handler_->Init(network_state_handler_.get()); + if (network_cert_migrator_) + network_cert_migrator_->Init(network_state_handler_.get()); network_configuration_handler_->Init(network_state_handler_.get()); managed_network_configuration_handler_->Init( network_state_handler_.get(), diff --git a/chromeos/network/network_handler.h b/chromeos/network/network_handler.h index 9cc2d46..a791f9e 100644 --- a/chromeos/network/network_handler.h +++ b/chromeos/network/network_handler.h @@ -13,6 +13,7 @@ namespace chromeos { class GeolocationHandler; class ManagedNetworkConfigurationHandler; +class NetworkCertMigrator; class NetworkConfigurationHandler; class NetworkConnectionHandler; class NetworkDeviceHandler; @@ -59,6 +60,7 @@ class CHROMEOS_EXPORT NetworkHandler { scoped_ptr<NetworkStateHandler> network_state_handler_; scoped_ptr<NetworkDeviceHandler> network_device_handler_; scoped_ptr<NetworkProfileHandler> network_profile_handler_; + scoped_ptr<NetworkCertMigrator> network_cert_migrator_; scoped_ptr<NetworkConfigurationHandler> network_configuration_handler_; scoped_ptr<ManagedNetworkConfigurationHandler> managed_network_configuration_handler_; diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc index ca0b7b6..f0da62d 100644 --- a/chromeos/network/network_state.cc +++ b/chromeos/network/network_state.cc @@ -68,6 +68,33 @@ chromeos::NetworkUIData* CreateUIDataFromValue( return new chromeos::NetworkUIData(*ui_data_dict); } +bool IsCaCertNssSet(const base::DictionaryValue& properties) { + std::string ca_cert_nss; + if (properties.GetStringWithoutPathExpansion(flimflam::kEapCaCertNssProperty, + &ca_cert_nss) && + !ca_cert_nss.empty()) { + return true; + } + + const base::DictionaryValue* provider = NULL; + properties.GetDictionaryWithoutPathExpansion(flimflam::kProviderProperty, + &provider); + if (!provider) + return false; + if (provider->GetStringWithoutPathExpansion( + flimflam::kL2tpIpsecCaCertNssProperty, &ca_cert_nss) && + !ca_cert_nss.empty()) { + return true; + } + if (provider->GetStringWithoutPathExpansion( + flimflam::kOpenVPNCaCertNSSProperty, &ca_cert_nss) && + !ca_cert_nss.empty()) { + return true; + } + + return false; +} + } // namespace namespace chromeos { @@ -82,7 +109,8 @@ NetworkState::NetworkState(const std::string& path) signal_strength_(0), connectable_(false), activate_over_non_cellular_networks_(false), - cellular_out_of_credits_(false) { + cellular_out_of_credits_(false), + has_ca_cert_nss_(false) { } NetworkState::~NetworkState() { @@ -191,6 +219,9 @@ bool NetworkState::PropertyChanged(const std::string& key, bool NetworkState::InitialPropertiesReceived( const base::DictionaryValue& properties) { bool changed = UpdateName(properties); + bool had_ca_cert_nss = has_ca_cert_nss_; + has_ca_cert_nss_ = IsCaCertNssSet(properties); + changed |= had_ca_cert_nss != has_ca_cert_nss_; return changed; } diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h index 5263d00..f177794 100644 --- a/chromeos/network/network_state.h +++ b/chromeos/network/network_state.h @@ -74,6 +74,9 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { const std::string& post_method() const { return post_method_; } const std::string& post_data() const { return post_data_; } + // Whether this network has a CACertNSS nickname set. + bool HasCACertNSS() const { return has_ca_cert_nss_; } + // Returns true if |connection_state_| is a connected/connecting state. bool IsConnectedState() const; bool IsConnectingState() const; @@ -154,6 +157,10 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { std::string post_method_; std::string post_data_; + // Whether a deprecated CaCertNSS property of this network is set. Required + // for migration to PEM. + bool has_ca_cert_nss_; + DISALLOW_COPY_AND_ASSIGN(NetworkState); }; |