| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/network/client_cert_util.h" | 5 #include "chromeos/network/client_cert_util.h" |
| 6 | 6 |
| 7 #include <cert.h> | 7 #include <cert.h> |
| 8 #include <pk11pub.h> | 8 #include <pk11pub.h> |
| 9 | 9 |
| 10 #include <list> | 10 #include <list> |
| 11 #include <string> | 11 #include <string> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/stringprintf.h" |
| 14 #include "base/values.h" | 16 #include "base/values.h" |
| 15 #include "chromeos/network/certificate_pattern.h" | 17 #include "chromeos/network/certificate_pattern.h" |
| 16 #include "chromeos/network/network_event_log.h" | 18 #include "chromeos/network/network_event_log.h" |
| 17 #include "components/onc/onc_constants.h" | 19 #include "components/onc/onc_constants.h" |
| 18 #include "net/base/net_errors.h" | 20 #include "net/base/net_errors.h" |
| 19 #include "net/cert/cert_database.h" | 21 #include "net/cert/cert_database.h" |
| 20 #include "net/cert/nss_cert_database.h" | 22 #include "net/cert/nss_cert_database.h" |
| 21 #include "net/cert/scoped_nss_types.h" | 23 #include "net/cert/scoped_nss_types.h" |
| 22 #include "net/cert/x509_cert_types.h" | 24 #include "net/cert/x509_cert_types.h" |
| 23 #include "net/cert/x509_certificate.h" | 25 #include "net/cert/x509_certificate.h" |
| 24 #include "third_party/cros_system_api/dbus/service_constants.h" | 26 #include "third_party/cros_system_api/dbus/service_constants.h" |
| 25 | 27 |
| 26 namespace chromeos { | 28 namespace chromeos { |
| 27 | 29 |
| 28 namespace client_cert { | 30 namespace client_cert { |
| 29 | 31 |
| 30 namespace { | 32 namespace { |
| 31 | 33 |
| 32 // Functor to filter out non-matching issuers. | 34 const char kDefaultTPMPin[] = "111111"; |
| 33 class IssuerFilter { | |
| 34 public: | |
| 35 explicit IssuerFilter(const IssuerSubjectPattern& issuer) | |
| 36 : issuer_(issuer) {} | |
| 37 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 38 return !CertPrincipalMatches(issuer_, cert.get()->issuer()); | |
| 39 } | |
| 40 private: | |
| 41 const IssuerSubjectPattern& issuer_; | |
| 42 }; | |
| 43 | |
| 44 // Functor to filter out non-matching subjects. | |
| 45 class SubjectFilter { | |
| 46 public: | |
| 47 explicit SubjectFilter(const IssuerSubjectPattern& subject) | |
| 48 : subject_(subject) {} | |
| 49 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 50 return !CertPrincipalMatches(subject_, cert.get()->subject()); | |
| 51 } | |
| 52 private: | |
| 53 const IssuerSubjectPattern& subject_; | |
| 54 }; | |
| 55 | |
| 56 // Functor to filter out certs that don't have private keys, or are invalid. | |
| 57 class PrivateKeyFilter { | |
| 58 public: | |
| 59 explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {} | |
| 60 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 61 return cert_db_->CheckUserCert(cert.get()) != net::OK; | |
| 62 } | |
| 63 private: | |
| 64 net::CertDatabase* cert_db_; | |
| 65 }; | |
| 66 | |
| 67 // Functor to filter out certs that don't have an issuer in the associated | |
| 68 // IssuerCAPEMs list. | |
| 69 class IssuerCaFilter { | |
| 70 public: | |
| 71 explicit IssuerCaFilter(const std::vector<std::string>& issuer_ca_pems) | |
| 72 : issuer_ca_pems_(issuer_ca_pems) {} | |
| 73 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 74 // Find the certificate issuer for each certificate. | |
| 75 // TODO(gspencer): this functionality should be available from | |
| 76 // X509Certificate or NSSCertDatabase. | |
| 77 net::ScopedCERTCertificate issuer_cert(CERT_FindCertIssuer( | |
| 78 cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA)); | |
| 79 | |
| 80 if (!issuer_cert) | |
| 81 return true; | |
| 82 | |
| 83 std::string pem_encoded; | |
| 84 if (!net::X509Certificate::GetPEMEncoded(issuer_cert.get(), | |
| 85 &pem_encoded)) { | |
| 86 LOG(ERROR) << "Couldn't PEM-encode certificate."; | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 return (std::find(issuer_ca_pems_.begin(), issuer_ca_pems_.end(), | |
| 91 pem_encoded) == | |
| 92 issuer_ca_pems_.end()); | |
| 93 } | |
| 94 private: | |
| 95 const std::vector<std::string>& issuer_ca_pems_; | |
| 96 }; | |
| 97 | 35 |
| 98 std::string GetStringFromDictionary(const base::DictionaryValue& dict, | 36 std::string GetStringFromDictionary(const base::DictionaryValue& dict, |
| 99 const std::string& key) { | 37 const std::string& key) { |
| 100 std::string s; | 38 std::string s; |
| 101 dict.GetStringWithoutPathExpansion(key, &s); | 39 dict.GetStringWithoutPathExpansion(key, &s); |
| 102 return s; | 40 return s; |
| 103 } | 41 } |
| 104 | 42 |
| 105 void GetClientCertTypeAndPattern( | 43 void GetClientCertTypeAndPattern( |
| 106 const base::DictionaryValue& dict_with_client_cert, | 44 const base::DictionaryValue& dict_with_client_cert, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 152 principal.organization_unit_names.end(), | 90 principal.organization_unit_names.end(), |
| 153 pattern.organizational_unit()) == | 91 pattern.organizational_unit()) == |
| 154 principal.organization_unit_names.end()) { | 92 principal.organization_unit_names.end()) { |
| 155 return false; | 93 return false; |
| 156 } | 94 } |
| 157 } | 95 } |
| 158 | 96 |
| 159 return true; | 97 return true; |
| 160 } | 98 } |
| 161 | 99 |
| 162 scoped_refptr<net::X509Certificate> GetCertificateMatch( | |
| 163 const CertificatePattern& pattern, | |
| 164 const net::CertificateList& all_certs) { | |
| 165 typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList; | |
| 166 | |
| 167 // Start with all the certs, and narrow it down from there. | |
| 168 CertificateStlList matching_certs; | |
| 169 | |
| 170 if (all_certs.empty()) | |
| 171 return NULL; | |
| 172 | |
| 173 for (net::CertificateList::const_iterator iter = all_certs.begin(); | |
| 174 iter != all_certs.end(); ++iter) { | |
| 175 matching_certs.push_back(*iter); | |
| 176 } | |
| 177 | |
| 178 // Strip off any certs that don't have the right issuer and/or subject. | |
| 179 if (!pattern.issuer().Empty()) { | |
| 180 matching_certs.remove_if(IssuerFilter(pattern.issuer())); | |
| 181 if (matching_certs.empty()) | |
| 182 return NULL; | |
| 183 } | |
| 184 | |
| 185 if (!pattern.subject().Empty()) { | |
| 186 matching_certs.remove_if(SubjectFilter(pattern.subject())); | |
| 187 if (matching_certs.empty()) | |
| 188 return NULL; | |
| 189 } | |
| 190 | |
| 191 if (!pattern.issuer_ca_pems().empty()) { | |
| 192 matching_certs.remove_if(IssuerCaFilter(pattern.issuer_ca_pems())); | |
| 193 if (matching_certs.empty()) | |
| 194 return NULL; | |
| 195 } | |
| 196 | |
| 197 // Eliminate any certs that don't have private keys associated with | |
| 198 // them. The CheckUserCert call in the filter is a little slow (because of | |
| 199 // underlying PKCS11 calls), so we do this last to reduce the number of times | |
| 200 // we have to call it. | |
| 201 PrivateKeyFilter private_filter(net::CertDatabase::GetInstance()); | |
| 202 matching_certs.remove_if(private_filter); | |
| 203 | |
| 204 if (matching_certs.empty()) | |
| 205 return NULL; | |
| 206 | |
| 207 // We now have a list of certificates that match the pattern we're | |
| 208 // looking for. Now we find the one with the latest start date. | |
| 209 scoped_refptr<net::X509Certificate> latest(NULL); | |
| 210 | |
| 211 // Iterate over the rest looking for the one that was issued latest. | |
| 212 for (CertificateStlList::iterator iter = matching_certs.begin(); | |
| 213 iter != matching_certs.end(); ++iter) { | |
| 214 if (!latest.get() || (*iter)->valid_start() > latest->valid_start()) | |
| 215 latest = *iter; | |
| 216 } | |
| 217 | |
| 218 return latest; | |
| 219 } | |
| 220 | |
| 221 std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { | 100 std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { |
| 222 if (cert_id.empty()) | 101 if (cert_id.empty()) |
| 223 return std::string(); | 102 return std::string(); |
| 224 | 103 |
| 225 size_t delimiter_pos = cert_id.find(':'); | 104 size_t delimiter_pos = cert_id.find(':'); |
| 226 if (delimiter_pos == std::string::npos) { | 105 if (delimiter_pos == std::string::npos) { |
| 227 // No delimiter found, so |cert_id| only contains the PKCS11 id. | 106 // No delimiter found, so |cert_id| only contains the PKCS11 id. |
| 228 return cert_id; | 107 return cert_id; |
| 229 } | 108 } |
| 230 if (delimiter_pos + 1 >= cert_id.size()) { | 109 if (delimiter_pos + 1 >= cert_id.size()) { |
| 231 LOG(ERROR) << "Empty PKCS11 id in cert id."; | 110 LOG(ERROR) << "Empty PKCS11 id in cert id."; |
| 232 return std::string(); | 111 return std::string(); |
| 233 } | 112 } |
| 234 return cert_id.substr(delimiter_pos + 1); | 113 return cert_id.substr(delimiter_pos + 1); |
| 235 } | 114 } |
| 236 | 115 |
| 237 void SetShillProperties(const ConfigType cert_config_type, | 116 void SetShillProperties(const ConfigType cert_config_type, |
| 238 const std::string& tpm_slot, | 117 const int tpm_slot, |
| 239 const std::string& tpm_pin, | 118 const std::string& pkcs11_id, |
| 240 const std::string* pkcs11_id, | |
| 241 base::DictionaryValue* properties) { | 119 base::DictionaryValue* properties) { |
| 242 const char* tpm_pin_property = NULL; | |
| 243 switch (cert_config_type) { | 120 switch (cert_config_type) { |
| 244 case CONFIG_TYPE_NONE: { | 121 case CONFIG_TYPE_NONE: { |
| 245 return; | 122 return; |
| 246 } | 123 } |
| 247 case CONFIG_TYPE_OPENVPN: { | 124 case CONFIG_TYPE_OPENVPN: { |
| 248 tpm_pin_property = shill::kOpenVPNPinProperty; | 125 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty, |
| 249 if (pkcs11_id) { | 126 kDefaultTPMPin); |
| 250 properties->SetStringWithoutPathExpansion( | 127 properties->SetStringWithoutPathExpansion( |
| 251 shill::kOpenVPNClientCertIdProperty, *pkcs11_id); | 128 shill::kOpenVPNClientCertIdProperty, pkcs11_id); |
| 252 } | |
| 253 break; | 129 break; |
| 254 } | 130 } |
| 255 case CONFIG_TYPE_IPSEC: { | 131 case CONFIG_TYPE_IPSEC: { |
| 256 tpm_pin_property = shill::kL2tpIpsecPinProperty; | 132 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty, |
| 257 if (!tpm_slot.empty()) { | 133 kDefaultTPMPin); |
| 258 properties->SetStringWithoutPathExpansion( | 134 properties->SetStringWithoutPathExpansion( |
| 259 shill::kL2tpIpsecClientCertSlotProperty, tpm_slot); | 135 shill::kL2tpIpsecClientCertSlotProperty, base::IntToString(tpm_slot)); |
| 260 } | 136 properties->SetStringWithoutPathExpansion( |
| 261 if (pkcs11_id) { | 137 shill::kL2tpIpsecClientCertIdProperty, pkcs11_id); |
| 262 properties->SetStringWithoutPathExpansion( | |
| 263 shill::kL2tpIpsecClientCertIdProperty, *pkcs11_id); | |
| 264 } | |
| 265 break; | 138 break; |
| 266 } | 139 } |
| 267 case CONFIG_TYPE_EAP: { | 140 case CONFIG_TYPE_EAP: { |
| 268 tpm_pin_property = shill::kEapPinProperty; | 141 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty, |
| 269 if (pkcs11_id) { | 142 kDefaultTPMPin); |
| 270 std::string key_id; | 143 std::string key_id = |
| 271 if (pkcs11_id->empty()) { | 144 base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str()); |
| 272 // An empty pkcs11_id means that we should clear the properties. | 145 |
| 273 } else { | 146 // Shill requires both CertID and KeyID for TLS connections, despite the |
| 274 if (tpm_slot.empty()) | 147 // fact that by convention they are the same ID, because one identifies |
| 275 NET_LOG_ERROR("Missing TPM slot id", ""); | 148 // the certificate and the other the private key. |
| 276 else | 149 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty, |
| 277 key_id = tpm_slot + ":"; | 150 key_id); |
| 278 key_id.append(*pkcs11_id); | 151 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty, |
| 279 } | 152 key_id); |
| 280 // Shill requires both CertID and KeyID for TLS connections, despite the | |
| 281 // fact that by convention they are the same ID, because one identifies | |
| 282 // the certificate and the other the private key. | |
| 283 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty, | |
| 284 key_id); | |
| 285 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty, | |
| 286 key_id); | |
| 287 } | |
| 288 break; | 153 break; |
| 289 } | 154 } |
| 290 } | 155 } |
| 291 DCHECK(tpm_pin_property); | 156 } |
| 292 if (!tpm_pin.empty()) | 157 |
| 293 properties->SetStringWithoutPathExpansion(tpm_pin_property, tpm_pin); | 158 void SetEmptyShillProperties(const ConfigType cert_config_type, |
| 159 base::DictionaryValue* properties) { |
| 160 switch (cert_config_type) { |
| 161 case CONFIG_TYPE_NONE: { |
| 162 return; |
| 163 } |
| 164 case CONFIG_TYPE_OPENVPN: { |
| 165 properties->SetStringWithoutPathExpansion(shill::kOpenVPNPinProperty, |
| 166 std::string()); |
| 167 properties->SetStringWithoutPathExpansion( |
| 168 shill::kOpenVPNClientCertIdProperty, std::string()); |
| 169 break; |
| 170 } |
| 171 case CONFIG_TYPE_IPSEC: { |
| 172 properties->SetStringWithoutPathExpansion(shill::kL2tpIpsecPinProperty, |
| 173 std::string()); |
| 174 properties->SetStringWithoutPathExpansion( |
| 175 shill::kL2tpIpsecClientCertSlotProperty, std::string()); |
| 176 properties->SetStringWithoutPathExpansion( |
| 177 shill::kL2tpIpsecClientCertIdProperty, std::string()); |
| 178 break; |
| 179 } |
| 180 case CONFIG_TYPE_EAP: { |
| 181 properties->SetStringWithoutPathExpansion(shill::kEapPinProperty, |
| 182 std::string()); |
| 183 // Shill requires both CertID and KeyID for TLS connections, despite the |
| 184 // fact that by convention they are the same ID, because one identifies |
| 185 // the certificate and the other the private key. |
| 186 properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty, |
| 187 std::string()); |
| 188 properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty, |
| 189 std::string()); |
| 190 break; |
| 191 } |
| 192 } |
| 294 } | 193 } |
| 295 | 194 |
| 296 ClientCertConfig::ClientCertConfig() | 195 ClientCertConfig::ClientCertConfig() |
| 297 : location(CONFIG_TYPE_NONE), | 196 : location(CONFIG_TYPE_NONE), |
| 298 client_cert_type(onc::client_cert::kClientCertTypeNone) { | 197 client_cert_type(onc::client_cert::kClientCertTypeNone) { |
| 299 } | 198 } |
| 300 | 199 |
| 301 void OncToClientCertConfig(const base::DictionaryValue& network_config, | 200 void OncToClientCertConfig(const base::DictionaryValue& network_config, |
| 302 ClientCertConfig* cert_config) { | 201 ClientCertConfig* cert_config) { |
| 303 using namespace ::onc; | 202 using namespace ::onc; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 return !cert_id.empty() && !key_id.empty() && !identity.empty(); | 284 return !cert_id.empty() && !key_id.empty() && !identity.empty(); |
| 386 } | 285 } |
| 387 } | 286 } |
| 388 NOTREACHED(); | 287 NOTREACHED(); |
| 389 return false; | 288 return false; |
| 390 } | 289 } |
| 391 | 290 |
| 392 } // namespace client_cert | 291 } // namespace client_cert |
| 393 | 292 |
| 394 } // namespace chromeos | 293 } // namespace chromeos |
| OLD | NEW |