Commit 56c6597c authored by pneubeck@chromium.org's avatar pneubeck@chromium.org

Migrate slot ID of client certs in network configuration.

If a network was configured with a client certificate before R38, the slot ID will either be missing or not be valid in R38 and later because of the added device wide token.

This migration code will look up a configured client certificate by its PKCS#11 ID and update the slot id.
If the certificate can't be found, the client configuration will be removed in order to trigger a clean 'missing configuration' error in the UI.

This change is primarily targeted for manually configured networks and not policy-configured networks. The latter are automatically updated by the chromeos::ClientCertResolver.

BUG=403900

Review URL: https://codereview.chromium.org/471183002

Cr-Commit-Position: refs/heads/master@{#290044}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290044 0039d316-1c4b-4281-b951-d872f2087c98
parent ccb6d923
...@@ -1293,7 +1293,9 @@ void WifiConfigView::InitFromProperties( ...@@ -1293,7 +1293,9 @@ void WifiConfigView::InitFromProperties(
std::string eap_cert_id; std::string eap_cert_id;
properties.GetStringWithoutPathExpansion( properties.GetStringWithoutPathExpansion(
shill::kEapCertIdProperty, &eap_cert_id); shill::kEapCertIdProperty, &eap_cert_id);
std::string pkcs11_id = client_cert::GetPkcs11IdFromEapCertId(eap_cert_id); int unused_slot_id = 0;
std::string pkcs11_id = client_cert::GetPkcs11AndSlotIdFromEapCertId(
eap_cert_id, &unused_slot_id);
if (!pkcs11_id.empty()) { if (!pkcs11_id.empty()) {
int cert_index = int cert_index =
CertLibrary::Get()->GetUserCertIndexByPkcs11Id(pkcs11_id); CertLibrary::Get()->GetUserCertIndexByPkcs11Id(pkcs11_id);
......
...@@ -497,6 +497,7 @@ void FakeShillManagerClient::ServiceStateChanged( ...@@ -497,6 +497,7 @@ void FakeShillManagerClient::ServiceStateChanged(
void FakeShillManagerClient::SortManagerServices(bool notify) { void FakeShillManagerClient::SortManagerServices(bool notify) {
DVLOG(1) << "SortManagerServices"; DVLOG(1) << "SortManagerServices";
static const char* ordered_types[] = {shill::kTypeEthernet, static const char* ordered_types[] = {shill::kTypeEthernet,
shill::kTypeEthernetEap,
shill::kTypeWifi, shill::kTypeWifi,
shill::kTypeCellular, shill::kTypeCellular,
shill::kTypeWimax, shill::kTypeWimax,
...@@ -876,6 +877,8 @@ base::ListValue* FakeShillManagerClient::GetListProperty( ...@@ -876,6 +877,8 @@ base::ListValue* FakeShillManagerClient::GetListProperty(
bool FakeShillManagerClient::TechnologyEnabled(const std::string& type) const { bool FakeShillManagerClient::TechnologyEnabled(const std::string& type) const {
if (type == shill::kTypeVPN) if (type == shill::kTypeVPN)
return true; // VPN is always "enabled" since there is no associated device return true; // VPN is always "enabled" since there is no associated device
if (type == shill::kTypeEthernetEap)
return true;
bool enabled = false; bool enabled = false;
const base::ListValue* technologies; const base::ListValue* technologies;
if (stub_properties_.GetListWithoutPathExpansion( if (stub_properties_.GetListWithoutPathExpansion(
......
...@@ -97,7 +97,9 @@ bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, ...@@ -97,7 +97,9 @@ bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
return true; return true;
} }
std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { std::string GetPkcs11AndSlotIdFromEapCertId(const std::string& cert_id,
int* slot_id) {
*slot_id = -1;
if (cert_id.empty()) if (cert_id.empty())
return std::string(); return std::string();
...@@ -110,9 +112,73 @@ std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { ...@@ -110,9 +112,73 @@ std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) {
LOG(ERROR) << "Empty PKCS11 id in cert id."; LOG(ERROR) << "Empty PKCS11 id in cert id.";
return std::string(); return std::string();
} }
int parsed_slot_id;
if (base::StringToInt(cert_id.substr(0, delimiter_pos), &parsed_slot_id))
*slot_id = parsed_slot_id;
else
LOG(ERROR) << "Slot ID is not an integer. Cert ID is: " << cert_id << ".";
return cert_id.substr(delimiter_pos + 1); return cert_id.substr(delimiter_pos + 1);
} }
void GetClientCertFromShillProperties(
const base::DictionaryValue& shill_properties,
ConfigType* cert_config_type,
int* tpm_slot,
std::string* pkcs11_id) {
*cert_config_type = CONFIG_TYPE_NONE;
*tpm_slot = -1;
pkcs11_id->clear();
// Look for VPN specific client certificate properties.
//
// VPN Provider values are read from the "Provider" dictionary, not the
// "Provider.Type", etc keys (which are used only to set the values).
const base::DictionaryValue* provider_properties = NULL;
if (shill_properties.GetDictionaryWithoutPathExpansion(
shill::kProviderProperty, &provider_properties)) {
// Look for OpenVPN specific properties.
if (provider_properties->GetStringWithoutPathExpansion(
shill::kOpenVPNClientCertIdProperty, pkcs11_id)) {
*cert_config_type = CONFIG_TYPE_OPENVPN;
return;
}
// Look for L2TP-IPsec specific properties.
if (provider_properties->GetStringWithoutPathExpansion(
shill::kL2tpIpsecClientCertIdProperty, pkcs11_id)) {
std::string cert_slot;
provider_properties->GetStringWithoutPathExpansion(
shill::kL2tpIpsecClientCertSlotProperty, &cert_slot);
if (!cert_slot.empty() && !base::StringToInt(cert_slot, tpm_slot)) {
LOG(ERROR) << "Cert slot is not an integer: " << cert_slot << ".";
return;
}
*cert_config_type = CONFIG_TYPE_IPSEC;
}
return;
}
// Look for EAP specific client certificate properties, which can either be
// part of a WiFi or EthernetEAP configuration.
std::string cert_id;
if (shill_properties.GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
&cert_id)) {
// Shill requires both CertID and KeyID for TLS connections, despite the
// fact that by convention they are the same ID, because one identifies
// the certificate and the other the private key.
std::string key_id;
shill_properties.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
&key_id);
// Assume the configuration to be invalid, if the two IDs are not identical.
if (cert_id != key_id) {
LOG(ERROR) << "EAP CertID differs from KeyID";
return;
}
*pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot);
*cert_config_type = CONFIG_TYPE_EAP;
}
}
void SetShillProperties(const ConfigType cert_config_type, void SetShillProperties(const ConfigType cert_config_type,
const int tpm_slot, const int tpm_slot,
const std::string& pkcs11_id, const std::string& pkcs11_id,
......
...@@ -55,10 +55,26 @@ struct CHROMEOS_EXPORT ClientCertConfig { ...@@ -55,10 +55,26 @@ struct CHROMEOS_EXPORT ClientCertConfig {
bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
const net::CertPrincipal& principal); const net::CertPrincipal& principal);
// Returns the PKCS11 id part of |cert_id|, which is expected to be the value of // Returns the PKCS11 and slot ID of |cert_id|, which is expected to be a
// the Shill property kEapCertIdProperty or kEapKeyIdProperty. // value of the Shill property kEapCertIdProperty or kEapKeyIdProperty, either
CHROMEOS_EXPORT std::string GetPkcs11IdFromEapCertId( // of format "<pkcs11_id>" or "<slot_id>:<pkcs11_id>".
const std::string& cert_id); CHROMEOS_EXPORT std::string GetPkcs11AndSlotIdFromEapCertId(
const std::string& cert_id,
int* slot_id);
// Reads the client certificate configuration from the Shill Service properties
// |shill_properties|.
// If such a configuration is found, the values |cert_config_type|, |tpm_slot|
// and |pkcs11_id| are filled accordingly. In case of OpenVPN or because the
// property was not set, |tpm_slot| will be set to -1.
// If an error occurred or no client configuration is found, |cert_config_type|
// will be set to CONFIG_TYPE_NONE, |tpm_slot| to -1 and |pkcs11_id| to the
// empty string.
CHROMEOS_EXPORT void GetClientCertFromShillProperties(
const base::DictionaryValue& shill_properties,
ConfigType* cert_config_type,
int* tpm_slot,
std::string* pkcs11_id);
// Sets the properties of a client cert and the TPM slot that it's contained in. // Sets the properties of a client cert and the TPM slot that it's contained in.
// |cert_config_type| determines which dictionary entries to set. // |cert_config_type| determines which dictionary entries to set.
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_service_client.h" #include "chromeos/dbus/shill_service_client.h"
#include "chromeos/network/client_cert_util.h"
#include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_handler_callbacks.h"
#include "chromeos/network/network_state.h" #include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h" #include "chromeos/network/network_state_handler.h"
...@@ -44,12 +45,23 @@ std::string GetNickname(const net::X509Certificate& cert) { ...@@ -44,12 +45,23 @@ std::string GetNickname(const net::X509Certificate& cert) {
} // namespace } // namespace
// Checks which of the given |networks| has one of the deprecated // Migrates each network of |networks| with a deprecated CaCertNss property to
// CaCertNssProperties set. If such a network already has a CaCertPEM property, // the respective CaCertPEM property and fixes an invalid or missing slot ID of
// then the NssProperty is cleared. Otherwise, the NssProperty is compared with // a client certificate configuration.
// the nickname of each certificate of |certs|. If a match is found, then the //
// CaCertPemProperty is set and the NssProperty is cleared. Otherwise, the // If a network already has a CaCertPEM property, then the NssProperty is
// network is not modified. // cleared. Otherwise, the NssProperty is compared with
// the nickname of each certificate of |certs|. If a match is found, the
// CaCertPemProperty is set and the NssProperty is cleared.
//
// If a network with a client certificate configuration (i.e. a PKCS11 ID) is
// found, the configured client certificate is looked up.
// If the certificate is found, the currently configured slot ID (if any) is
// compared with the actual slot ID of the certificate and if required updated.
// If the certificate is not found, the client certificate configuration is
// removed.
//
// Only if necessary, a network will be notified.
class NetworkCertMigrator::MigrationTask class NetworkCertMigrator::MigrationTask
: public base::RefCounted<MigrationTask> { : public base::RefCounted<MigrationTask> {
public: public:
...@@ -61,11 +73,15 @@ class NetworkCertMigrator::MigrationTask ...@@ -61,11 +73,15 @@ class NetworkCertMigrator::MigrationTask
void Run(const NetworkStateHandler::NetworkStateList& networks) { void Run(const NetworkStateHandler::NetworkStateList& networks) {
// Request properties for each network that has a CaCertNssProperty set // Request properties for each network that has a CaCertNssProperty set
// according to the NetworkStateHandler. // or which could be configured with a client certificate.
for (NetworkStateHandler::NetworkStateList::const_iterator it = for (NetworkStateHandler::NetworkStateList::const_iterator it =
networks.begin(); it != networks.end(); ++it) { networks.begin(); it != networks.end(); ++it) {
if (!(*it)->HasCACertNSS()) if (!(*it)->HasCACertNSS() &&
(*it)->security() != shill::kSecurity8021x &&
(*it)->type() != shill::kTypeVPN &&
(*it)->type() != shill::kTypeEthernetEap) {
continue; continue;
}
const std::string& service_path = (*it)->path(); const std::string& service_path = (*it)->path();
DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
dbus::ObjectPath(service_path), dbus::ObjectPath(service_path),
...@@ -83,6 +99,59 @@ class NetworkCertMigrator::MigrationTask ...@@ -83,6 +99,59 @@ class NetworkCertMigrator::MigrationTask
return; return;
} }
base::DictionaryValue new_properties;
MigrateClientCertProperties(service_path, properties, &new_properties);
MigrateNssProperties(service_path, properties, &new_properties);
if (new_properties.empty())
return;
SendPropertiesToShill(service_path, new_properties);
}
void MigrateClientCertProperties(const std::string& service_path,
const base::DictionaryValue& properties,
base::DictionaryValue* new_properties) {
int configured_slot_id = -1;
std::string pkcs11_id;
chromeos::client_cert::ConfigType config_type =
chromeos::client_cert::CONFIG_TYPE_NONE;
chromeos::client_cert::GetClientCertFromShillProperties(
properties, &config_type, &configured_slot_id, &pkcs11_id);
if (config_type == chromeos::client_cert::CONFIG_TYPE_NONE ||
pkcs11_id.empty()) {
return;
}
// OpenVPN configuration doesn't have a slot id to migrate.
if (config_type == chromeos::client_cert::CONFIG_TYPE_OPENVPN)
return;
int real_slot_id = -1;
scoped_refptr<net::X509Certificate> cert =
FindCertificateWithPkcs11Id(pkcs11_id, &real_slot_id);
if (!cert) {
LOG(WARNING) << "No matching cert found, removing the certificate "
"configuration from network " << service_path;
chromeos::client_cert::SetEmptyShillProperties(config_type,
new_properties);
return;
}
if (real_slot_id == -1) {
LOG(WARNING) << "Found a certificate without slot id.";
return;
}
if (cert && real_slot_id != configured_slot_id) {
VLOG(1) << "Network " << service_path
<< " is configured with no or an incorrect slot id.";
chromeos::client_cert::SetShillProperties(
config_type, real_slot_id, pkcs11_id, new_properties);
}
}
void MigrateNssProperties(const std::string& service_path,
const base::DictionaryValue& properties,
base::DictionaryValue* new_properties) {
std::string nss_key, pem_key, nickname; std::string nss_key, pem_key, nickname;
const base::ListValue* pem_property = NULL; const base::ListValue* pem_property = NULL;
UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE; UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE;
...@@ -99,7 +168,7 @@ class NetworkCertMigrator::MigrationTask ...@@ -99,7 +168,7 @@ class NetworkCertMigrator::MigrationTask
if (pem_property && !pem_property->empty()) { if (pem_property && !pem_property->empty()) {
VLOG(2) << "PEM already exists, clearing NSS property."; VLOG(2) << "PEM already exists, clearing NSS property.";
ClearNssProperty(service_path, nss_key); ClearNssProperty(nss_key, new_properties);
return; return;
} }
...@@ -117,7 +186,8 @@ class NetworkCertMigrator::MigrationTask ...@@ -117,7 +186,8 @@ class NetworkCertMigrator::MigrationTask
return; return;
} }
SetNssAndPemProperties(service_path, nss_key, pem_key, pem_encoded); ClearNssProperty(nss_key, new_properties);
SetPemProperty(pem_key, pem_encoded, new_properties);
} }
void GetNssAndPemProperties(const base::DictionaryValue& shill_properties, void GetNssAndPemProperties(const base::DictionaryValue& shill_properties,
...@@ -159,18 +229,25 @@ class NetworkCertMigrator::MigrationTask ...@@ -159,18 +229,25 @@ class NetworkCertMigrator::MigrationTask
} }
} }
void ClearNssProperty(const std::string& service_path, void ClearNssProperty(const std::string& nss_key,
const std::string& nss_key) { base::DictionaryValue* new_properties) {
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( new_properties->SetStringWithoutPathExpansion(nss_key, std::string());
dbus::ObjectPath(service_path), }
nss_key,
base::StringValue(std::string()), scoped_refptr<net::X509Certificate> FindCertificateWithPkcs11Id(
base::Bind( const std::string& pkcs11_id, int* slot_id) {
&MigrationTask::NotifyNetworkStateHandler, this, service_path), *slot_id = -1;
base::Bind(&network_handler::ShillErrorCallbackFunction, for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end();
"MigrationTask.SetProperty failed", ++it) {
service_path, int current_slot_id = -1;
network_handler::ErrorCallback())); std::string current_pkcs11_id =
CertLoader::GetPkcs11IdAndSlotForCert(**it, &current_slot_id);
if (current_pkcs11_id == pkcs11_id) {
*slot_id = current_slot_id;
return *it;
}
}
return NULL;
} }
scoped_refptr<net::X509Certificate> FindCertificateWithNickname( scoped_refptr<net::X509Certificate> FindCertificateWithNickname(
...@@ -183,19 +260,19 @@ class NetworkCertMigrator::MigrationTask ...@@ -183,19 +260,19 @@ class NetworkCertMigrator::MigrationTask
return NULL; return NULL;
} }
void SetNssAndPemProperties(const std::string& service_path, void SetPemProperty(const std::string& pem_key,
const std::string& nss_key, const std::string& pem_encoded_cert,
const std::string& pem_key, base::DictionaryValue* new_properties) {
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); scoped_ptr<base::ListValue> ca_cert_pems(new base::ListValue);
ca_cert_pems->AppendString(pem_encoded_cert); ca_cert_pems->AppendString(pem_encoded_cert);
new_properties.SetWithoutPathExpansion(pem_key, ca_cert_pems.release()); new_properties->SetWithoutPathExpansion(pem_key, ca_cert_pems.release());
}
void SendPropertiesToShill(const std::string& service_path,
const base::DictionaryValue& properties) {
DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
dbus::ObjectPath(service_path), dbus::ObjectPath(service_path),
new_properties, properties,
base::Bind( base::Bind(
&MigrationTask::NotifyNetworkStateHandler, this, service_path), &MigrationTask::NotifyNetworkStateHandler, this, service_path),
base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler, base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler,
...@@ -258,20 +335,26 @@ void NetworkCertMigrator::NetworkListChanged() { ...@@ -258,20 +335,26 @@ void NetworkCertMigrator::NetworkListChanged() {
VLOG(2) << "Certs not loaded yet."; VLOG(2) << "Certs not loaded yet.";
return; return;
} }
// Run the migration process from deprecated CaCertNssProperties to CaCertPem. // Run the migration process from deprecated CaCertNssProperties to CaCertPem
VLOG(2) << "Start NSS nickname to PEM migration."; // and to fix missing or incorrect slot ids of client certificates.
VLOG(2) << "Start certificate migration of network configurations.";
scoped_refptr<MigrationTask> helper(new MigrationTask( scoped_refptr<MigrationTask> helper(new MigrationTask(
CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr()));
NetworkStateHandler::NetworkStateList networks; NetworkStateHandler::NetworkStateList networks;
network_state_handler_->GetVisibleNetworkList(&networks); network_state_handler_->GetNetworkListByType(
NetworkTypePattern::Default(),
true, // only configured networks
false, // visible and not visible networks
0, // no count limit
&networks);
helper->Run(networks); helper->Run(networks);
} }
void NetworkCertMigrator::OnCertificatesLoaded( void NetworkCertMigrator::OnCertificatesLoaded(
const net::CertificateList& cert_list, const net::CertificateList& cert_list,
bool initial_load) { bool initial_load) {
// Maybe there are networks referring to certs (by NSS nickname) that were not // Maybe there are networks referring to certs that were not loaded before but
// loaded before but are now. // are now.
NetworkListChanged(); NetworkListChanged();
} }
......
...@@ -16,7 +16,7 @@ namespace chromeos { ...@@ -16,7 +16,7 @@ namespace chromeos {
class NetworkStateHandler; class NetworkStateHandler;
// Migrates network configurations from deprecated CaCertNSS properties to // Migrates network configurations from deprecated CaCertNSS properties to
// CaCertPEM. // CaCertPEM and incorrect or missing slot IDs of client certificates.
class CHROMEOS_EXPORT NetworkCertMigrator : public NetworkStateHandlerObserver, class CHROMEOS_EXPORT NetworkCertMigrator : public NetworkStateHandlerObserver,
public CertLoader::Observer { public CertLoader::Observer {
public: public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment