Home | History | Annotate | Download | only in network
      1 // Copyright (c) 2012 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_util.h"
      6 
      7 #include <cert.h>
      8 #include <pk11pub.h>
      9 
     10 #include <list>
     11 #include <string>
     12 #include <vector>
     13 
     14 #include "base/values.h"
     15 #include "chromeos/network/certificate_pattern.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/cert/cert_database.h"
     18 #include "net/cert/nss_cert_database.h"
     19 #include "net/cert/x509_cert_types.h"
     20 #include "net/cert/x509_certificate.h"
     21 #include "third_party/cros_system_api/dbus/service_constants.h"
     22 
     23 namespace chromeos {
     24 
     25 namespace client_cert {
     26 
     27 namespace {
     28 
     29 // Functor to filter out non-matching issuers.
     30 class IssuerFilter {
     31  public:
     32   explicit IssuerFilter(const IssuerSubjectPattern& issuer)
     33     : issuer_(issuer) {}
     34   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
     35     return !CertPrincipalMatches(issuer_, cert.get()->issuer());
     36   }
     37  private:
     38   const IssuerSubjectPattern& issuer_;
     39 };
     40 
     41 // Functor to filter out non-matching subjects.
     42 class SubjectFilter {
     43  public:
     44   explicit SubjectFilter(const IssuerSubjectPattern& subject)
     45     : subject_(subject) {}
     46   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
     47     return !CertPrincipalMatches(subject_, cert.get()->subject());
     48   }
     49  private:
     50   const IssuerSubjectPattern& subject_;
     51 };
     52 
     53 // Functor to filter out certs that don't have private keys, or are invalid.
     54 class PrivateKeyFilter {
     55  public:
     56   explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {}
     57   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
     58     return cert_db_->CheckUserCert(cert.get()) != net::OK;
     59   }
     60  private:
     61   net::CertDatabase* cert_db_;
     62 };
     63 
     64 // Functor to filter out certs that don't have an issuer in the associated
     65 // IssuerCAPEMs list.
     66 class IssuerCaFilter {
     67  public:
     68   explicit IssuerCaFilter(const std::vector<std::string>& issuer_ca_pems)
     69     : issuer_ca_pems_(issuer_ca_pems) {}
     70   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
     71     // Find the certificate issuer for each certificate.
     72     // TODO(gspencer): this functionality should be available from
     73     // X509Certificate or NSSCertDatabase.
     74     CERTCertificate* issuer_cert = CERT_FindCertIssuer(
     75         cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA);
     76 
     77     if (!issuer_cert)
     78       return true;
     79 
     80     std::string pem_encoded;
     81     if (!net::X509Certificate::GetPEMEncoded(issuer_cert, &pem_encoded)) {
     82       LOG(ERROR) << "Couldn't PEM-encode certificate.";
     83       return true;
     84     }
     85 
     86     return (std::find(issuer_ca_pems_.begin(), issuer_ca_pems_.end(),
     87                       pem_encoded) ==
     88             issuer_ca_pems_.end());
     89   }
     90  private:
     91   const std::vector<std::string>& issuer_ca_pems_;
     92 };
     93 
     94 std::string GetStringFromDictionary(const base::DictionaryValue& dict,
     95                                     const std::string& key) {
     96   std::string s;
     97   dict.GetStringWithoutPathExpansion(key, &s);
     98   return s;
     99 }
    100 
    101 }  // namespace
    102 
    103 // Returns true only if any fields set in this pattern match exactly with
    104 // similar fields in the principal.  If organization_ or organizational_unit_
    105 // are set, then at least one of the organizations or units in the principal
    106 // must match.
    107 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
    108                           const net::CertPrincipal& principal) {
    109   if (!pattern.common_name().empty() &&
    110       pattern.common_name() != principal.common_name) {
    111     return false;
    112   }
    113 
    114   if (!pattern.locality().empty() &&
    115       pattern.locality() != principal.locality_name) {
    116     return false;
    117   }
    118 
    119   if (!pattern.organization().empty()) {
    120     if (std::find(principal.organization_names.begin(),
    121                   principal.organization_names.end(),
    122                   pattern.organization()) ==
    123         principal.organization_names.end()) {
    124       return false;
    125     }
    126   }
    127 
    128   if (!pattern.organizational_unit().empty()) {
    129     if (std::find(principal.organization_unit_names.begin(),
    130                   principal.organization_unit_names.end(),
    131                   pattern.organizational_unit()) ==
    132         principal.organization_unit_names.end()) {
    133       return false;
    134     }
    135   }
    136 
    137   return true;
    138 }
    139 
    140 scoped_refptr<net::X509Certificate> GetCertificateMatch(
    141     const CertificatePattern& pattern) {
    142   typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList;
    143 
    144   // Start with all the certs, and narrow it down from there.
    145   net::CertificateList all_certs;
    146   CertificateStlList matching_certs;
    147   net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs);
    148 
    149   if (all_certs.empty())
    150     return NULL;
    151 
    152   for (net::CertificateList::iterator iter = all_certs.begin();
    153        iter != all_certs.end(); ++iter) {
    154     matching_certs.push_back(*iter);
    155   }
    156 
    157   // Strip off any certs that don't have the right issuer and/or subject.
    158   if (!pattern.issuer().Empty()) {
    159     matching_certs.remove_if(IssuerFilter(pattern.issuer()));
    160     if (matching_certs.empty())
    161       return NULL;
    162   }
    163 
    164   if (!pattern.subject().Empty()) {
    165     matching_certs.remove_if(SubjectFilter(pattern.subject()));
    166     if (matching_certs.empty())
    167       return NULL;
    168   }
    169 
    170   if (!pattern.issuer_ca_pems().empty()) {
    171     matching_certs.remove_if(IssuerCaFilter(pattern.issuer_ca_pems()));
    172     if (matching_certs.empty())
    173       return NULL;
    174   }
    175 
    176   // Eliminate any certs that don't have private keys associated with
    177   // them.  The CheckUserCert call in the filter is a little slow (because of
    178   // underlying PKCS11 calls), so we do this last to reduce the number of times
    179   // we have to call it.
    180   PrivateKeyFilter private_filter(net::CertDatabase::GetInstance());
    181   matching_certs.remove_if(private_filter);
    182 
    183   if (matching_certs.empty())
    184     return NULL;
    185 
    186   // We now have a list of certificates that match the pattern we're
    187   // looking for.  Now we find the one with the latest start date.
    188   scoped_refptr<net::X509Certificate> latest(NULL);
    189 
    190   // Iterate over the rest looking for the one that was issued latest.
    191   for (CertificateStlList::iterator iter = matching_certs.begin();
    192        iter != matching_certs.end(); ++iter) {
    193     if (!latest.get() || (*iter)->valid_start() > latest->valid_start())
    194       latest = *iter;
    195   }
    196 
    197   return latest;
    198 }
    199 
    200 void SetShillProperties(const client_cert::ConfigType cert_config_type,
    201                         const std::string& tpm_slot,
    202                         const std::string& tpm_pin,
    203                         const std::string* pkcs11_id,
    204                         base::DictionaryValue* properties) {
    205   const char* tpm_pin_property = NULL;
    206   switch (cert_config_type) {
    207     case CONFIG_TYPE_NONE: {
    208       return;
    209     }
    210     case CONFIG_TYPE_OPENVPN: {
    211       tpm_pin_property = shill::kOpenVPNPinProperty;
    212       if (pkcs11_id) {
    213         properties->SetStringWithoutPathExpansion(
    214             shill::kOpenVPNClientCertIdProperty, *pkcs11_id);
    215       }
    216       break;
    217     }
    218     case CONFIG_TYPE_IPSEC: {
    219       tpm_pin_property = shill::kL2tpIpsecPinProperty;
    220       if (!tpm_slot.empty()) {
    221         properties->SetStringWithoutPathExpansion(
    222             shill::kL2tpIpsecClientCertSlotProperty, tpm_slot);
    223       }
    224       if (pkcs11_id) {
    225         properties->SetStringWithoutPathExpansion(
    226             shill::kL2tpIpsecClientCertIdProperty, *pkcs11_id);
    227       }
    228       break;
    229     }
    230     case CONFIG_TYPE_EAP: {
    231       tpm_pin_property = shill::kEapPinProperty;
    232       if (pkcs11_id) {
    233         // Shill requires both CertID and KeyID for TLS connections, despite the
    234         // fact that by convention they are the same ID.
    235         properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
    236                                                   *pkcs11_id);
    237         properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
    238                                                   *pkcs11_id);
    239       }
    240       break;
    241     }
    242   }
    243   DCHECK(tpm_pin_property);
    244   if (!tpm_pin.empty())
    245     properties->SetStringWithoutPathExpansion(tpm_pin_property, tpm_pin);
    246 }
    247 
    248 bool IsCertificateConfigured(const client_cert::ConfigType cert_config_type,
    249                              const base::DictionaryValue& service_properties) {
    250   // VPN certificate properties are read from the Provider dictionary.
    251   const base::DictionaryValue* provider_properties = NULL;
    252   service_properties.GetDictionaryWithoutPathExpansion(
    253       shill::kProviderProperty, &provider_properties);
    254   switch (cert_config_type) {
    255     case CONFIG_TYPE_NONE:
    256       return true;
    257     case CONFIG_TYPE_OPENVPN:
    258       // OpenVPN generally requires a passphrase and we don't know whether or
    259       // not one is required, so always return false here.
    260       return false;
    261     case CONFIG_TYPE_IPSEC: {
    262       if (!provider_properties)
    263         return false;
    264 
    265       std::string client_cert_id;
    266       provider_properties->GetStringWithoutPathExpansion(
    267           shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
    268       return !client_cert_id.empty();
    269     }
    270     case CONFIG_TYPE_EAP: {
    271       std::string cert_id = GetStringFromDictionary(
    272           service_properties, shill::kEapCertIdProperty);
    273       std::string key_id = GetStringFromDictionary(
    274           service_properties, shill::kEapKeyIdProperty);
    275       std::string identity = GetStringFromDictionary(
    276           service_properties, shill::kEapIdentityProperty);
    277       return !cert_id.empty() && !key_id.empty() && !identity.empty();
    278     }
    279   }
    280   NOTREACHED();
    281   return false;
    282 }
    283 
    284 }  // namespace client_cert
    285 
    286 }  // namespace chromeos
    287