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