Refactor HashedExtensionMetrics into ExtensionsMetricsProvider.
[chromium-blink-merge.git] / chromeos / network / network_cert_migrator.cc
blob693ebf587628d61e037955e35005e15deb04082c
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/network/network_cert_migrator.h"
7 #include <cert.h>
8 #include <string>
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/shill_service_client.h"
15 #include "chromeos/network/network_handler_callbacks.h"
16 #include "chromeos/network/network_state.h"
17 #include "chromeos/network/network_state_handler.h"
18 #include "dbus/object_path.h"
19 #include "third_party/cros_system_api/dbus/service_constants.h"
21 namespace chromeos {
23 namespace {
25 enum UMANetworkType {
26 UMA_NETWORK_TYPE_EAP,
27 UMA_NETWORK_TYPE_OPENVPN,
28 UMA_NETWORK_TYPE_IPSEC,
29 UMA_NETWORK_TYPE_SIZE,
32 // Copied from x509_certificate_model_nss.cc
33 std::string GetNickname(const net::X509Certificate& cert) {
34 if (!cert.os_cert_handle()->nickname)
35 return std::string();
36 std::string name = cert.os_cert_handle()->nickname;
37 // Hack copied from mozilla: Cut off text before first :, which seems to
38 // just be the token name.
39 size_t colon_pos = name.find(':');
40 if (colon_pos != std::string::npos)
41 name = name.substr(colon_pos + 1);
42 return name;
45 } // namespace
47 // Checks which of the given |networks| has one of the deprecated
48 // CaCertNssProperties set. If such a network already has a CaCertPEM property,
49 // then the NssProperty is cleared. Otherwise, the NssProperty is compared with
50 // the nickname of each certificate of |certs|. If a match is found, then the
51 // CaCertPemProperty is set and the NssProperty is cleared. Otherwise, the
52 // network is not modified.
53 class NetworkCertMigrator::MigrationTask
54 : public base::RefCounted<MigrationTask> {
55 public:
56 MigrationTask(const net::CertificateList& certs,
57 const base::WeakPtr<NetworkCertMigrator>& cert_migrator)
58 : certs_(certs),
59 cert_migrator_(cert_migrator) {
62 void Run(const NetworkStateHandler::NetworkStateList& networks) {
63 // Request properties for each network that has a CaCertNssProperty set
64 // according to the NetworkStateHandler.
65 for (NetworkStateHandler::NetworkStateList::const_iterator it =
66 networks.begin(); it != networks.end(); ++it) {
67 if (!(*it)->HasCACertNSS())
68 continue;
69 const std::string& service_path = (*it)->path();
70 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
71 dbus::ObjectPath(service_path),
72 base::Bind(&network_handler::GetPropertiesCallback,
73 base::Bind(&MigrationTask::MigrateNetwork, this),
74 network_handler::ErrorCallback(),
75 service_path));
79 void MigrateNetwork(const std::string& service_path,
80 const base::DictionaryValue& properties) {
81 if (!cert_migrator_) {
82 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
83 return;
86 std::string nss_key, pem_key, nickname;
87 const base::ListValue* pem_property = NULL;
88 UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE;
90 GetNssAndPemProperties(
91 properties, &nss_key, &pem_key, &pem_property, &nickname, &uma_type);
92 if (nickname.empty())
93 return; // Didn't find any nickname.
95 VLOG(2) << "Found NSS nickname to migrate. Property: " << nss_key
96 << ", network: " << service_path;
97 UMA_HISTOGRAM_ENUMERATION(
98 "Network.MigrationNssToPem", uma_type, UMA_NETWORK_TYPE_SIZE);
100 if (pem_property && !pem_property->empty()) {
101 VLOG(2) << "PEM already exists, clearing NSS property.";
102 ClearNssProperty(service_path, nss_key);
103 return;
106 scoped_refptr<net::X509Certificate> cert =
107 FindCertificateWithNickname(nickname);
108 if (!cert) {
109 VLOG(2) << "No matching cert found.";
110 return;
113 std::string pem_encoded;
114 if (!net::X509Certificate::GetPEMEncoded(cert->os_cert_handle(),
115 &pem_encoded)) {
116 LOG(ERROR) << "PEM encoding failed.";
117 return;
120 SetNssAndPemProperties(service_path, nss_key, pem_key, pem_encoded);
123 void GetNssAndPemProperties(const base::DictionaryValue& shill_properties,
124 std::string* nss_key,
125 std::string* pem_key,
126 const base::ListValue** pem_property,
127 std::string* nickname,
128 UMANetworkType* uma_type) {
129 struct NssPem {
130 const char* read_prefix;
131 const char* nss_key;
132 const char* pem_key;
133 UMANetworkType uma_type;
134 } const kNssPemMap[] = {
135 { NULL, shill::kEapCaCertNssProperty, shill::kEapCaCertPemProperty,
136 UMA_NETWORK_TYPE_EAP },
137 { shill::kProviderProperty, shill::kL2tpIpsecCaCertNssProperty,
138 shill::kL2tpIpsecCaCertPemProperty, UMA_NETWORK_TYPE_IPSEC },
139 { shill::kProviderProperty, shill::kOpenVPNCaCertNSSProperty,
140 shill::kOpenVPNCaCertPemProperty, UMA_NETWORK_TYPE_OPENVPN },
143 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNssPemMap); ++i) {
144 const base::DictionaryValue* dict = &shill_properties;
145 if (kNssPemMap[i].read_prefix) {
146 shill_properties.GetDictionaryWithoutPathExpansion(
147 kNssPemMap[i].read_prefix, &dict);
148 if (!dict)
149 continue;
151 dict->GetStringWithoutPathExpansion(kNssPemMap[i].nss_key, nickname);
152 if (!nickname->empty()) {
153 *nss_key = kNssPemMap[i].nss_key;
154 *pem_key = kNssPemMap[i].pem_key;
155 *uma_type = kNssPemMap[i].uma_type;
156 dict->GetListWithoutPathExpansion(kNssPemMap[i].pem_key, pem_property);
157 return;
162 void ClearNssProperty(const std::string& service_path,
163 const std::string& nss_key) {
164 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
165 dbus::ObjectPath(service_path),
166 nss_key,
167 base::StringValue(std::string()),
168 base::Bind(
169 &MigrationTask::NotifyNetworkStateHandler, this, service_path),
170 base::Bind(&network_handler::ShillErrorCallbackFunction,
171 "MigrationTask.SetProperty failed",
172 service_path,
173 network_handler::ErrorCallback()));
176 scoped_refptr<net::X509Certificate> FindCertificateWithNickname(
177 const std::string& nickname) {
178 for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end();
179 ++it) {
180 if (nickname == GetNickname(**it))
181 return *it;
183 return NULL;
186 void SetNssAndPemProperties(const std::string& service_path,
187 const std::string& nss_key,
188 const std::string& pem_key,
189 const std::string& pem_encoded_cert) {
190 base::DictionaryValue new_properties;
191 new_properties.SetStringWithoutPathExpansion(nss_key, std::string());
192 scoped_ptr<base::ListValue> ca_cert_pems(new base::ListValue);
193 ca_cert_pems->AppendString(pem_encoded_cert);
194 new_properties.SetWithoutPathExpansion(pem_key, ca_cert_pems.release());
196 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
197 dbus::ObjectPath(service_path),
198 new_properties,
199 base::Bind(
200 &MigrationTask::NotifyNetworkStateHandler, this, service_path),
201 base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler,
202 this,
203 service_path));
206 void LogErrorAndNotifyNetworkStateHandler(const std::string& service_path,
207 const std::string& error_name,
208 const std::string& error_message) {
209 network_handler::ShillErrorCallbackFunction(
210 "MigrationTask.SetProperties failed",
211 service_path,
212 network_handler::ErrorCallback(),
213 error_name,
214 error_message);
215 NotifyNetworkStateHandler(service_path);
218 void NotifyNetworkStateHandler(const std::string& service_path) {
219 if (!cert_migrator_) {
220 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
221 return;
223 cert_migrator_->network_state_handler_->RequestUpdateForNetwork(
224 service_path);
227 private:
228 friend class base::RefCounted<MigrationTask>;
229 virtual ~MigrationTask() {
232 net::CertificateList certs_;
233 base::WeakPtr<NetworkCertMigrator> cert_migrator_;
236 NetworkCertMigrator::NetworkCertMigrator()
237 : network_state_handler_(NULL),
238 weak_ptr_factory_(this) {
241 NetworkCertMigrator::~NetworkCertMigrator() {
242 network_state_handler_->RemoveObserver(this, FROM_HERE);
243 if (CertLoader::IsInitialized())
244 CertLoader::Get()->RemoveObserver(this);
247 void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) {
248 DCHECK(network_state_handler);
249 network_state_handler_ = network_state_handler;
250 network_state_handler_->AddObserver(this, FROM_HERE);
252 DCHECK(CertLoader::IsInitialized());
253 CertLoader::Get()->AddObserver(this);
256 void NetworkCertMigrator::NetworkListChanged() {
257 if (!CertLoader::Get()->certificates_loaded()) {
258 VLOG(2) << "Certs not loaded yet.";
259 return;
261 // Run the migration process from deprecated CaCertNssProperties to CaCertPem.
262 VLOG(2) << "Start NSS nickname to PEM migration.";
263 scoped_refptr<MigrationTask> helper(new MigrationTask(
264 CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr()));
265 NetworkStateHandler::NetworkStateList networks;
266 network_state_handler_->GetNetworkList(&networks);
267 helper->Run(networks);
270 void NetworkCertMigrator::OnCertificatesLoaded(
271 const net::CertificateList& cert_list,
272 bool initial_load) {
273 // Maybe there are networks referring to certs (by NSS nickname) that were not
274 // loaded before but are now.
275 NetworkListChanged();
278 } // namespace chromeos