| 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 |