Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chromeos / network / network_cert_migrator.cc
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/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"
21
22 namespace chromeos {
23
24 namespace {
25
26 enum UMANetworkType {
27   UMA_NETWORK_TYPE_EAP,
28   UMA_NETWORK_TYPE_OPENVPN,
29   UMA_NETWORK_TYPE_IPSEC,
30   UMA_NETWORK_TYPE_SIZE,
31 };
32
33 // Copied from x509_certificate_model_nss.cc
34 std::string GetNickname(const net::X509Certificate& cert) {
35   if (!cert.os_cert_handle()->nickname)
36     return std::string();
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);
43   return name;
44 }
45
46 }  // namespace
47
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.
51 //
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.
56 //
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
62 // removed.
63 //
64 // Only if necessary, a network will be notified.
65 class NetworkCertMigrator::MigrationTask
66     : public base::RefCounted<MigrationTask> {
67  public:
68   MigrationTask(const net::CertificateList& certs,
69                 const base::WeakPtr<NetworkCertMigrator>& cert_migrator)
70       : certs_(certs),
71         cert_migrator_(cert_migrator) {
72   }
73
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) {
83         continue;
84       }
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(),
91                      service_path));
92     }
93   }
94
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.";
99       return;
100     }
101
102     base::DictionaryValue new_properties;
103     MigrateClientCertProperties(service_path, properties, &new_properties);
104     MigrateNssProperties(service_path, properties, &new_properties);
105
106     if (new_properties.empty())
107       return;
108     SendPropertiesToShill(service_path, new_properties);
109   }
110
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 ||
121         pkcs11_id.empty()) {
122       return;
123     }
124
125     // OpenVPN configuration doesn't have a slot id to migrate.
126     if (config_type == chromeos::client_cert::CONFIG_TYPE_OPENVPN)
127       return;
128
129     int real_slot_id = -1;
130     scoped_refptr<net::X509Certificate> cert =
131         FindCertificateWithPkcs11Id(pkcs11_id, &real_slot_id);
132     if (!cert.get()) {
133       LOG(WARNING) << "No matching cert found, removing the certificate "
134                       "configuration from network " << service_path;
135       chromeos::client_cert::SetEmptyShillProperties(config_type,
136                                                      new_properties);
137       return;
138     }
139     if (real_slot_id == -1) {
140       LOG(WARNING) << "Found a certificate without slot id.";
141       return;
142     }
143
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);
149     }
150   }
151
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;
158
159     GetNssAndPemProperties(
160         properties, &nss_key, &pem_key, &pem_property, &nickname, &uma_type);
161     if (nickname.empty())
162       return;  // Didn't find any nickname.
163
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);
168
169     if (pem_property && !pem_property->empty()) {
170       VLOG(2) << "PEM already exists, clearing NSS property.";
171       ClearNssProperty(nss_key, new_properties);
172       return;
173     }
174
175     scoped_refptr<net::X509Certificate> cert =
176         FindCertificateWithNickname(nickname);
177     if (!cert.get()) {
178       VLOG(2) << "No matching cert found.";
179       return;
180     }
181
182     std::string pem_encoded;
183     if (!net::X509Certificate::GetPEMEncoded(cert->os_cert_handle(),
184                                              &pem_encoded)) {
185       LOG(ERROR) << "PEM encoding failed.";
186       return;
187     }
188
189     ClearNssProperty(nss_key, new_properties);
190     SetPemProperty(pem_key, pem_encoded, new_properties);
191   }
192
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) {
199     struct NssPem {
200       const char* read_prefix;
201       const char* nss_key;
202       const char* pem_key;
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 },
211     };
212
213     for (size_t i = 0; i < arraysize(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);
218         if (!dict)
219           continue;
220       }
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);
227         return;
228       }
229     }
230   }
231
232   void ClearNssProperty(const std::string& nss_key,
233                         base::DictionaryValue* new_properties) {
234     new_properties->SetStringWithoutPathExpansion(nss_key, std::string());
235   }
236
237   scoped_refptr<net::X509Certificate> FindCertificateWithPkcs11Id(
238       const std::string& pkcs11_id, int* slot_id) {
239     *slot_id = -1;
240     for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end();
241          ++it) {
242       int current_slot_id = -1;
243       std::string current_pkcs11_id =
244           CertLoader::GetPkcs11IdAndSlotForCert(**it, &current_slot_id);
245       if (current_pkcs11_id == pkcs11_id) {
246         *slot_id = current_slot_id;
247         return *it;
248       }
249     }
250     return NULL;
251   }
252
253   scoped_refptr<net::X509Certificate> FindCertificateWithNickname(
254       const std::string& nickname) {
255     for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end();
256          ++it) {
257       if (nickname == GetNickname(**it))
258         return *it;
259     }
260     return NULL;
261   }
262
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());
269   }
270
271   void SendPropertiesToShill(const std::string& service_path,
272                              const base::DictionaryValue& properties) {
273     DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
274         dbus::ObjectPath(service_path),
275         properties,
276         base::Bind(
277             &MigrationTask::NotifyNetworkStateHandler, this, service_path),
278         base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler,
279                    this,
280                    service_path));
281   }
282
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",
288         service_path,
289         network_handler::ErrorCallback(),
290         error_name,
291         error_message);
292     NotifyNetworkStateHandler(service_path);
293   }
294
295   void NotifyNetworkStateHandler(const std::string& service_path) {
296     if (!cert_migrator_) {
297       VLOG(2) << "NetworkCertMigrator already destroyed. Aborting migration.";
298       return;
299     }
300     cert_migrator_->network_state_handler_->RequestUpdateForNetwork(
301         service_path);
302   }
303
304  private:
305   friend class base::RefCounted<MigrationTask>;
306   virtual ~MigrationTask() {
307   }
308
309   net::CertificateList certs_;
310   base::WeakPtr<NetworkCertMigrator> cert_migrator_;
311 };
312
313 NetworkCertMigrator::NetworkCertMigrator()
314     : network_state_handler_(NULL),
315       weak_ptr_factory_(this) {
316 }
317
318 NetworkCertMigrator::~NetworkCertMigrator() {
319   network_state_handler_->RemoveObserver(this, FROM_HERE);
320   if (CertLoader::IsInitialized())
321     CertLoader::Get()->RemoveObserver(this);
322 }
323
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);
328
329   DCHECK(CertLoader::IsInitialized());
330   CertLoader::Get()->AddObserver(this);
331 }
332
333 void NetworkCertMigrator::NetworkListChanged() {
334   if (!CertLoader::Get()->certificates_loaded()) {
335     VLOG(2) << "Certs not loaded yet.";
336     return;
337   }
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
348       0,     // no count limit
349       &networks);
350   helper->Run(networks);
351 }
352
353 void NetworkCertMigrator::OnCertificatesLoaded(
354     const net::CertificateList& cert_list,
355     bool initial_load) {
356   // Maybe there are networks referring to certs that were not loaded before but
357   // are now.
358   NetworkListChanged();
359 }
360
361 }  // namespace chromeos