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/client_cert_util.h"
16 #include "chromeos/network/network_handler_callbacks.h"
17 #include "chromeos/network/network_state.h"
18 #include "chromeos/network/network_state_handler.h"
19 #include "dbus/object_path.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
28 UMA_NETWORK_TYPE_OPENVPN
,
29 UMA_NETWORK_TYPE_IPSEC
,
30 UMA_NETWORK_TYPE_SIZE
,
33 // Copied from x509_certificate_model_nss.cc
34 std::string
GetNickname(const net::X509Certificate
& cert
) {
35 if (!cert
.os_cert_handle()->nickname
)
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);
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.
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.
57 // If a network with a client certificate configuration (i.e. a PKCS11 ID) is
58 // found, the configured client certificate is looked up.
59 // 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.
61 // If the certificate is not found, the client certificate configuration is
64 // Only if necessary, a network will be notified.
65 class NetworkCertMigrator::MigrationTask
66 : public base::RefCounted
<MigrationTask
> {
68 MigrationTask(const net::CertificateList
& certs
,
69 const base::WeakPtr
<NetworkCertMigrator
>& cert_migrator
)
71 cert_migrator_(cert_migrator
) {
74 void Run(const NetworkStateHandler::NetworkStateList
& networks
) {
75 // Request properties for each network that has a CaCertNssProperty set
76 // or which could be configured with a client certificate.
77 for (NetworkStateHandler::NetworkStateList::const_iterator it
=
78 networks
.begin(); it
!= networks
.end(); ++it
) {
79 if (!(*it
)->HasCACertNSS() &&
80 (*it
)->security() != shill::kSecurity8021x
&&
81 (*it
)->type() != shill::kTypeVPN
&&
82 (*it
)->type() != shill::kTypeEthernetEap
) {
85 const std::string
& service_path
= (*it
)->path();
86 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
87 dbus::ObjectPath(service_path
),
88 base::Bind(&network_handler::GetPropertiesCallback
,
89 base::Bind(&MigrationTask::MigrateNetwork
, this),
90 network_handler::ErrorCallback(),
95 void MigrateNetwork(const std::string
& service_path
,
96 const base::DictionaryValue
& properties
) {
97 if (!cert_migrator_
) {
98 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
102 base::DictionaryValue new_properties
;
103 MigrateClientCertProperties(service_path
, properties
, &new_properties
);
104 MigrateNssProperties(service_path
, properties
, &new_properties
);
106 if (new_properties
.empty())
108 SendPropertiesToShill(service_path
, new_properties
);
111 void MigrateClientCertProperties(const std::string
& service_path
,
112 const base::DictionaryValue
& properties
,
113 base::DictionaryValue
* new_properties
) {
114 int configured_slot_id
= -1;
115 std::string pkcs11_id
;
116 chromeos::client_cert::ConfigType config_type
=
117 chromeos::client_cert::CONFIG_TYPE_NONE
;
118 chromeos::client_cert::GetClientCertFromShillProperties(
119 properties
, &config_type
, &configured_slot_id
, &pkcs11_id
);
120 if (config_type
== chromeos::client_cert::CONFIG_TYPE_NONE
||
125 // OpenVPN configuration doesn't have a slot id to migrate.
126 if (config_type
== chromeos::client_cert::CONFIG_TYPE_OPENVPN
)
129 int real_slot_id
= -1;
130 scoped_refptr
<net::X509Certificate
> cert
=
131 FindCertificateWithPkcs11Id(pkcs11_id
, &real_slot_id
);
133 LOG(WARNING
) << "No matching cert found, removing the certificate "
134 "configuration from network " << service_path
;
135 chromeos::client_cert::SetEmptyShillProperties(config_type
,
139 if (real_slot_id
== -1) {
140 LOG(WARNING
) << "Found a certificate without slot id.";
144 if (cert
.get() && real_slot_id
!= configured_slot_id
) {
145 VLOG(1) << "Network " << service_path
146 << " is configured with no or an incorrect slot id.";
147 chromeos::client_cert::SetShillProperties(
148 config_type
, real_slot_id
, pkcs11_id
, new_properties
);
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
;
159 GetNssAndPemProperties(
160 properties
, &nss_key
, &pem_key
, &pem_property
, &nickname
, &uma_type
);
161 if (nickname
.empty())
162 return; // Didn't find any nickname.
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
);
169 if (pem_property
&& !pem_property
->empty()) {
170 VLOG(2) << "PEM already exists, clearing NSS property.";
171 ClearNssProperty(nss_key
, new_properties
);
175 scoped_refptr
<net::X509Certificate
> cert
=
176 FindCertificateWithNickname(nickname
);
178 VLOG(2) << "No matching cert found.";
182 std::string pem_encoded
;
183 if (!net::X509Certificate::GetPEMEncoded(cert
->os_cert_handle(),
185 LOG(ERROR
) << "PEM encoding failed.";
189 ClearNssProperty(nss_key
, new_properties
);
190 SetPemProperty(pem_key
, pem_encoded
, new_properties
);
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
) {
200 const char* read_prefix
;
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
},
213 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(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
);
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
);
232 void ClearNssProperty(const std::string
& nss_key
,
233 base::DictionaryValue
* new_properties
) {
234 new_properties
->SetStringWithoutPathExpansion(nss_key
, std::string());
237 scoped_refptr
<net::X509Certificate
> FindCertificateWithPkcs11Id(
238 const std::string
& pkcs11_id
, int* slot_id
) {
240 for (net::CertificateList::iterator it
= certs_
.begin(); it
!= certs_
.end();
242 int current_slot_id
= -1;
243 std::string current_pkcs11_id
=
244 CertLoader::GetPkcs11IdAndSlotForCert(**it
, ¤t_slot_id
);
245 if (current_pkcs11_id
== pkcs11_id
) {
246 *slot_id
= current_slot_id
;
253 scoped_refptr
<net::X509Certificate
> FindCertificateWithNickname(
254 const std::string
& nickname
) {
255 for (net::CertificateList::iterator it
= certs_
.begin(); it
!= certs_
.end();
257 if (nickname
== GetNickname(**it
))
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());
271 void SendPropertiesToShill(const std::string
& service_path
,
272 const base::DictionaryValue
& properties
) {
273 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
274 dbus::ObjectPath(service_path
),
277 &MigrationTask::NotifyNetworkStateHandler
, this, service_path
),
278 base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler
,
283 void LogErrorAndNotifyNetworkStateHandler(const std::string
& service_path
,
284 const std::string
& error_name
,
285 const std::string
& error_message
) {
286 network_handler::ShillErrorCallbackFunction(
287 "MigrationTask.SetProperties failed",
289 network_handler::ErrorCallback(),
292 NotifyNetworkStateHandler(service_path
);
295 void NotifyNetworkStateHandler(const std::string
& service_path
) {
296 if (!cert_migrator_
) {
297 VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
300 cert_migrator_
->network_state_handler_
->RequestUpdateForNetwork(
305 friend class base::RefCounted
<MigrationTask
>;
306 virtual ~MigrationTask() {
309 net::CertificateList certs_
;
310 base::WeakPtr
<NetworkCertMigrator
> cert_migrator_
;
313 NetworkCertMigrator::NetworkCertMigrator()
314 : network_state_handler_(NULL
),
315 weak_ptr_factory_(this) {
318 NetworkCertMigrator::~NetworkCertMigrator() {
319 network_state_handler_
->RemoveObserver(this, FROM_HERE
);
320 if (CertLoader::IsInitialized())
321 CertLoader::Get()->RemoveObserver(this);
324 void NetworkCertMigrator::Init(NetworkStateHandler
* network_state_handler
) {
325 DCHECK(network_state_handler
);
326 network_state_handler_
= network_state_handler
;
327 network_state_handler_
->AddObserver(this, FROM_HERE
);
329 DCHECK(CertLoader::IsInitialized());
330 CertLoader::Get()->AddObserver(this);
333 void NetworkCertMigrator::NetworkListChanged() {
334 if (!CertLoader::Get()->certificates_loaded()) {
335 VLOG(2) << "Certs not loaded yet.";
338 // Run the migration process from deprecated CaCertNssProperties to CaCertPem
339 // and to fix missing or incorrect slot ids of client certificates.
340 VLOG(2) << "Start certificate migration of network configurations.";
341 scoped_refptr
<MigrationTask
> helper(new MigrationTask(
342 CertLoader::Get()->cert_list(), weak_ptr_factory_
.GetWeakPtr()));
343 NetworkStateHandler::NetworkStateList networks
;
344 network_state_handler_
->GetNetworkListByType(
345 NetworkTypePattern::Default(),
346 true, // only configured networks
347 false, // visible and not visible networks
350 helper
->Run(networks
);
353 void NetworkCertMigrator::OnCertificatesLoaded(
354 const net::CertificateList
& cert_list
,
356 // Maybe there are networks referring to certs that were not loaded before but
358 NetworkListChanged();
361 } // namespace chromeos