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