Home | History | Annotate | Download | only in network
      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/client_cert_resolver.h"
      6 
      7 #include <cert.h>
      8 #include <certt.h>  // for (SECCertUsageEnum) certUsageAnyCA
      9 #include <pk11pub.h>
     10 
     11 #include <algorithm>
     12 #include <string>
     13 
     14 #include "base/stl_util.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/task_runner.h"
     17 #include "base/threading/worker_pool.h"
     18 #include "base/time/time.h"
     19 #include "chromeos/cert_loader.h"
     20 #include "chromeos/dbus/dbus_thread_manager.h"
     21 #include "chromeos/dbus/shill_service_client.h"
     22 #include "chromeos/network/certificate_pattern.h"
     23 #include "chromeos/network/client_cert_util.h"
     24 #include "chromeos/network/favorite_state.h"
     25 #include "chromeos/network/managed_network_configuration_handler.h"
     26 #include "chromeos/network/network_state_handler.h"
     27 #include "chromeos/network/network_ui_data.h"
     28 #include "components/onc/onc_constants.h"
     29 #include "dbus/object_path.h"
     30 #include "net/cert/x509_certificate.h"
     31 
     32 namespace chromeos {
     33 
     34 // Describes a network |network_path| for which a matching certificate |cert_id|
     35 // was found.
     36 struct ClientCertResolver::NetworkAndMatchingCert {
     37   NetworkAndMatchingCert(const std::string& network_path,
     38                          client_cert::ConfigType config_type,
     39                          const std::string& cert_id)
     40       : service_path(network_path),
     41         cert_config_type(config_type),
     42         pkcs11_id(cert_id) {}
     43 
     44   std::string service_path;
     45   client_cert::ConfigType cert_config_type;
     46   // The id of the matching certificate.
     47   std::string pkcs11_id;
     48 };
     49 
     50 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert>
     51     NetworkCertMatches;
     52 
     53 namespace {
     54 
     55 // Returns true if |vector| contains |value|.
     56 template <class T>
     57 bool ContainsValue(const std::vector<T>& vector, const T& value) {
     58   return find(vector.begin(), vector.end(), value) != vector.end();
     59 }
     60 
     61 // Returns true if a private key for certificate |cert| is installed.
     62 bool HasPrivateKey(const net::X509Certificate& cert) {
     63   PK11SlotInfo* slot = PK11_KeyForCertExists(cert.os_cert_handle(), NULL, NULL);
     64   if (!slot)
     65     return false;
     66 
     67   PK11_FreeSlot(slot);
     68   return true;
     69 }
     70 
     71 // Describes a certificate which is issued by |issuer| (encoded as PEM).
     72 struct CertAndIssuer {
     73   CertAndIssuer(const scoped_refptr<net::X509Certificate>& certificate,
     74                 const std::string& issuer)
     75       : cert(certificate),
     76         pem_encoded_issuer(issuer) {}
     77 
     78   scoped_refptr<net::X509Certificate> cert;
     79   std::string pem_encoded_issuer;
     80 };
     81 
     82 bool CompareCertExpiration(const CertAndIssuer& a,
     83                            const CertAndIssuer& b) {
     84   return (a.cert->valid_expiry() > b.cert->valid_expiry());
     85 }
     86 
     87 // Describes a network that is configured with the certificate pattern
     88 // |client_cert_pattern|.
     89 struct NetworkAndCertPattern {
     90   NetworkAndCertPattern(const std::string& network_path,
     91                         client_cert::ConfigType config_type,
     92                         const CertificatePattern& pattern)
     93       : service_path(network_path),
     94         cert_config_type(config_type),
     95         client_cert_pattern(pattern) {}
     96   std::string service_path;
     97   client_cert::ConfigType cert_config_type;
     98   CertificatePattern client_cert_pattern;
     99 };
    100 
    101 // A unary predicate that returns true if the given CertAndIssuer matches the
    102 // certificate pattern of the NetworkAndCertPattern.
    103 struct MatchCertWithPattern {
    104   explicit MatchCertWithPattern(const NetworkAndCertPattern& pattern)
    105       : net_and_pattern(pattern) {}
    106 
    107   bool operator()(const CertAndIssuer& cert_and_issuer) {
    108     const CertificatePattern& pattern = net_and_pattern.client_cert_pattern;
    109     if (!pattern.issuer().Empty() &&
    110         !client_cert::CertPrincipalMatches(pattern.issuer(),
    111                                            cert_and_issuer.cert->issuer())) {
    112       return false;
    113     }
    114     if (!pattern.subject().Empty() &&
    115         !client_cert::CertPrincipalMatches(pattern.subject(),
    116                                            cert_and_issuer.cert->subject())) {
    117       return false;
    118     }
    119 
    120     const std::vector<std::string>& issuer_ca_pems = pattern.issuer_ca_pems();
    121     if (!issuer_ca_pems.empty() &&
    122         !ContainsValue(issuer_ca_pems, cert_and_issuer.pem_encoded_issuer)) {
    123       return false;
    124     }
    125     return true;
    126   }
    127 
    128   NetworkAndCertPattern net_and_pattern;
    129 };
    130 
    131 // Searches for matches between |networks| and |certs| and writes matches to
    132 // |matches|. Because this calls NSS functions and is potentially slow, it must
    133 // be run on a worker thread.
    134 void FindCertificateMatches(const net::CertificateList& certs,
    135                             std::vector<NetworkAndCertPattern>* networks,
    136                             NetworkCertMatches* matches) {
    137   // Filter all client certs and determines each certificate's issuer, which is
    138   // required for the pattern matching.
    139   std::vector<CertAndIssuer> client_certs;
    140   for (net::CertificateList::const_iterator it = certs.begin();
    141        it != certs.end(); ++it) {
    142     const net::X509Certificate& cert = **it;
    143     if (cert.valid_expiry().is_null() || cert.HasExpired() ||
    144         !HasPrivateKey(cert)) {
    145       continue;
    146     }
    147     net::X509Certificate::OSCertHandle issuer_handle =
    148         CERT_FindCertIssuer(cert.os_cert_handle(), PR_Now(), certUsageAnyCA);
    149     if (!issuer_handle) {
    150       LOG(ERROR) << "Couldn't find an issuer.";
    151       continue;
    152     }
    153     scoped_refptr<net::X509Certificate> issuer =
    154         net::X509Certificate::CreateFromHandle(
    155             issuer_handle,
    156             net::X509Certificate::OSCertHandles() /* no intermediate certs */);
    157     if (!issuer) {
    158       LOG(ERROR) << "Couldn't create issuer cert.";
    159       continue;
    160     }
    161     std::string pem_encoded_issuer;
    162     if (!net::X509Certificate::GetPEMEncoded(issuer->os_cert_handle(),
    163                                              &pem_encoded_issuer)) {
    164       LOG(ERROR) << "Couldn't PEM-encode certificate.";
    165       continue;
    166     }
    167     client_certs.push_back(CertAndIssuer(*it, pem_encoded_issuer));
    168   }
    169 
    170   std::sort(client_certs.begin(), client_certs.end(), &CompareCertExpiration);
    171 
    172   for (std::vector<NetworkAndCertPattern>::const_iterator it =
    173            networks->begin();
    174        it != networks->end(); ++it) {
    175     std::vector<CertAndIssuer>::iterator cert_it = std::find_if(
    176         client_certs.begin(), client_certs.end(), MatchCertWithPattern(*it));
    177     if (cert_it == client_certs.end()) {
    178       LOG(WARNING) << "Couldn't find a matching client cert for network "
    179                    << it->service_path;
    180       continue;
    181     }
    182     std::string pkcs11_id = CertLoader::GetPkcs11IdForCert(*cert_it->cert);
    183     if (pkcs11_id.empty()) {
    184       LOG(ERROR) << "Couldn't determine PKCS#11 ID.";
    185       continue;
    186     }
    187     matches->push_back(ClientCertResolver::NetworkAndMatchingCert(
    188         it->service_path, it->cert_config_type, pkcs11_id));
    189   }
    190 }
    191 
    192 // Determines the type of the CertificatePattern configuration, i.e. is it a
    193 // pattern within an EAP, IPsec or OpenVPN configuration.
    194 client_cert::ConfigType OncToClientCertConfigurationType(
    195     const base::DictionaryValue& network_config) {
    196   using namespace ::onc;
    197 
    198   const base::DictionaryValue* wifi = NULL;
    199   network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
    200                                                    &wifi);
    201   if (wifi) {
    202     const base::DictionaryValue* eap = NULL;
    203     wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
    204     if (!eap)
    205       return client_cert::CONFIG_TYPE_NONE;
    206     return client_cert::CONFIG_TYPE_EAP;
    207   }
    208 
    209   const base::DictionaryValue* vpn = NULL;
    210   network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
    211   if (vpn) {
    212     const base::DictionaryValue* openvpn = NULL;
    213     vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
    214     if (openvpn)
    215       return client_cert::CONFIG_TYPE_OPENVPN;
    216 
    217     const base::DictionaryValue* ipsec = NULL;
    218     vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
    219     if (ipsec)
    220       return client_cert::CONFIG_TYPE_IPSEC;
    221 
    222     return client_cert::CONFIG_TYPE_NONE;
    223   }
    224 
    225   const base::DictionaryValue* ethernet = NULL;
    226   network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
    227                                                    &ethernet);
    228   if (ethernet) {
    229     const base::DictionaryValue* eap = NULL;
    230     ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
    231     if (eap)
    232       return client_cert::CONFIG_TYPE_EAP;
    233     return client_cert::CONFIG_TYPE_NONE;
    234   }
    235 
    236   return client_cert::CONFIG_TYPE_NONE;
    237 }
    238 
    239 void LogError(const std::string& service_path,
    240               const std::string& dbus_error_name,
    241               const std::string& dbus_error_message) {
    242   network_handler::ShillErrorCallbackFunction(
    243       "ClientCertResolver.SetProperties failed",
    244       service_path,
    245       network_handler::ErrorCallback(),
    246       dbus_error_name,
    247       dbus_error_message);
    248 }
    249 
    250 bool ClientCertificatesLoaded() {
    251   if (!CertLoader::Get()->certificates_loaded()) {
    252     VLOG(1) << "Certificates not loaded yet.";
    253     return false;
    254   }
    255   if (!CertLoader::Get()->IsHardwareBacked()) {
    256     VLOG(1) << "TPM is not available.";
    257     return false;
    258   }
    259   return true;
    260 }
    261 
    262 }  // namespace
    263 
    264 ClientCertResolver::ClientCertResolver()
    265     : network_state_handler_(NULL),
    266       managed_network_config_handler_(NULL),
    267       weak_ptr_factory_(this) {
    268 }
    269 
    270 ClientCertResolver::~ClientCertResolver() {
    271   if (network_state_handler_)
    272     network_state_handler_->RemoveObserver(this, FROM_HERE);
    273   if (CertLoader::IsInitialized())
    274     CertLoader::Get()->RemoveObserver(this);
    275   if (managed_network_config_handler_)
    276     managed_network_config_handler_->RemoveObserver(this);
    277 }
    278 
    279 void ClientCertResolver::Init(
    280     NetworkStateHandler* network_state_handler,
    281     ManagedNetworkConfigurationHandler* managed_network_config_handler) {
    282   DCHECK(network_state_handler);
    283   network_state_handler_ = network_state_handler;
    284   network_state_handler_->AddObserver(this, FROM_HERE);
    285 
    286   DCHECK(managed_network_config_handler);
    287   managed_network_config_handler_ = managed_network_config_handler;
    288   managed_network_config_handler_->AddObserver(this);
    289 
    290   CertLoader::Get()->AddObserver(this);
    291 }
    292 
    293 void ClientCertResolver::SetSlowTaskRunnerForTest(
    294     const scoped_refptr<base::TaskRunner>& task_runner) {
    295   slow_task_runner_for_test_ = task_runner;
    296 }
    297 
    298 void ClientCertResolver::NetworkListChanged() {
    299   VLOG(2) << "NetworkListChanged.";
    300   if (!ClientCertificatesLoaded())
    301     return;
    302   // Configure only networks that were not configured before.
    303 
    304   // We'll drop networks from |resolved_networks_|, which are not known anymore.
    305   std::set<std::string> old_resolved_networks;
    306   old_resolved_networks.swap(resolved_networks_);
    307 
    308   FavoriteStateList networks;
    309   network_state_handler_->GetFavoriteList(&networks);
    310 
    311   FavoriteStateList networks_to_check;
    312   for (FavoriteStateList::const_iterator it = networks.begin();
    313        it != networks.end(); ++it) {
    314     const std::string& service_path = (*it)->path();
    315     if (ContainsKey(old_resolved_networks, service_path)) {
    316       resolved_networks_.insert(service_path);
    317       continue;
    318     }
    319     networks_to_check.push_back(*it);
    320   }
    321 
    322   ResolveNetworks(networks_to_check);
    323 }
    324 
    325 void ClientCertResolver::OnCertificatesLoaded(
    326     const net::CertificateList& cert_list,
    327     bool initial_load) {
    328   VLOG(2) << "OnCertificatesLoaded.";
    329   if (!ClientCertificatesLoaded())
    330     return;
    331   // Compare all networks with all certificates.
    332   FavoriteStateList networks;
    333   network_state_handler_->GetFavoriteList(&networks);
    334   ResolveNetworks(networks);
    335 }
    336 
    337 void ClientCertResolver::PolicyApplied(const std::string& service_path) {
    338   VLOG(2) << "PolicyApplied " << service_path;
    339   if (!ClientCertificatesLoaded())
    340     return;
    341   // Compare this network with all certificates.
    342   const FavoriteState* network =
    343       network_state_handler_->GetFavoriteState(service_path);
    344   if (!network) {
    345     LOG(ERROR) << "service path '" << service_path << "' unknown.";
    346     return;
    347   }
    348   FavoriteStateList networks;
    349   networks.push_back(network);
    350   ResolveNetworks(networks);
    351 }
    352 
    353 void ClientCertResolver::ResolveNetworks(const FavoriteStateList& networks) {
    354   scoped_ptr<std::vector<NetworkAndCertPattern> > networks_with_pattern(
    355       new std::vector<NetworkAndCertPattern>);
    356 
    357   // Filter networks with ClientCertPattern. As ClientCertPatterns can only be
    358   // set by policy, we check there.
    359   for (FavoriteStateList::const_iterator it = networks.begin();
    360        it != networks.end(); ++it) {
    361     const FavoriteState* network = *it;
    362 
    363     // In any case, don't check this network again in NetworkListChanged.
    364     resolved_networks_.insert(network->path());
    365 
    366     // If this network is not managed, it cannot have a ClientCertPattern.
    367     if (network->guid().empty())
    368       continue;
    369 
    370     if (network->profile_path().empty()) {
    371       LOG(ERROR) << "Network " << network->path()
    372                  << " has a GUID but not profile path";
    373       continue;
    374     }
    375     const base::DictionaryValue* policy =
    376         managed_network_config_handler_->FindPolicyByGuidAndProfile(
    377             network->guid(), network->profile_path());
    378 
    379     if (!policy) {
    380       VLOG(1) << "The policy for network " << network->path() << " with GUID "
    381               << network->guid() << " is not available yet.";
    382       // Skip this network for now. Once the policy is loaded, PolicyApplied()
    383       // will retry.
    384       continue;
    385     }
    386 
    387     VLOG(2) << "Inspecting network " << network->path();
    388     // TODO(pneubeck): Access the ClientCertPattern without relying on
    389     //   NetworkUIData, which also parses other things that we don't need here.
    390     // The ONCSource is irrelevant here.
    391     scoped_ptr<NetworkUIData> ui_data(
    392         NetworkUIData::CreateFromONC(onc::ONC_SOURCE_NONE, *policy));
    393 
    394     // Skip networks that don't have a ClientCertPattern.
    395     if (ui_data->certificate_type() != CLIENT_CERT_TYPE_PATTERN)
    396       continue;
    397 
    398     client_cert::ConfigType config_type =
    399         OncToClientCertConfigurationType(*policy);
    400     if (config_type == client_cert::CONFIG_TYPE_NONE) {
    401       LOG(ERROR) << "UIData contains a CertificatePattern, but the policy "
    402                  << "doesn't. Network: " << network->path();
    403       continue;
    404     }
    405 
    406     networks_with_pattern->push_back(NetworkAndCertPattern(
    407         network->path(), config_type, ui_data->certificate_pattern()));
    408   }
    409   if (networks_with_pattern->empty())
    410     return;
    411 
    412   VLOG(2) << "Start task for resolving client cert patterns.";
    413   base::TaskRunner* task_runner = slow_task_runner_for_test_.get();
    414   if (!task_runner)
    415     task_runner = base::WorkerPool::GetTaskRunner(true /* task is slow */);
    416 
    417   NetworkCertMatches* matches = new NetworkCertMatches;
    418   task_runner->PostTaskAndReply(
    419       FROM_HERE,
    420       base::Bind(&FindCertificateMatches,
    421                  CertLoader::Get()->cert_list(),
    422                  base::Owned(networks_with_pattern.release()),
    423                  matches),
    424       base::Bind(&ClientCertResolver::ConfigureCertificates,
    425                  weak_ptr_factory_.GetWeakPtr(),
    426                  base::Owned(matches)));
    427 }
    428 
    429 void ClientCertResolver::ConfigureCertificates(NetworkCertMatches* matches) {
    430   for (NetworkCertMatches::const_iterator it = matches->begin();
    431        it != matches->end(); ++it) {
    432     VLOG(1) << "Configuring certificate of network " << it->service_path;
    433     CertLoader* cert_loader = CertLoader::Get();
    434     base::DictionaryValue shill_properties;
    435     client_cert::SetShillProperties(
    436         it->cert_config_type,
    437         base::IntToString(cert_loader->tpm_token_slot_id()),
    438         cert_loader->tpm_user_pin(),
    439         &it->pkcs11_id,
    440         &shill_properties);
    441     DBusThreadManager::Get()->GetShillServiceClient()->
    442         SetProperties(dbus::ObjectPath(it->service_path),
    443                         shill_properties,
    444                         base::Bind(&base::DoNothing),
    445                         base::Bind(&LogError, it->service_path));
    446     network_state_handler_->RequestUpdateForNetwork(it->service_path);
    447   }
    448 }
    449 
    450 }  // namespace chromeos
    451