network_cert_migrator.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
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() 164 ->SetProperty(dbus::ObjectPath(service_path), 165 nss_key, 166 base::StringValue(std::string()), 167 base::Bind(&base::DoNothing), 168 base::Bind(&network_handler::ShillErrorCallbackFunction, 169 "MigrationTask.SetProperty failed", 170 service_path, 171 network_handler::ErrorCallback())); 172 cert_migrator_->network_state_handler_ 173 ->RequestUpdateForNetwork(service_path); 174 } 175 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; 182 } 183 return NULL; 184 } 185 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()); 195 196 DBusThreadManager::Get()->GetShillServiceClient() 197 ->SetProperties(dbus::ObjectPath(service_path), 198 new_properties, 199 base::Bind(&base::DoNothing), 200 base::Bind(&network_handler::ShillErrorCallbackFunction, 201 "MigrationTask.SetProperties failed", 202 service_path, 203 network_handler::ErrorCallback())); 204 cert_migrator_->network_state_handler_ 205 ->RequestUpdateForNetwork(service_path); 206 } 207 208 private: 209 friend class base::RefCounted<MigrationTask>; 210 virtual ~MigrationTask() { 211 } 212 213 net::CertificateList certs_; 214 base::WeakPtr<NetworkCertMigrator> cert_migrator_; 215}; 216 217NetworkCertMigrator::NetworkCertMigrator() 218 : network_state_handler_(NULL), 219 weak_ptr_factory_(this) { 220} 221 222NetworkCertMigrator::~NetworkCertMigrator() { 223 network_state_handler_->RemoveObserver(this, FROM_HERE); 224 if (CertLoader::IsInitialized()) 225 CertLoader::Get()->RemoveObserver(this); 226} 227 228void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) { 229 DCHECK(network_state_handler); 230 network_state_handler_ = network_state_handler; 231 network_state_handler_->AddObserver(this, FROM_HERE); 232 233 DCHECK(CertLoader::IsInitialized()); 234 CertLoader::Get()->AddObserver(this); 235} 236 237void NetworkCertMigrator::NetworkListChanged() { 238 if (!CertLoader::Get()->certificates_loaded()) { 239 VLOG(2) << "Certs not loaded yet."; 240 return; 241 } 242 // Run the migration process from deprecated CaCertNssProperties to CaCertPem. 243 VLOG(2) << "Start NSS nickname to PEM migration."; 244 scoped_refptr<MigrationTask> helper(new MigrationTask( 245 CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); 246 NetworkStateHandler::NetworkStateList networks; 247 network_state_handler_->GetNetworkList(&networks); 248 helper->Run(networks); 249} 250 251void NetworkCertMigrator::OnCertificatesLoaded( 252 const net::CertificateList& cert_list, 253 bool initial_load) { 254 // Maybe there are networks referring to certs (by NSS nickname) that were not 255 // loaded before but are now. 256 NetworkListChanged(); 257} 258 259} // namespace chromeos 260