network_cert_migrator.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/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" 20 21namespace chromeos { 22 23namespace { 24 25enum UMANetworkType { 26 UMA_NETWORK_TYPE_EAP, 27 UMA_NETWORK_TYPE_OPENVPN, 28 UMA_NETWORK_TYPE_IPSEC, 29 UMA_NETWORK_TYPE_SIZE, 30}; 31 32// Copied from x509_certificate_model_nss.cc 33std::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; 43} 44 45} // namespace 46 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. 53class 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) { 60 } 61 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)); 76 } 77 } 78 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; 84 } 85 86 std::string nss_key, pem_key, nickname; 87 const base::ListValue* pem_property = NULL; 88 UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE; 89 90 GetNssAndPemProperties( 91 properties, &nss_key, &pem_key, &pem_property, &nickname, &uma_type); 92 if (nickname.empty()) 93 return; // Didn't find any nickname. 94 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); 99 100 if (pem_property && !pem_property->empty()) { 101 VLOG(2) << "PEM already exists, clearing NSS property."; 102 ClearNssProperty(service_path, nss_key); 103 return; 104 } 105 106 scoped_refptr<net::X509Certificate> cert = 107 FindCertificateWithNickname(nickname); 108 if (!cert) { 109 VLOG(2) << "No matching cert found."; 110 return; 111 } 112 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; 118 } 119 120 SetNssAndPemProperties(service_path, nss_key, pem_key, pem_encoded); 121 } 122 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 }, 141 }; 142 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; 150 } 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; 158 } 159 } 160 } 161 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())); 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()->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)); 204 } 205 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); 216 } 217 218 void NotifyNetworkStateHandler(const std::string& service_path) { 219 if (!cert_migrator_) { 220 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration."; 221 return; 222 } 223 cert_migrator_->network_state_handler_->RequestUpdateForNetwork( 224 service_path); 225 } 226 227 private: 228 friend class base::RefCounted<MigrationTask>; 229 virtual ~MigrationTask() { 230 } 231 232 net::CertificateList certs_; 233 base::WeakPtr<NetworkCertMigrator> cert_migrator_; 234}; 235 236NetworkCertMigrator::NetworkCertMigrator() 237 : network_state_handler_(NULL), 238 weak_ptr_factory_(this) { 239} 240 241NetworkCertMigrator::~NetworkCertMigrator() { 242 network_state_handler_->RemoveObserver(this, FROM_HERE); 243 if (CertLoader::IsInitialized()) 244 CertLoader::Get()->RemoveObserver(this); 245} 246 247void 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); 251 252 DCHECK(CertLoader::IsInitialized()); 253 CertLoader::Get()->AddObserver(this); 254} 255 256void NetworkCertMigrator::NetworkListChanged() { 257 if (!CertLoader::Get()->certificates_loaded()) { 258 VLOG(2) << "Certs not loaded yet."; 259 return; 260 } 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_->GetVisibleNetworkList(&networks); 267 helper->Run(networks); 268} 269 270void 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(); 276} 277 278} // namespace chromeos 279