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