OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/network/network_cert_migrator.h" | 5 #include "chromeos/network/network_cert_migrator.h" |
6 | 6 |
7 #include <cert.h> | 7 #include <cert.h> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
13 #include "chromeos/dbus/dbus_thread_manager.h" | 13 #include "chromeos/dbus/dbus_thread_manager.h" |
14 #include "chromeos/dbus/shill_service_client.h" | 14 #include "chromeos/dbus/shill_service_client.h" |
15 #include "chromeos/network/client_cert_util.h" | 15 #include "chromeos/network/client_cert_util.h" |
16 #include "chromeos/network/network_handler_callbacks.h" | 16 #include "chromeos/network/network_handler_callbacks.h" |
17 #include "chromeos/network/network_state.h" | 17 #include "chromeos/network/network_state.h" |
18 #include "chromeos/network/network_state_handler.h" | 18 #include "chromeos/network/network_state_handler.h" |
19 #include "dbus/object_path.h" | 19 #include "dbus/object_path.h" |
20 #include "third_party/cros_system_api/dbus/service_constants.h" | 20 #include "third_party/cros_system_api/dbus/service_constants.h" |
21 | 21 |
22 namespace chromeos { | 22 namespace chromeos { |
23 | 23 |
24 namespace { | 24 // Migrates each network of |networks| with an invalid or missing slot ID in |
25 | 25 // their client certificate configuration. |
26 enum UMANetworkType { | |
27 UMA_NETWORK_TYPE_EAP, | |
28 UMA_NETWORK_TYPE_OPENVPN, | |
29 UMA_NETWORK_TYPE_IPSEC, | |
30 UMA_NETWORK_TYPE_SIZE, | |
31 }; | |
32 | |
33 // Copied from x509_certificate_model_nss.cc | |
34 std::string GetNickname(const net::X509Certificate& cert) { | |
35 if (!cert.os_cert_handle()->nickname) | |
36 return std::string(); | |
37 std::string name = cert.os_cert_handle()->nickname; | |
38 // Hack copied from mozilla: Cut off text before first :, which seems to | |
39 // just be the token name. | |
40 size_t colon_pos = name.find(':'); | |
41 if (colon_pos != std::string::npos) | |
42 name = name.substr(colon_pos + 1); | |
43 return name; | |
44 } | |
45 | |
46 } // namespace | |
47 | |
48 // Migrates each network of |networks| with a deprecated CaCertNss property to | |
49 // the respective CaCertPEM property and fixes an invalid or missing slot ID of | |
50 // a client certificate configuration. | |
51 // | |
52 // If a network already has a CaCertPEM property, then the NssProperty is | |
53 // cleared. Otherwise, the NssProperty is compared with | |
54 // the nickname of each certificate of |certs|. If a match is found, the | |
55 // CaCertPemProperty is set and the NssProperty is cleared. | |
56 // | 26 // |
57 // If a network with a client certificate configuration (i.e. a PKCS11 ID) is | 27 // If a network with a client certificate configuration (i.e. a PKCS11 ID) is |
58 // found, the configured client certificate is looked up. | 28 // found, the configured client certificate is looked up. |
59 // If the certificate is found, the currently configured slot ID (if any) is | 29 // If the certificate is found, the currently configured slot ID (if any) is |
60 // compared with the actual slot ID of the certificate and if required updated. | 30 // compared with the actual slot ID of the certificate and if required updated. |
61 // If the certificate is not found, the client certificate configuration is | 31 // If the certificate is not found, the client certificate configuration is |
62 // removed. | 32 // removed. |
63 // | 33 // |
64 // Only if necessary, a network will be notified. | 34 // Only if necessary, a network will be notified. |
65 class NetworkCertMigrator::MigrationTask | 35 class NetworkCertMigrator::MigrationTask |
66 : public base::RefCounted<MigrationTask> { | 36 : public base::RefCounted<MigrationTask> { |
67 public: | 37 public: |
68 MigrationTask(const net::CertificateList& certs, | 38 MigrationTask(const net::CertificateList& certs, |
69 const base::WeakPtr<NetworkCertMigrator>& cert_migrator) | 39 const base::WeakPtr<NetworkCertMigrator>& cert_migrator) |
70 : certs_(certs), | 40 : certs_(certs), |
71 cert_migrator_(cert_migrator) { | 41 cert_migrator_(cert_migrator) { |
72 } | 42 } |
73 | 43 |
74 void Run(const NetworkStateHandler::NetworkStateList& networks) { | 44 void Run(const NetworkStateHandler::NetworkStateList& networks) { |
75 // Request properties for each network that has a CaCertNssProperty set | 45 // Request properties for each network that could be configured with a |
76 // or which could be configured with a client certificate. | 46 // client certificate. |
77 for (NetworkStateHandler::NetworkStateList::const_iterator it = | 47 for (const NetworkState* network : networks) { |
78 networks.begin(); it != networks.end(); ++it) { | 48 if (network->security() != shill::kSecurity8021x && |
79 if (!(*it)->HasCACertNSS() && | 49 network->type() != shill::kTypeVPN && |
80 (*it)->security() != shill::kSecurity8021x && | 50 network->type() != shill::kTypeEthernetEap) { |
81 (*it)->type() != shill::kTypeVPN && | |
82 (*it)->type() != shill::kTypeEthernetEap) { | |
83 continue; | 51 continue; |
84 } | 52 } |
85 const std::string& service_path = (*it)->path(); | 53 const std::string& service_path = network->path(); |
86 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( | 54 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( |
87 dbus::ObjectPath(service_path), | 55 dbus::ObjectPath(service_path), |
88 base::Bind(&network_handler::GetPropertiesCallback, | 56 base::Bind(&network_handler::GetPropertiesCallback, |
89 base::Bind(&MigrationTask::MigrateNetwork, this), | 57 base::Bind(&MigrationTask::MigrateNetwork, this), |
90 network_handler::ErrorCallback(), | 58 network_handler::ErrorCallback(), |
91 service_path)); | 59 service_path)); |
92 } | 60 } |
93 } | 61 } |
94 | 62 |
95 void MigrateNetwork(const std::string& service_path, | 63 void MigrateNetwork(const std::string& service_path, |
96 const base::DictionaryValue& properties) { | 64 const base::DictionaryValue& properties) { |
97 if (!cert_migrator_) { | 65 if (!cert_migrator_) { |
98 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration."; | 66 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration."; |
99 return; | 67 return; |
100 } | 68 } |
101 | 69 |
102 base::DictionaryValue new_properties; | 70 base::DictionaryValue new_properties; |
103 MigrateClientCertProperties(service_path, properties, &new_properties); | 71 MigrateClientCertProperties(service_path, properties, &new_properties); |
104 MigrateNssProperties(service_path, properties, &new_properties); | |
105 | 72 |
106 if (new_properties.empty()) | 73 if (new_properties.empty()) |
107 return; | 74 return; |
108 SendPropertiesToShill(service_path, new_properties); | 75 SendPropertiesToShill(service_path, new_properties); |
109 } | 76 } |
110 | 77 |
111 void MigrateClientCertProperties(const std::string& service_path, | 78 void MigrateClientCertProperties(const std::string& service_path, |
112 const base::DictionaryValue& properties, | 79 const base::DictionaryValue& properties, |
113 base::DictionaryValue* new_properties) { | 80 base::DictionaryValue* new_properties) { |
114 int configured_slot_id = -1; | 81 int configured_slot_id = -1; |
(...skipping 27 matching lines...) Expand all Loading... |
142 } | 109 } |
143 | 110 |
144 if (cert.get() && real_slot_id != configured_slot_id) { | 111 if (cert.get() && real_slot_id != configured_slot_id) { |
145 VLOG(1) << "Network " << service_path | 112 VLOG(1) << "Network " << service_path |
146 << " is configured with no or an incorrect slot id."; | 113 << " is configured with no or an incorrect slot id."; |
147 chromeos::client_cert::SetShillProperties( | 114 chromeos::client_cert::SetShillProperties( |
148 config_type, real_slot_id, pkcs11_id, new_properties); | 115 config_type, real_slot_id, pkcs11_id, new_properties); |
149 } | 116 } |
150 } | 117 } |
151 | 118 |
152 void MigrateNssProperties(const std::string& service_path, | |
153 const base::DictionaryValue& properties, | |
154 base::DictionaryValue* new_properties) { | |
155 std::string nss_key, pem_key, nickname; | |
156 const base::ListValue* pem_property = NULL; | |
157 UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE; | |
158 | |
159 GetNssAndPemProperties( | |
160 properties, &nss_key, &pem_key, &pem_property, &nickname, &uma_type); | |
161 if (nickname.empty()) | |
162 return; // Didn't find any nickname. | |
163 | |
164 VLOG(2) << "Found NSS nickname to migrate. Property: " << nss_key | |
165 << ", network: " << service_path; | |
166 UMA_HISTOGRAM_ENUMERATION( | |
167 "Network.MigrationNssToPem", uma_type, UMA_NETWORK_TYPE_SIZE); | |
168 | |
169 if (pem_property && !pem_property->empty()) { | |
170 VLOG(2) << "PEM already exists, clearing NSS property."; | |
171 ClearNssProperty(nss_key, new_properties); | |
172 return; | |
173 } | |
174 | |
175 scoped_refptr<net::X509Certificate> cert = | |
176 FindCertificateWithNickname(nickname); | |
177 if (!cert.get()) { | |
178 VLOG(2) << "No matching cert found."; | |
179 return; | |
180 } | |
181 | |
182 std::string pem_encoded; | |
183 if (!net::X509Certificate::GetPEMEncoded(cert->os_cert_handle(), | |
184 &pem_encoded)) { | |
185 LOG(ERROR) << "PEM encoding failed."; | |
186 return; | |
187 } | |
188 | |
189 ClearNssProperty(nss_key, new_properties); | |
190 SetPemProperty(pem_key, pem_encoded, new_properties); | |
191 } | |
192 | |
193 void GetNssAndPemProperties(const base::DictionaryValue& shill_properties, | |
194 std::string* nss_key, | |
195 std::string* pem_key, | |
196 const base::ListValue** pem_property, | |
197 std::string* nickname, | |
198 UMANetworkType* uma_type) { | |
199 struct NssPem { | |
200 const char* read_prefix; | |
201 const char* nss_key; | |
202 const char* pem_key; | |
203 UMANetworkType uma_type; | |
204 } const kNssPemMap[] = { | |
205 { NULL, shill::kEapCaCertNssProperty, shill::kEapCaCertPemProperty, | |
206 UMA_NETWORK_TYPE_EAP }, | |
207 { shill::kProviderProperty, shill::kL2tpIpsecCaCertNssProperty, | |
208 shill::kL2tpIpsecCaCertPemProperty, UMA_NETWORK_TYPE_IPSEC }, | |
209 { shill::kProviderProperty, shill::kOpenVPNCaCertNSSProperty, | |
210 shill::kOpenVPNCaCertPemProperty, UMA_NETWORK_TYPE_OPENVPN }, | |
211 }; | |
212 | |
213 for (size_t i = 0; i < arraysize(kNssPemMap); ++i) { | |
214 const base::DictionaryValue* dict = &shill_properties; | |
215 if (kNssPemMap[i].read_prefix) { | |
216 shill_properties.GetDictionaryWithoutPathExpansion( | |
217 kNssPemMap[i].read_prefix, &dict); | |
218 if (!dict) | |
219 continue; | |
220 } | |
221 dict->GetStringWithoutPathExpansion(kNssPemMap[i].nss_key, nickname); | |
222 if (!nickname->empty()) { | |
223 *nss_key = kNssPemMap[i].nss_key; | |
224 *pem_key = kNssPemMap[i].pem_key; | |
225 *uma_type = kNssPemMap[i].uma_type; | |
226 dict->GetListWithoutPathExpansion(kNssPemMap[i].pem_key, pem_property); | |
227 return; | |
228 } | |
229 } | |
230 } | |
231 | |
232 void ClearNssProperty(const std::string& nss_key, | |
233 base::DictionaryValue* new_properties) { | |
234 new_properties->SetStringWithoutPathExpansion(nss_key, std::string()); | |
235 } | |
236 | |
237 scoped_refptr<net::X509Certificate> FindCertificateWithPkcs11Id( | 119 scoped_refptr<net::X509Certificate> FindCertificateWithPkcs11Id( |
238 const std::string& pkcs11_id, int* slot_id) { | 120 const std::string& pkcs11_id, int* slot_id) { |
239 *slot_id = -1; | 121 *slot_id = -1; |
240 for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end(); | 122 for (scoped_refptr<net::X509Certificate> cert : certs_) { |
241 ++it) { | |
242 int current_slot_id = -1; | 123 int current_slot_id = -1; |
243 std::string current_pkcs11_id = | 124 std::string current_pkcs11_id = |
244 CertLoader::GetPkcs11IdAndSlotForCert(**it, ¤t_slot_id); | 125 CertLoader::GetPkcs11IdAndSlotForCert(*cert, ¤t_slot_id); |
245 if (current_pkcs11_id == pkcs11_id) { | 126 if (current_pkcs11_id == pkcs11_id) { |
246 *slot_id = current_slot_id; | 127 *slot_id = current_slot_id; |
247 return *it; | 128 return cert; |
248 } | 129 } |
249 } | 130 } |
250 return NULL; | 131 return nullptr; |
251 } | |
252 | |
253 scoped_refptr<net::X509Certificate> FindCertificateWithNickname( | |
254 const std::string& nickname) { | |
255 for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end(); | |
256 ++it) { | |
257 if (nickname == GetNickname(**it)) | |
258 return *it; | |
259 } | |
260 return NULL; | |
261 } | |
262 | |
263 void SetPemProperty(const std::string& pem_key, | |
264 const std::string& pem_encoded_cert, | |
265 base::DictionaryValue* new_properties) { | |
266 scoped_ptr<base::ListValue> ca_cert_pems(new base::ListValue); | |
267 ca_cert_pems->AppendString(pem_encoded_cert); | |
268 new_properties->SetWithoutPathExpansion(pem_key, ca_cert_pems.release()); | |
269 } | 132 } |
270 | 133 |
271 void SendPropertiesToShill(const std::string& service_path, | 134 void SendPropertiesToShill(const std::string& service_path, |
272 const base::DictionaryValue& properties) { | 135 const base::DictionaryValue& properties) { |
273 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( | 136 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( |
274 dbus::ObjectPath(service_path), | 137 dbus::ObjectPath(service_path), properties, |
275 properties, | 138 base::Bind(&base::DoNothing), base::Bind(&LogError, service_path)); |
276 base::Bind( | |
277 &MigrationTask::NotifyNetworkStateHandler, this, service_path), | |
278 base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler, | |
279 this, | |
280 service_path)); | |
281 } | 139 } |
282 | 140 |
283 void LogErrorAndNotifyNetworkStateHandler(const std::string& service_path, | 141 static void LogError(const std::string& service_path, |
284 const std::string& error_name, | 142 const std::string& error_name, |
285 const std::string& error_message) { | 143 const std::string& error_message) { |
286 network_handler::ShillErrorCallbackFunction( | 144 network_handler::ShillErrorCallbackFunction( |
287 "MigrationTask.SetProperties failed", | 145 "MigrationTask.SetProperties failed", |
288 service_path, | 146 service_path, |
289 network_handler::ErrorCallback(), | 147 network_handler::ErrorCallback(), |
290 error_name, | 148 error_name, |
291 error_message); | 149 error_message); |
292 NotifyNetworkStateHandler(service_path); | |
293 } | |
294 | |
295 void NotifyNetworkStateHandler(const std::string& service_path) { | |
296 if (!cert_migrator_) { | |
297 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration."; | |
298 return; | |
299 } | |
300 cert_migrator_->network_state_handler_->RequestUpdateForNetwork( | |
301 service_path); | |
302 } | 150 } |
303 | 151 |
304 private: | 152 private: |
305 friend class base::RefCounted<MigrationTask>; | 153 friend class base::RefCounted<MigrationTask>; |
306 virtual ~MigrationTask() { | 154 virtual ~MigrationTask() { |
307 } | 155 } |
308 | 156 |
309 net::CertificateList certs_; | 157 net::CertificateList certs_; |
310 base::WeakPtr<NetworkCertMigrator> cert_migrator_; | 158 base::WeakPtr<NetworkCertMigrator> cert_migrator_; |
311 }; | 159 }; |
312 | 160 |
313 NetworkCertMigrator::NetworkCertMigrator() | 161 NetworkCertMigrator::NetworkCertMigrator() |
314 : network_state_handler_(NULL), | 162 : network_state_handler_(nullptr), |
315 weak_ptr_factory_(this) { | 163 weak_ptr_factory_(this) { |
316 } | 164 } |
317 | 165 |
318 NetworkCertMigrator::~NetworkCertMigrator() { | 166 NetworkCertMigrator::~NetworkCertMigrator() { |
319 network_state_handler_->RemoveObserver(this, FROM_HERE); | 167 network_state_handler_->RemoveObserver(this, FROM_HERE); |
320 if (CertLoader::IsInitialized()) | 168 if (CertLoader::IsInitialized()) |
321 CertLoader::Get()->RemoveObserver(this); | 169 CertLoader::Get()->RemoveObserver(this); |
322 } | 170 } |
323 | 171 |
324 void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) { | 172 void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) { |
325 DCHECK(network_state_handler); | 173 DCHECK(network_state_handler); |
326 network_state_handler_ = network_state_handler; | 174 network_state_handler_ = network_state_handler; |
327 network_state_handler_->AddObserver(this, FROM_HERE); | 175 network_state_handler_->AddObserver(this, FROM_HERE); |
328 | 176 |
329 DCHECK(CertLoader::IsInitialized()); | 177 DCHECK(CertLoader::IsInitialized()); |
330 CertLoader::Get()->AddObserver(this); | 178 CertLoader::Get()->AddObserver(this); |
331 } | 179 } |
332 | 180 |
333 void NetworkCertMigrator::NetworkListChanged() { | 181 void NetworkCertMigrator::NetworkListChanged() { |
334 if (!CertLoader::Get()->certificates_loaded()) { | 182 if (!CertLoader::Get()->certificates_loaded()) { |
335 VLOG(2) << "Certs not loaded yet."; | 183 VLOG(2) << "Certs not loaded yet."; |
336 return; | 184 return; |
337 } | 185 } |
338 // Run the migration process from deprecated CaCertNssProperties to CaCertPem | 186 // Run the migration process to fix missing or incorrect slot ids of client |
339 // and to fix missing or incorrect slot ids of client certificates. | 187 // certificates. |
340 VLOG(2) << "Start certificate migration of network configurations."; | 188 VLOG(2) << "Start certificate migration of network configurations."; |
341 scoped_refptr<MigrationTask> helper(new MigrationTask( | 189 scoped_refptr<MigrationTask> helper(new MigrationTask( |
342 CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); | 190 CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); |
343 NetworkStateHandler::NetworkStateList networks; | 191 NetworkStateHandler::NetworkStateList networks; |
344 network_state_handler_->GetNetworkListByType( | 192 network_state_handler_->GetNetworkListByType( |
345 NetworkTypePattern::Default(), | 193 NetworkTypePattern::Default(), |
346 true, // only configured networks | 194 true, // only configured networks |
347 false, // visible and not visible networks | 195 false, // visible and not visible networks |
348 0, // no count limit | 196 0, // no count limit |
349 &networks); | 197 &networks); |
350 helper->Run(networks); | 198 helper->Run(networks); |
351 } | 199 } |
352 | 200 |
353 void NetworkCertMigrator::OnCertificatesLoaded( | 201 void NetworkCertMigrator::OnCertificatesLoaded( |
354 const net::CertificateList& cert_list, | 202 const net::CertificateList& cert_list, |
355 bool initial_load) { | 203 bool initial_load) { |
356 // Maybe there are networks referring to certs that were not loaded before but | 204 if (initial_load) |
357 // are now. | 205 NetworkListChanged(); |
358 NetworkListChanged(); | |
359 } | 206 } |
360 | 207 |
361 } // namespace chromeos | 208 } // namespace chromeos |
OLD | NEW |