Skip to content

Commit

Permalink
Add migration from CaCert NSS nicknames to PEM.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pneubeck@chromium.org committed Aug 6, 2013
1 parent d8f1fa0 commit 18e8d54
Show file tree
Hide file tree
Showing 11 changed files with 670 additions and 11 deletions.
23 changes: 16 additions & 7 deletions chromeos/cert_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void CallOpenPersistentNSSDB() {
} // namespace

static CertLoader* g_cert_loader = NULL;

// static
void CertLoader::Initialize() {
CHECK(!g_cert_loader);
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 9 additions & 1 deletion chromeos/cert_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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_;
Expand Down
4 changes: 4 additions & 0 deletions chromeos/chromeos.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
23 changes: 21 additions & 2 deletions chromeos/dbus/shill_service_client_stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}

Expand Down
259 changes: 259 additions & 0 deletions chromeos/network/network_cert_migrator.cc
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 18e8d54

Please sign in to comment.