Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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_resolver.h" | 5 #include "chromeos/network/client_cert_resolver.h" |
| 6 | 6 |
| 7 #include <cert.h> | 7 #include <cert.h> |
| 8 #include <certt.h> // for (SECCertUsageEnum) certUsageAnyCA | 8 #include <certt.h> // for (SECCertUsageEnum) certUsageAnyCA |
| 9 #include <pk11pub.h> | 9 #include <pk11pub.h> |
| 10 | 10 |
| 11 #include <algorithm> | 11 #include <algorithm> |
| 12 | 12 |
| 13 #include "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/location.h" | 14 #include "base/location.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 17 #include "base/strings/string_util.h" | |
| 17 #include "base/task_runner.h" | 18 #include "base/task_runner.h" |
| 18 #include "base/threading/worker_pool.h" | 19 #include "base/threading/worker_pool.h" |
| 19 #include "base/time/clock.h" | 20 #include "base/time/clock.h" |
| 20 #include "chromeos/dbus/dbus_thread_manager.h" | 21 #include "chromeos/dbus/dbus_thread_manager.h" |
| 21 #include "chromeos/dbus/shill_service_client.h" | 22 #include "chromeos/dbus/shill_service_client.h" |
| 22 #include "chromeos/network/managed_network_configuration_handler.h" | 23 #include "chromeos/network/managed_network_configuration_handler.h" |
| 23 #include "chromeos/network/network_state.h" | 24 #include "chromeos/network/network_state.h" |
| 24 #include "components/onc/onc_constants.h" | 25 #include "components/onc/onc_constants.h" |
| 25 #include "dbus/object_path.h" | 26 #include "dbus/object_path.h" |
| 26 #include "net/cert/scoped_nss_types.h" | 27 #include "net/cert/scoped_nss_types.h" |
| 27 #include "net/cert/x509_certificate.h" | 28 #include "net/cert/x509_certificate.h" |
| 29 #include "net/cert/x509_util_nss.h" | |
| 30 #include "third_party/cros_system_api/dbus/service_constants.h" | |
| 28 | 31 |
| 29 namespace chromeos { | 32 namespace chromeos { |
| 30 | 33 |
| 31 // Describes a network |network_path| for which a matching certificate |cert_id| | 34 // Describes a network |network_path| for which a matching certificate |cert_id| |
| 32 // was found or for which no certificate was found (|cert_id| will be empty). | 35 // was found or for which no certificate was found (|cert_id| will be empty). |
| 33 struct ClientCertResolver::NetworkAndMatchingCert { | 36 struct ClientCertResolver::NetworkAndMatchingCert { |
| 34 NetworkAndMatchingCert(const std::string& network_path, | 37 NetworkAndMatchingCert(const std::string& network_path, |
| 35 client_cert::ConfigType config_type, | 38 client_cert::ConfigType config_type, |
| 36 const std::string& cert_id, | 39 const std::string& cert_id, |
| 37 int slot_id) | 40 int slot_id, |
| 41 const std::string& configured_identity) | |
| 38 : service_path(network_path), | 42 : service_path(network_path), |
| 39 cert_config_type(config_type), | 43 cert_config_type(config_type), |
| 40 pkcs11_id(cert_id), | 44 pkcs11_id(cert_id), |
| 41 key_slot_id(slot_id) {} | 45 key_slot_id(slot_id), |
| 46 identity(configured_identity) {} | |
| 42 | 47 |
| 43 std::string service_path; | 48 std::string service_path; |
| 44 client_cert::ConfigType cert_config_type; | 49 client_cert::ConfigType cert_config_type; |
| 45 | 50 |
| 46 // The id of the matching certificate or empty if no certificate was found. | 51 // The id of the matching certificate or empty if no certificate was found. |
| 47 std::string pkcs11_id; | 52 std::string pkcs11_id; |
| 48 | 53 |
| 49 // The id of the slot containing the certificate and the private key. | 54 // The id of the slot containing the certificate and the private key. |
| 50 int key_slot_id; | 55 int key_slot_id; |
| 56 | |
| 57 // The EAP-TLS Identity field, if it is configured through a policy | |
| 58 // substitution. | |
|
Ryan Sleevi
2016/02/29 23:51:51
What does this mean?
Kevin Cernekee
2016/03/02 21:38:03
Done.
| |
| 59 std::string identity; | |
| 51 }; | 60 }; |
| 52 | 61 |
| 53 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert> | 62 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert> |
| 54 NetworkCertMatches; | 63 NetworkCertMatches; |
| 55 | 64 |
| 56 namespace { | 65 namespace { |
| 57 | 66 |
| 58 // Returns true if |vector| contains |value|. | 67 // Returns true if |vector| contains |value|. |
| 59 template <class T> | 68 template <class T> |
| 60 bool ContainsValue(const std::vector<T>& vector, const T& value) { | 69 bool ContainsValue(const std::vector<T>& vector, const T& value) { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 !CertLoader::IsCertificateHardwareBacked(&cert)) { | 180 !CertLoader::IsCertificateHardwareBacked(&cert)) { |
| 172 continue; | 181 continue; |
| 173 } | 182 } |
| 174 client_certs.push_back(CertAndIssuer(*it, GetPEMEncodedIssuer(cert))); | 183 client_certs.push_back(CertAndIssuer(*it, GetPEMEncodedIssuer(cert))); |
| 175 } | 184 } |
| 176 | 185 |
| 177 std::sort(client_certs.begin(), client_certs.end(), &CompareCertExpiration); | 186 std::sort(client_certs.begin(), client_certs.end(), &CompareCertExpiration); |
| 178 return client_certs; | 187 return client_certs; |
| 179 } | 188 } |
| 180 | 189 |
| 190 // Looks up subjectAlternativeName fields in |cert| that match |type|, | |
| 191 // then searches for |var_name| in |identity| and replaces any occurrences | |
| 192 // with the SAN. | |
|
Ryan Sleevi
2016/02/29 23:51:51
Comment: This comment is somewhat grammatically co
Kevin Cernekee
2016/03/02 21:38:03
Done.
| |
| 193 void ReplaceSAN(const std::string& var_name, | |
| 194 const net::X509Certificate& cert, | |
| 195 net::x509_util::SubjectAltNameType type, | |
| 196 std::string* identity) { | |
| 197 if (identity->find(var_name, 0) == std::string::npos) | |
| 198 return; | |
| 199 std::vector<std::string> names; | |
| 200 net::x509_util::GetSubjectAltNameByType(cert.os_cert_handle(), type, &names); | |
|
Ryan Sleevi
2016/02/29 23:51:51
API DESIGN: Why do you return all names, if you're
Kevin Cernekee
2016/03/02 21:38:03
Someday I might care about other names. The calle
Ryan Sleevi
2016/03/02 21:57:28
But today you don't.
| |
| 201 if (!names.empty()) | |
| 202 base::ReplaceSubstringsAfterOffset(identity, 0, var_name, names.at(0)); | |
|
Ryan Sleevi
2016/02/29 23:51:51
DESIGN: Why do you use this method of substitution
Kevin Cernekee
2016/03/02 21:38:03
It mirrors what was done for ${LOGIN_ID} and ${LOG
| |
| 203 } | |
| 204 | |
| 181 // Searches for matches between |networks| and |certs| and writes matches to | 205 // Searches for matches between |networks| and |certs| and writes matches to |
| 182 // |matches|. Because this calls NSS functions and is potentially slow, it must | 206 // |matches|. Because this calls NSS functions and is potentially slow, it must |
| 183 // be run on a worker thread. | 207 // be run on a worker thread. |
| 184 void FindCertificateMatches(const net::CertificateList& certs, | 208 void FindCertificateMatches(const net::CertificateList& certs, |
| 185 std::vector<NetworkAndCertPattern>* networks, | 209 std::vector<NetworkAndCertPattern>* networks, |
| 186 base::Time now, | 210 base::Time now, |
| 187 NetworkCertMatches* matches) { | 211 NetworkCertMatches* matches) { |
| 188 std::vector<CertAndIssuer> client_certs( | 212 std::vector<CertAndIssuer> client_certs( |
| 189 CreateSortedCertAndIssuerList(certs, now)); | 213 CreateSortedCertAndIssuerList(certs, now)); |
| 190 | 214 |
| 191 for (std::vector<NetworkAndCertPattern>::const_iterator it = | 215 for (std::vector<NetworkAndCertPattern>::const_iterator it = |
| 192 networks->begin(); | 216 networks->begin(); |
| 193 it != networks->end(); ++it) { | 217 it != networks->end(); ++it) { |
| 194 std::vector<CertAndIssuer>::iterator cert_it = | 218 std::vector<CertAndIssuer>::iterator cert_it = |
| 195 std::find_if(client_certs.begin(), | 219 std::find_if(client_certs.begin(), |
| 196 client_certs.end(), | 220 client_certs.end(), |
| 197 MatchCertWithPattern(it->cert_config.pattern)); | 221 MatchCertWithPattern(it->cert_config.pattern)); |
| 198 std::string pkcs11_id; | 222 std::string pkcs11_id; |
| 199 int slot_id = -1; | 223 int slot_id = -1; |
| 224 std::string identity; | |
| 225 | |
| 200 if (cert_it == client_certs.end()) { | 226 if (cert_it == client_certs.end()) { |
| 201 VLOG(1) << "Couldn't find a matching client cert for network " | 227 VLOG(1) << "Couldn't find a matching client cert for network " |
| 202 << it->service_path; | 228 << it->service_path; |
| 203 // Leave |pkcs11_id| empty to indicate that no cert was found for this | 229 // Leave |pkcs11_id| empty to indicate that no cert was found for this |
| 204 // network. | 230 // network. |
| 205 } else { | 231 } else { |
| 206 pkcs11_id = | 232 pkcs11_id = |
| 207 CertLoader::GetPkcs11IdAndSlotForCert(*cert_it->cert, &slot_id); | 233 CertLoader::GetPkcs11IdAndSlotForCert(*cert_it->cert, &slot_id); |
| 208 if (pkcs11_id.empty()) { | 234 if (pkcs11_id.empty()) { |
| 209 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; | 235 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; |
| 210 // So far this error is not expected to happen. We can just continue, in | 236 // So far this error is not expected to happen. We can just continue, in |
| 211 // the worst case the user can remove the problematic cert. | 237 // the worst case the user can remove the problematic cert. |
| 212 continue; | 238 continue; |
| 213 } | 239 } |
| 240 | |
| 241 // If the policy specifies an identity containing ${CERT_SAN_xxx}, | |
| 242 // see if the cert contains a suitable subjectAltName that we can | |
| 243 // stuff into the shill properties. | |
|
Ryan Sleevi
2016/02/29 23:51:51
Comment nit: Avoid "we" in comments; it's ambiguou
Kevin Cernekee
2016/03/02 21:38:03
Done.
| |
| 244 identity = it->cert_config.policy_identity; | |
| 245 ReplaceSAN(::onc::substitutes::kCertSANEmail, *cert_it->cert, | |
| 246 net::x509_util::SAN_RFC822_NAME, &identity); | |
|
Ryan Sleevi
2016/02/29 23:51:51
API DESIGN: When the SAN is absent and it just con
Kevin Cernekee
2016/03/02 21:38:03
Documented this in the bug.
| |
| 247 ReplaceSAN(::onc::substitutes::kCertSANUPN, *cert_it->cert, | |
| 248 net::x509_util::SAN_UPN, &identity); | |
|
Ryan Sleevi
2016/02/29 23:51:51
So when there's no match, you leave the |identity|
Kevin Cernekee
2016/03/02 21:38:03
I like it. If a user can't get on the network and
| |
| 214 } | 249 } |
| 250 | |
| 215 matches->push_back(ClientCertResolver::NetworkAndMatchingCert( | 251 matches->push_back(ClientCertResolver::NetworkAndMatchingCert( |
| 216 it->service_path, it->cert_config.location, pkcs11_id, slot_id)); | 252 it->service_path, it->cert_config.location, pkcs11_id, slot_id, |
| 253 identity)); | |
| 217 } | 254 } |
| 218 } | 255 } |
| 219 | 256 |
| 220 void LogError(const std::string& service_path, | 257 void LogError(const std::string& service_path, |
| 221 const std::string& dbus_error_name, | 258 const std::string& dbus_error_name, |
| 222 const std::string& dbus_error_message) { | 259 const std::string& dbus_error_message) { |
| 223 network_handler::ShillErrorCallbackFunction( | 260 network_handler::ShillErrorCallbackFunction( |
| 224 "ClientCertResolver.SetProperties failed", | 261 "ClientCertResolver.SetProperties failed", |
| 225 service_path, | 262 service_path, |
| 226 network_handler::ErrorCallback(), | 263 network_handler::ErrorCallback(), |
| (...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 VLOG(1) << "Configuring certificate of network " << it->service_path; | 532 VLOG(1) << "Configuring certificate of network " << it->service_path; |
| 496 base::DictionaryValue shill_properties; | 533 base::DictionaryValue shill_properties; |
| 497 if (it->pkcs11_id.empty()) { | 534 if (it->pkcs11_id.empty()) { |
| 498 client_cert::SetEmptyShillProperties(it->cert_config_type, | 535 client_cert::SetEmptyShillProperties(it->cert_config_type, |
| 499 &shill_properties); | 536 &shill_properties); |
| 500 } else { | 537 } else { |
| 501 client_cert::SetShillProperties(it->cert_config_type, | 538 client_cert::SetShillProperties(it->cert_config_type, |
| 502 it->key_slot_id, | 539 it->key_slot_id, |
| 503 it->pkcs11_id, | 540 it->pkcs11_id, |
| 504 &shill_properties); | 541 &shill_properties); |
| 542 if (!it->identity.empty()) { | |
| 543 shill_properties.SetStringWithoutPathExpansion( | |
| 544 shill::kEapIdentityProperty, it->identity); | |
| 545 } | |
| 505 } | 546 } |
| 506 network_properties_changed_ = true; | 547 network_properties_changed_ = true; |
| 507 DBusThreadManager::Get()->GetShillServiceClient()-> | 548 DBusThreadManager::Get()->GetShillServiceClient()-> |
| 508 SetProperties(dbus::ObjectPath(it->service_path), | 549 SetProperties(dbus::ObjectPath(it->service_path), |
| 509 shill_properties, | 550 shill_properties, |
| 510 base::Bind(&base::DoNothing), | 551 base::Bind(&base::DoNothing), |
| 511 base::Bind(&LogError, it->service_path)); | 552 base::Bind(&LogError, it->service_path)); |
| 512 network_state_handler_->RequestUpdateForNetwork(it->service_path); | 553 network_state_handler_->RequestUpdateForNetwork(it->service_path); |
| 513 } | 554 } |
| 514 if (queued_networks_to_resolve_.empty()) | 555 if (queued_networks_to_resolve_.empty()) |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 526 FOR_EACH_OBSERVER(Observer, observers_, ResolveRequestCompleted(changed)); | 567 FOR_EACH_OBSERVER(Observer, observers_, ResolveRequestCompleted(changed)); |
| 527 } | 568 } |
| 528 | 569 |
| 529 base::Time ClientCertResolver::Now() const { | 570 base::Time ClientCertResolver::Now() const { |
| 530 if (testing_clock_) | 571 if (testing_clock_) |
| 531 return testing_clock_->Now(); | 572 return testing_clock_->Now(); |
| 532 return base::Time::Now(); | 573 return base::Time::Now(); |
| 533 } | 574 } |
| 534 | 575 |
| 535 } // namespace chromeos | 576 } // namespace chromeos |
| OLD | NEW |