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