Chromium Code Reviews| Index: chromeos/network/network_cert_handler.cc |
| diff --git a/chromeos/network/network_cert_handler.cc b/chromeos/network/network_cert_handler.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4b02bda742e52f25e61e5af744e5b2741123c397 |
| --- /dev/null |
| +++ b/chromeos/network/network_cert_handler.cc |
| @@ -0,0 +1,244 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chromeos/network/network_cert_handler.h" |
| + |
| +#include <cert.h> |
| +#include <string> |
| + |
| +#include "base/location.h" |
| +#include "base/metrics/histogram.h" |
| +#include "chromeos/dbus/dbus_thread_manager.h" |
| +#include "chromeos/dbus/shill_service_client.h" |
| +#include "chromeos/network/network_handler_callbacks.h" |
| +#include "chromeos/network/network_state.h" |
| +#include "chromeos/network/network_state_handler.h" |
| +#include "dbus/object_path.h" |
| +#include "third_party/cros_system_api/dbus/service_constants.h" |
| + |
| +namespace chromeos { |
| + |
| +namespace { |
| + |
| +typedef std::vector<std::string> Nicknames; |
|
pneubeck (no reviews)
2013/07/29 08:41:39
Not used, to be removed.
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + |
| +enum UMANetworkType { |
| + UMA_NETWORK_TYPE_EAP, |
| + UMA_NETWORK_TYPE_OPENVPN, |
| + UMA_NETWORK_TYPE_IPSEC, |
| + UMA_NETWORK_TYPE_SIZE, |
| +}; |
| + |
| +// Copied from x509_certificate_model_nss.cc |
| +std::string GetNickname(const net::X509Certificate::OSCertHandle& cert_handle) { |
| + if (!cert_handle->nickname) |
| + return std::string(); |
| + std::string name = cert_handle->nickname; |
| + // Hack copied from mozilla: Cut off text before first :, which seems to |
| + // just be the token name. |
| + size_t colon_pos = name.find(':'); |
| + if (colon_pos != std::string::npos) |
| + name = name.substr(colon_pos + 1); |
| + return name; |
| +} |
| + |
| +} // namespace |
| + |
| +// Checks which of the given |networks| has one of the deprecated |
| +// CaCertNssProperties set. If such a network already has a CaCertPEM property, |
| +// then the NssProperty is cleared. Otherwise, the NssProperty is compared with |
| +// the nickname of each certificate of |certs|. If a match is found, then the |
| +// CaCertPemProperty is set and the NssProperty is cleared. Otherwise, the |
| +// network is not modified. |
| +class NetworkCertHandler::MigrationHelper |
| + : public base::RefCounted<MigrationHelper> { |
| + public: |
| + MigrationHelper(const net::CertificateList& certs, |
| + const base::WeakPtr<NetworkCertHandler>& cert_handler) |
| + : certs_(certs), |
| + cert_handler_(cert_handler) { |
| + } |
| + |
| + void Run(const NetworkStateHandler::NetworkStateList& networks) { |
| + // Request properties for each network that has a CaCertNssProperty set |
| + // according to the NetworkStateHandler. |
| + for (NetworkStateHandler::NetworkStateList::const_iterator it = |
| + networks.begin(); |
| + it != networks.end(); |
| + ++it) { |
| + if (!(*it)->HasCACertNSS()) |
| + continue; |
| + const std::string& service_path = (*it)->path(); |
| + DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( |
| + dbus::ObjectPath(service_path), |
| + base::Bind( |
| + &MigrationHelper::GetPropertiesCallback, this, service_path)); |
| + } |
| + } |
| + |
| + void GetPropertiesCallback(const std::string& service_path, |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
This does too damn much given the unsuspecting nam
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + DBusMethodCallStatus call_status, |
| + const base::DictionaryValue& properties) { |
| + if (!cert_handler_) { |
| + VLOG(2) << "NetworkCertHandler already destroyed. Aborting migration."; |
| + return; // The CertHandler is destroyed, stop. |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
Comment is redundant here, remove.
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + } |
| + |
| + if (call_status == DBUS_METHOD_CALL_FAILURE) |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
Should this have a VLOG as well?
pneubeck (no reviews)
2013/07/29 13:15:08
Good point. Handling this error case like typical
|
| + return; |
| + |
| + struct NssPem { |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
static
pneubeck (no reviews)
2013/07/29 13:15:08
No, doesn't add anything:
https://groups.google.co
|
| + const char* read_prefix; |
| + const char* nss_key; |
| + const char* pem_key; |
| + UMANetworkType uma_type; |
| + } const kNssPemMap[] = { |
| + {NULL, flimflam::kEapCaCertNssProperty, shill::kEapCaCertPemProperty, |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
I'm not entirely sure, but I think style guide / c
pneubeck (no reviews)
2013/07/29 13:15:08
optional, so I'd like to keep the automatic format
|
| + UMA_NETWORK_TYPE_EAP}, |
| + {flimflam::kProviderProperty, flimflam::kL2tpIpsecCaCertNssProperty, |
| + shill::kL2tpIpsecCaCertPemProperty, UMA_NETWORK_TYPE_IPSEC}, |
| + {flimflam::kProviderProperty, flimflam::kOpenVPNCaCertNSSProperty, |
| + shill::kOpenVPNCaCertPemProperty, UMA_NETWORK_TYPE_OPENVPN}, |
| + }; |
| + |
| + std::string nickname; |
| + const base::ListValue* pem_property = NULL; |
| + const NssPem* found_nss_pem = NULL; |
| + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNssPemMap); ++i) { |
| + const base::DictionaryValue* dict = &properties; |
| + if (kNssPemMap[i].read_prefix) { |
| + properties.GetDictionaryWithoutPathExpansion(kNssPemMap[i].read_prefix, |
| + &dict); |
| + if (!dict) |
| + continue; |
| + } |
| + dict->GetStringWithoutPathExpansion(kNssPemMap[i].nss_key, &nickname); |
| + if (!nickname.empty()) { |
| + found_nss_pem = &kNssPemMap[i]; |
| + dict->GetListWithoutPathExpansion(kNssPemMap[i].pem_key, &pem_property); |
| + break; |
| + } |
| + } |
| + |
| + if (nickname.empty()) |
| + return; // Didn't find any nickname. |
| + |
| + VLOG(2) << "Found NSS nickname to migrate. Property: " |
| + << found_nss_pem->nss_key << ", network: " << service_path; |
| + UMA_HISTOGRAM_ENUMERATION("Network.MigrationNssToPem", |
| + found_nss_pem->uma_type, |
| + UMA_NETWORK_TYPE_SIZE); |
| + |
| + if (pem_property && !pem_property->empty()) { |
| + // The PEM property exists already. We only have to clear the NSS |
| + // property. |
| + VLOG(2) << "PEM already exists, clear NSS property."; |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
nit: s/clear/clearing/
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( |
| + dbus::ObjectPath(service_path), |
| + found_nss_pem->nss_key, |
| + base::StringValue(std::string()), |
| + base::Bind(&base::DoNothing), |
| + base::Bind(&network_handler::ShillErrorCallbackFunction, |
| + "MigrationHelper.ClearProperty failed", |
| + service_path, |
| + network_handler::ErrorCallback())); |
| + CHECK(cert_handler_); |
| + CHECK(cert_handler_->network_state_handler_); |
| + cert_handler_->network_state_handler_ |
| + ->RequestUpdateForNetwork(service_path); |
| + return; |
| + } |
| + |
| + scoped_refptr<net::X509Certificate> cert; |
| + for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end(); |
| + ++it) { |
| + if (nickname == GetNickname((*it)->os_cert_handle())) { |
| + cert = *it; |
| + break; |
| + } |
| + } |
| + |
| + if (!cert) { |
| + VLOG(2) << "No matching cert found."; |
| + return; // Didn't find a matching certificate. |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
nit: remove redundant comment
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + } |
| + |
| + std::string pem_encoded; |
| + if (!net::X509Certificate::GetPEMEncoded(cert->os_cert_handle(), |
| + &pem_encoded)) { |
| + LOG(ERROR) << "PEM encoding failed."; |
| + return; // PEM encoding failed. |
|
Mattias Nissler (ping if slow)
2013/07/29 10:08:15
nit: remove redundant comment
pneubeck (no reviews)
2013/07/29 13:15:08
Done.
|
| + } |
| + |
| + base::DictionaryValue new_properties; |
| + new_properties.SetStringWithoutPathExpansion(found_nss_pem->nss_key, |
| + std::string()); |
| + scoped_ptr<base::ListValue> ca_cert_pems(new base::ListValue); |
| + ca_cert_pems->AppendString(pem_encoded); |
| + new_properties.SetWithoutPathExpansion(found_nss_pem->pem_key, |
| + ca_cert_pems.release()); |
| + |
| + DBusThreadManager::Get()->GetShillServiceClient() |
| + ->SetProperties(dbus::ObjectPath(service_path), |
| + new_properties, |
| + base::Bind(&base::DoNothing), |
| + base::Bind(&network_handler::ShillErrorCallbackFunction, |
| + "MigrationHelper.SetProperties failed", |
| + service_path, |
| + network_handler::ErrorCallback())); |
| + cert_handler_->network_state_handler_ |
| + ->RequestUpdateForNetwork(service_path); |
| + } |
| + |
| + private: |
| + friend class base::RefCounted<MigrationHelper>; |
| + virtual ~MigrationHelper() { |
| + } |
| + |
| + net::CertificateList certs_; |
| + base::WeakPtr<NetworkCertHandler> cert_handler_; |
| +}; |
| + |
| +NetworkCertHandler::NetworkCertHandler() |
| + : network_state_handler_(NULL), weak_ptr_factory_(this) {} |
| + |
| +NetworkCertHandler::~NetworkCertHandler() { |
| + if (network_state_handler_) |
| + network_state_handler_->RemoveObserver(this, FROM_HERE); |
| + if (CertLoader::IsInitialized()) |
| + CertLoader::Get()->RemoveObserver(this); |
| +} |
| + |
| +void NetworkCertHandler::Init(NetworkStateHandler* network_state_handler) { |
| + DCHECK(network_state_handler); |
| + network_state_handler_ = network_state_handler; |
| + network_state_handler_->AddObserver(this, FROM_HERE); |
| + |
| + DCHECK(CertLoader::IsInitialized()); |
| + CertLoader::Get()->AddObserver(this); |
| +} |
| + |
| +void NetworkCertHandler::NetworkListChanged() { |
| + if (!CertLoader::Get()->certificates_loaded()) { |
| + VLOG(2) << "Certs not loaded yet."; |
| + return; |
| + } |
| + // Run the migration process from deprecated CaCertNssProperties to CaCertPem. |
| + VLOG(2) << "Start NSS nickname to PEM migration."; |
| + scoped_refptr<MigrationHelper> helper(new MigrationHelper( |
| + CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); |
| + NetworkStateHandler::NetworkStateList networks; |
| + network_state_handler_->GetNetworkList(&networks); |
| + helper->Run(networks); |
| +} |
| + |
| +void NetworkCertHandler::OnCertificatesLoaded( |
| + const net::CertificateList& cert_list, |
| + bool initial_load) { |
| + // Maybe there are networks referring to certs (by NSS nickname) that were not |
| + // loaded before but are now. |
| + NetworkListChanged(); |
| +} |
| + |
| +} // namespace chromeos |