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.
5 #include "chromeos/network/network_cert_migrator.h"
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"
27 UMA_NETWORK_TYPE_OPENVPN
,
28 UMA_NETWORK_TYPE_IPSEC
,
29 UMA_NETWORK_TYPE_SIZE
,
32 // Copied from x509_certificate_model_nss.cc
33 std::string
GetNickname(const net::X509Certificate
& cert
) {
34 if (!cert
.os_cert_handle()->nickname
)
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);
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.
53 class NetworkCertMigrator::MigrationTask
54 : public base::RefCounted
<MigrationTask
> {
56 MigrationTask(const net::CertificateList
& certs
,
57 const base::WeakPtr
<NetworkCertMigrator
>& cert_migrator
)
59 cert_migrator_(cert_migrator
) {
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())
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(),
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.";
86 std::string nss_key
, pem_key
, nickname
;
87 const base::ListValue
* pem_property
= NULL
;
88 UMANetworkType uma_type
= UMA_NETWORK_TYPE_SIZE
;
90 GetNssAndPemProperties(
91 properties
, &nss_key
, &pem_key
, &pem_property
, &nickname
, &uma_type
);
93 return; // Didn't find any nickname.
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
);
100 if (pem_property
&& !pem_property
->empty()) {
101 VLOG(2) << "PEM already exists, clearing NSS property.";
102 ClearNssProperty(service_path
, nss_key
);
106 scoped_refptr
<net::X509Certificate
> cert
=
107 FindCertificateWithNickname(nickname
);
109 VLOG(2) << "No matching cert found.";
113 std::string pem_encoded
;
114 if (!net::X509Certificate::GetPEMEncoded(cert
->os_cert_handle(),
116 LOG(ERROR
) << "PEM encoding failed.";
120 SetNssAndPemProperties(service_path
, nss_key
, pem_key
, pem_encoded
);
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
) {
130 const char* read_prefix
;
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
},
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
);
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
);
162 void ClearNssProperty(const std::string
& service_path
,
163 const std::string
& nss_key
) {
164 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
165 dbus::ObjectPath(service_path
),
167 base::StringValue(std::string()),
169 &MigrationTask::NotifyNetworkStateHandler
, this, service_path
),
170 base::Bind(&network_handler::ShillErrorCallbackFunction
,
171 "MigrationTask.SetProperty failed",
173 network_handler::ErrorCallback()));
176 scoped_refptr
<net::X509Certificate
> FindCertificateWithNickname(
177 const std::string
& nickname
) {
178 for (net::CertificateList::iterator it
= certs_
.begin(); it
!= certs_
.end();
180 if (nickname
== GetNickname(**it
))
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());
196 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
197 dbus::ObjectPath(service_path
),
200 &MigrationTask::NotifyNetworkStateHandler
, this, service_path
),
201 base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler
,
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",
212 network_handler::ErrorCallback(),
215 NotifyNetworkStateHandler(service_path
);
218 void NotifyNetworkStateHandler(const std::string
& service_path
) {
219 if (!cert_migrator_
) {
220 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
223 cert_migrator_
->network_state_handler_
->RequestUpdateForNetwork(
228 friend class base::RefCounted
<MigrationTask
>;
229 virtual ~MigrationTask() {
232 net::CertificateList certs_
;
233 base::WeakPtr
<NetworkCertMigrator
> cert_migrator_
;
236 NetworkCertMigrator::NetworkCertMigrator()
237 : network_state_handler_(NULL
),
238 weak_ptr_factory_(this) {
241 NetworkCertMigrator::~NetworkCertMigrator() {
242 network_state_handler_
->RemoveObserver(this, FROM_HERE
);
243 if (CertLoader::IsInitialized())
244 CertLoader::Get()->RemoveObserver(this);
247 void 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
);
252 DCHECK(CertLoader::IsInitialized());
253 CertLoader::Get()->AddObserver(this);
256 void NetworkCertMigrator::NetworkListChanged() {
257 if (!CertLoader::Get()->certificates_loaded()) {
258 VLOG(2) << "Certs not loaded yet.";
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_
->GetNetworkList(&networks
);
267 helper
->Run(networks
);
270 void NetworkCertMigrator::OnCertificatesLoaded(
271 const net::CertificateList
& cert_list
,
273 // Maybe there are networks referring to certs (by NSS nickname) that were not
274 // loaded before but are now.
275 NetworkListChanged();
278 } // namespace chromeos