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/strings/string_number_conversions.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/values.h"
     17 #include "chromeos/network/certificate_pattern.h"
     18 #include "chromeos/network/network_event_log.h"
     19 #include "components/onc/onc_constants.h"
     20 #include "net/base/net_errors.h"
     21 #include "net/cert/cert_database.h"
     22 #include "net/cert/nss_cert_database.h"
     23 #include "net/cert/scoped_nss_types.h"
     24 #include "net/cert/x509_cert_types.h"
     25 #include "net/cert/x509_certificate.h"
     26 #include "third_party/cros_system_api/dbus/service_constants.h"
     27 
     28 namespace chromeos {
     29 
     30 namespace client_cert {
     31 
     32 namespace {
     33 
     34 const char kDefaultTPMPin[] = "111111";
     35 
     36 std::string GetStringFromDictionary(const base::DictionaryValue& dict,
     37                                     const std::string& key) {
     38   std::string s;
     39   dict.GetStringWithoutPathExpansion(key, &s);
     40   return s;
     41 }
     42 
     43 void GetClientCertTypeAndPattern(
     44     const base::DictionaryValue& dict_with_client_cert,
     45     ClientCertConfig* cert_config) {
     46   using namespace ::onc::client_cert;
     47   dict_with_client_cert.GetStringWithoutPathExpansion(
     48       kClientCertType, &cert_config->client_cert_type);
     49 
     50   if (cert_config->client_cert_type == kPattern) {
     51     const base::DictionaryValue* pattern = NULL;
     52     dict_with_client_cert.GetDictionaryWithoutPathExpansion(kClientCertPattern,
     53                                                             &pattern);
     54     if (pattern) {
     55       bool success = cert_config->pattern.ReadFromONCDictionary(*pattern);
     56       DCHECK(success);
     57     }
     58   }
     59 }
     60 
     61 }  // namespace
     62 
     63 // Returns true only if any fields set in this pattern match exactly with
     64 // similar fields in the principal.  If organization_ or organizational_unit_
     65 // are set, then at least one of the organizations or units in the principal
     66 // must match.
     67 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
     68                           const net::CertPrincipal& principal) {
     69   if (!pattern.common_name().empty() &&
     70       pattern.common_name() != principal.common_name) {
     71     return false;
     72   }
     73 
     74   if (!pattern.locality().empty() &&
     75       pattern.locality() != principal.locality_name) {
     76     return false;
     77   }
     78 
     79   if (!pattern.organization().empty()) {
     80     if (std::find(principal.organization_names.begin(),
     81                   principal.organization_names.end(),
     82                   pattern.organization()) ==
     83         principal.organization_names.end()) {
     84       return false;
     85     }
     86   }
     87 
     88   if (!pattern.organizational_unit().empty()) {
     89     if (std::find(principal.organization_unit_names.begin(),
     90                   principal.organization_unit_names.end(),
     91                   pattern.organizational_unit()) ==
     92         principal.organization_unit_names.end()) {
     93       return false;
     94     }
     95   }
     96 
     97   return true;
     98 }
     99 
    100 std::string GetPkcs11AndSlotIdFromEapCertId(const std::string& cert_id,
    101                                             int* slot_id) {
    102   *slot_id = -1;
    103   if (cert_id.empty())
    104     return std::string();
    105 
    106   size_t delimiter_pos = cert_id.find(':');
    107   if (delimiter_pos == std::string::npos) {
    108     // No delimiter found, so |cert_id| only contains the PKCS11 id.
    109     return cert_id;
    110   }
    111   if (delimiter_pos + 1 >= cert_id.size()) {
    112     LOG(ERROR) << "Empty PKCS11 id in cert id.";
    113     return std::string();
    114   }
    115   int parsed_slot_id;
    116   if (base::StringToInt(cert_id.substr(0, delimiter_pos), &parsed_slot_id))
    117     *slot_id = parsed_slot_id;
    118   else
    119     LOG(ERROR) << "Slot ID is not an integer. Cert ID is: " << cert_id << ".";
    120   return cert_id.substr(delimiter_pos + 1);
    121 }
    122 
    123 void GetClientCertFromShillProperties(
    124     const base::DictionaryValue& shill_properties,
    125     ConfigType* cert_config_type,
    126     int* tpm_slot,
    127     std::string* pkcs11_id) {
    128   *cert_config_type = CONFIG_TYPE_NONE;
    129   *tpm_slot = -1;
    130   pkcs11_id->clear();
    131 
    132   // Look for VPN specific client certificate properties.
    133   //
    134   // VPN Provider values are read from the "Provider" dictionary, not the
    135   // "Provider.Type", etc keys (which are used only to set the values).
    136   const base::DictionaryValue* provider_properties = NULL;
    137   if (shill_properties.GetDictionaryWithoutPathExpansion(
    138           shill::kProviderProperty, &provider_properties)) {
    139     // Look for OpenVPN specific properties.
    140     if (provider_properties->GetStringWithoutPathExpansion(
    141             shill::kOpenVPNClientCertIdProperty, pkcs11_id)) {
    142       *cert_config_type = CONFIG_TYPE_OPENVPN;
    143       return;
    144     }
    145     // Look for L2TP-IPsec specific properties.
    146     if (provider_properties->GetStringWithoutPathExpansion(
    147                    shill::kL2tpIpsecClientCertIdProperty, pkcs11_id)) {
    148       std::string cert_slot;
    149       provider_properties->GetStringWithoutPathExpansion(
    150           shill::kL2tpIpsecClientCertSlotProperty, &cert_slot);
    151       if (!cert_slot.empty() && !base::StringToInt(cert_slot, tpm_slot)) {
    152         LOG(ERROR) << "Cert slot is not an integer: " << cert_slot << ".";
    153         return;
    154       }
    155 
    156       *cert_config_type = CONFIG_TYPE_IPSEC;
    157     }
    158     return;
    159   }
    160 
    161   // Look for EAP specific client certificate properties, which can either be
    162   // part of a WiFi or EthernetEAP configuration.
    163   std::string cert_id;
    164   if (shill_properties.GetStringWithoutPathExpansion(shill::kEapCertIdProperty,
    165                                                      &cert_id)) {
    166     // Shill requires both CertID and KeyID for TLS connections, despite the
    167     // fact that by convention they are the same ID, because one identifies
    168     // the certificate and the other the private key.
    169     std::string key_id;
    170     shill_properties.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
    171                                                    &key_id);
    172     // Assume the configuration to be invalid, if the two IDs are not identical.
    173     if (cert_id != key_id) {
    174       LOG(ERROR) << "EAP CertID differs from KeyID";
    175       return;
    176     }
    177     *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot);
    178     *cert_config_type = CONFIG_TYPE_EAP;
    179   }
    180 }
    181 
    182 void SetShillProperties(const ConfigType cert_config_type,
    183                         const int tpm_slot,
    184                         const std::string& pkcs11_id,
    185                         base::DictionaryValue* properties) {
    186   switch (cert_config_type) {
    187     case CONFIG_TYPE_NONE: {
    188       return;
    189     }
    190     case CONFIG_TYPE_OPENVPN: {
    191       properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
    192                                                 kDefaultTPMPin);
    193       properties->SetStringWithoutPathExpansion(
    194           shill::kOpenVPNClientCertIdProperty, pkcs11_id);
    195       break;
    196     }
    197     case CONFIG_TYPE_IPSEC: {
    198       properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
    199                                                 kDefaultTPMPin);
    200       properties->SetStringWithoutPathExpansion(
    201           shill::kL2tpIpsecClientCertSlotProperty, base::IntToString(tpm_slot));
    202       properties->SetStringWithoutPathExpansion(
    203           shill::kL2tpIpsecClientCertIdProperty, pkcs11_id);
    204       break;
    205     }
    206     case CONFIG_TYPE_EAP: {
    207       properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
    208                                                 kDefaultTPMPin);
    209       std::string key_id =
    210           base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str());
    211 
    212       // Shill requires both CertID and KeyID for TLS connections, despite the
    213       // fact that by convention they are the same ID, because one identifies
    214       // the certificate and the other the private key.
    215       properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
    216                                                 key_id);
    217       properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
    218                                                 key_id);
    219       break;
    220     }
    221   }
    222 }
    223 
    224 void SetEmptyShillProperties(const ConfigType cert_config_type,
    225                              base::DictionaryValue* properties) {
    226   switch (cert_config_type) {
    227     case CONFIG_TYPE_NONE: {
    228       return;
    229     }
    230     case CONFIG_TYPE_OPENVPN: {
    231       properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty,
    232                                                 std::string());
    233       properties->SetStringWithoutPathExpansion(
    234           shill::kOpenVPNClientCertIdProperty, std::string());
    235       break;
    236     }
    237     case CONFIG_TYPE_IPSEC: {
    238       properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty,
    239                                                 std::string());
    240       properties->SetStringWithoutPathExpansion(
    241           shill::kL2tpIpsecClientCertSlotProperty, std::string());
    242       properties->SetStringWithoutPathExpansion(
    243           shill::kL2tpIpsecClientCertIdProperty, std::string());
    244       break;
    245     }
    246     case CONFIG_TYPE_EAP: {
    247       properties->SetStringWithoutPathExpansion(shill::kEapPinProperty,
    248                                                 std::string());
    249       // Shill requires both CertID and KeyID for TLS connections, despite the
    250       // fact that by convention they are the same ID, because one identifies
    251       // the certificate and the other the private key.
    252       properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
    253                                                 std::string());
    254       properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
    255                                                 std::string());
    256       break;
    257     }
    258   }
    259 }
    260 
    261 ClientCertConfig::ClientCertConfig()
    262     : location(CONFIG_TYPE_NONE),
    263       client_cert_type(onc::client_cert::kClientCertTypeNone) {
    264 }
    265 
    266 void OncToClientCertConfig(const base::DictionaryValue& network_config,
    267                            ClientCertConfig* cert_config) {
    268   using namespace ::onc;
    269 
    270   *cert_config = ClientCertConfig();
    271 
    272   const base::DictionaryValue* dict_with_client_cert = NULL;
    273 
    274   const base::DictionaryValue* wifi = NULL;
    275   network_config.GetDictionaryWithoutPathExpansion(network_config::kWiFi,
    276                                                    &wifi);
    277   if (wifi) {
    278     const base::DictionaryValue* eap = NULL;
    279     wifi->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
    280     if (!eap)
    281       return;
    282 
    283     dict_with_client_cert = eap;
    284     cert_config->location = CONFIG_TYPE_EAP;
    285   }
    286 
    287   const base::DictionaryValue* vpn = NULL;
    288   network_config.GetDictionaryWithoutPathExpansion(network_config::kVPN, &vpn);
    289   if (vpn) {
    290     const base::DictionaryValue* openvpn = NULL;
    291     vpn->GetDictionaryWithoutPathExpansion(vpn::kOpenVPN, &openvpn);
    292     const base::DictionaryValue* ipsec = NULL;
    293     vpn->GetDictionaryWithoutPathExpansion(vpn::kIPsec, &ipsec);
    294     if (openvpn) {
    295       dict_with_client_cert = openvpn;
    296       cert_config->location = CONFIG_TYPE_OPENVPN;
    297     } else if (ipsec) {
    298       dict_with_client_cert = ipsec;
    299       cert_config->location = CONFIG_TYPE_IPSEC;
    300     } else {
    301       return;
    302     }
    303   }
    304 
    305   const base::DictionaryValue* ethernet = NULL;
    306   network_config.GetDictionaryWithoutPathExpansion(network_config::kEthernet,
    307                                                    &ethernet);
    308   if (ethernet) {
    309     const base::DictionaryValue* eap = NULL;
    310     ethernet->GetDictionaryWithoutPathExpansion(wifi::kEAP, &eap);
    311     if (!eap)
    312       return;
    313     dict_with_client_cert = eap;
    314     cert_config->location = CONFIG_TYPE_EAP;
    315   }
    316 
    317   if (dict_with_client_cert)
    318     GetClientCertTypeAndPattern(*dict_with_client_cert, cert_config);
    319 }
    320 
    321 bool IsCertificateConfigured(const ConfigType cert_config_type,
    322                              const base::DictionaryValue& service_properties) {
    323   // VPN certificate properties are read from the Provider dictionary.
    324   const base::DictionaryValue* provider_properties = NULL;
    325   service_properties.GetDictionaryWithoutPathExpansion(
    326       shill::kProviderProperty, &provider_properties);
    327   switch (cert_config_type) {
    328     case CONFIG_TYPE_NONE:
    329       return true;
    330     case CONFIG_TYPE_OPENVPN:
    331       // OpenVPN generally requires a passphrase and we don't know whether or
    332       // not one is required, so always return false here.
    333       return false;
    334     case CONFIG_TYPE_IPSEC: {
    335       if (!provider_properties)
    336         return false;
    337 
    338       std::string client_cert_id;
    339       provider_properties->GetStringWithoutPathExpansion(
    340           shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
    341       return !client_cert_id.empty();
    342     }
    343     case CONFIG_TYPE_EAP: {
    344       std::string cert_id = GetStringFromDictionary(
    345           service_properties, shill::kEapCertIdProperty);
    346       std::string key_id = GetStringFromDictionary(
    347           service_properties, shill::kEapKeyIdProperty);
    348       std::string identity = GetStringFromDictionary(
    349           service_properties, shill::kEapIdentityProperty);
    350       return !cert_id.empty() && !key_id.empty() && !identity.empty();
    351     }
    352   }
    353   NOTREACHED();
    354   return false;
    355 }
    356 
    357 }  // namespace client_cert
    358 
    359 }  // namespace chromeos
    360