Index: chromeos/network/network_cert_migrator.cc |
diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c8128d357381f5024f40dc124a640559ea916df1 |
--- /dev/null |
+++ b/chromeos/network/network_cert_migrator.cc |
@@ -0,0 +1,259 @@ |
+// Copyright (c) 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 |