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 #include <string> | 12 #include <string> |
| 13 | 13 |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/location.h" | 15 #include "base/location.h" |
| 16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | |
| 18 #include "base/task_runner.h" | 17 #include "base/task_runner.h" |
| 19 #include "base/threading/worker_pool.h" | 18 #include "base/threading/worker_pool.h" |
| 20 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 21 #include "chromeos/cert_loader.h" | 20 #include "chromeos/cert_loader.h" |
| 22 #include "chromeos/dbus/dbus_thread_manager.h" | 21 #include "chromeos/dbus/dbus_thread_manager.h" |
| 23 #include "chromeos/dbus/shill_service_client.h" | 22 #include "chromeos/dbus/shill_service_client.h" |
| 24 #include "chromeos/network/certificate_pattern.h" | |
| 25 #include "chromeos/network/client_cert_util.h" | |
| 26 #include "chromeos/network/managed_network_configuration_handler.h" | 23 #include "chromeos/network/managed_network_configuration_handler.h" |
| 27 #include "chromeos/network/network_state.h" | 24 #include "chromeos/network/network_state.h" |
| 28 #include "chromeos/tpm_token_loader.h" | |
| 29 #include "components/onc/onc_constants.h" | 25 #include "components/onc/onc_constants.h" |
| 30 #include "dbus/object_path.h" | 26 #include "dbus/object_path.h" |
| 31 #include "net/cert/scoped_nss_types.h" | 27 #include "net/cert/scoped_nss_types.h" |
| 32 #include "net/cert/x509_certificate.h" | 28 #include "net/cert/x509_certificate.h" |
| 33 | 29 |
| 34 namespace chromeos { | 30 namespace chromeos { |
| 35 | 31 |
| 36 // Describes a network |network_path| for which a matching certificate |cert_id| | 32 // Describes a network |network_path| for which a matching certificate |cert_id| |
| 37 // was found or for which no certificate was found (|cert_id| will be empty). | 33 // was found or for which no certificate was found (|cert_id| will be empty). |
| 38 struct ClientCertResolver::NetworkAndMatchingCert { | 34 struct ClientCertResolver::NetworkAndMatchingCert { |
| 39 NetworkAndMatchingCert(const std::string& network_path, | 35 NetworkAndMatchingCert(const std::string& network_path, |
| 40 client_cert::ConfigType config_type, | 36 client_cert::ConfigType config_type, |
| 41 const std::string& cert_id) | 37 const std::string& cert_id, |
| 38 int slot_id) | |
| 42 : service_path(network_path), | 39 : service_path(network_path), |
| 43 cert_config_type(config_type), | 40 cert_config_type(config_type), |
| 44 pkcs11_id(cert_id) {} | 41 pkcs11_id(cert_id), |
| 42 key_slot_id(slot_id) {} | |
| 45 | 43 |
| 46 std::string service_path; | 44 std::string service_path; |
| 47 client_cert::ConfigType cert_config_type; | 45 client_cert::ConfigType cert_config_type; |
| 48 | 46 |
| 49 // The id of the matching certificate or empty if no certificate was found. | 47 // The id of the matching certificate or empty if no certificate was found. |
| 50 std::string pkcs11_id; | 48 std::string pkcs11_id; |
| 49 | |
| 50 // The id of the slot containing the certificate and the private key. | |
| 51 int key_slot_id; | |
| 51 }; | 52 }; |
| 52 | 53 |
| 53 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert> | 54 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert> |
| 54 NetworkCertMatches; | 55 NetworkCertMatches; |
| 55 | 56 |
| 56 namespace { | 57 namespace { |
| 57 | 58 |
| 58 // Returns true if |vector| contains |value|. | 59 // Returns true if |vector| contains |value|. |
| 59 template <class T> | 60 template <class T> |
| 60 bool ContainsValue(const std::vector<T>& vector, const T& value) { | 61 bool ContainsValue(const std::vector<T>& vector, const T& value) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 NetworkAndCertPattern(const std::string& network_path, | 94 NetworkAndCertPattern(const std::string& network_path, |
| 94 const client_cert::ClientCertConfig& client_cert_config) | 95 const client_cert::ClientCertConfig& client_cert_config) |
| 95 : service_path(network_path), | 96 : service_path(network_path), |
| 96 cert_config(client_cert_config) {} | 97 cert_config(client_cert_config) {} |
| 97 | 98 |
| 98 std::string service_path; | 99 std::string service_path; |
| 99 client_cert::ClientCertConfig cert_config; | 100 client_cert::ClientCertConfig cert_config; |
| 100 }; | 101 }; |
| 101 | 102 |
| 102 // A unary predicate that returns true if the given CertAndIssuer matches the | 103 // A unary predicate that returns true if the given CertAndIssuer matches the |
| 103 // certificate pattern of the NetworkAndCertPattern. | 104 // given certificate pattern. |
| 104 struct MatchCertWithPattern { | 105 struct MatchCertWithPattern { |
| 105 explicit MatchCertWithPattern(const NetworkAndCertPattern& pattern) | 106 explicit MatchCertWithPattern(const CertificatePattern& cert_pattern) |
| 106 : net_and_pattern(pattern) {} | 107 : pattern(cert_pattern) {} |
| 107 | 108 |
| 108 bool operator()(const CertAndIssuer& cert_and_issuer) { | 109 bool operator()(const CertAndIssuer& cert_and_issuer) { |
| 109 const CertificatePattern& pattern = net_and_pattern.cert_config.pattern; | |
| 110 if (!pattern.issuer().Empty() && | 110 if (!pattern.issuer().Empty() && |
| 111 !client_cert::CertPrincipalMatches(pattern.issuer(), | 111 !client_cert::CertPrincipalMatches(pattern.issuer(), |
| 112 cert_and_issuer.cert->issuer())) { | 112 cert_and_issuer.cert->issuer())) { |
| 113 return false; | 113 return false; |
| 114 } | 114 } |
| 115 if (!pattern.subject().Empty() && | 115 if (!pattern.subject().Empty() && |
| 116 !client_cert::CertPrincipalMatches(pattern.subject(), | 116 !client_cert::CertPrincipalMatches(pattern.subject(), |
| 117 cert_and_issuer.cert->subject())) { | 117 cert_and_issuer.cert->subject())) { |
| 118 return false; | 118 return false; |
| 119 } | 119 } |
| 120 | 120 |
| 121 const std::vector<std::string>& issuer_ca_pems = pattern.issuer_ca_pems(); | 121 const std::vector<std::string>& issuer_ca_pems = pattern.issuer_ca_pems(); |
| 122 if (!issuer_ca_pems.empty() && | 122 if (!issuer_ca_pems.empty() && |
| 123 !ContainsValue(issuer_ca_pems, cert_and_issuer.pem_encoded_issuer)) { | 123 !ContainsValue(issuer_ca_pems, cert_and_issuer.pem_encoded_issuer)) { |
| 124 return false; | 124 return false; |
| 125 } | 125 } |
| 126 return true; | 126 return true; |
| 127 } | 127 } |
| 128 | 128 |
| 129 NetworkAndCertPattern net_and_pattern; | 129 const CertificatePattern pattern; |
| 130 }; | 130 }; |
| 131 | 131 |
| 132 // Searches for matches between |networks| and |certs| and writes matches to | 132 std::vector<CertAndIssuer> CreateSortedCertAndIssuerList( |
| 133 // |matches|. Because this calls NSS functions and is potentially slow, it must | 133 const net::CertificateList& certs) { |
| 134 // be run on a worker thread. | |
| 135 void FindCertificateMatches(const net::CertificateList& certs, | |
| 136 std::vector<NetworkAndCertPattern>* networks, | |
| 137 NetworkCertMatches* matches) { | |
| 138 // Filter all client certs and determines each certificate's issuer, which is | 134 // Filter all client certs and determines each certificate's issuer, which is |
| 139 // required for the pattern matching. | 135 // required for the pattern matching. |
| 140 std::vector<CertAndIssuer> client_certs; | 136 std::vector<CertAndIssuer> client_certs; |
| 141 for (net::CertificateList::const_iterator it = certs.begin(); | 137 for (net::CertificateList::const_iterator it = certs.begin(); |
| 142 it != certs.end(); ++it) { | 138 it != certs.end(); ++it) { |
| 143 const net::X509Certificate& cert = **it; | 139 const net::X509Certificate& cert = **it; |
| 144 if (cert.valid_expiry().is_null() || cert.HasExpired() || | 140 if (cert.valid_expiry().is_null() || cert.HasExpired() || |
| 145 !HasPrivateKey(cert)) { | 141 !HasPrivateKey(cert)) { |
| 146 continue; | 142 continue; |
| 147 } | 143 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 162 std::string pem_encoded_issuer; | 158 std::string pem_encoded_issuer; |
| 163 if (!net::X509Certificate::GetPEMEncoded(issuer->os_cert_handle(), | 159 if (!net::X509Certificate::GetPEMEncoded(issuer->os_cert_handle(), |
| 164 &pem_encoded_issuer)) { | 160 &pem_encoded_issuer)) { |
| 165 LOG(ERROR) << "Couldn't PEM-encode certificate."; | 161 LOG(ERROR) << "Couldn't PEM-encode certificate."; |
| 166 continue; | 162 continue; |
| 167 } | 163 } |
| 168 client_certs.push_back(CertAndIssuer(*it, pem_encoded_issuer)); | 164 client_certs.push_back(CertAndIssuer(*it, pem_encoded_issuer)); |
| 169 } | 165 } |
| 170 | 166 |
| 171 std::sort(client_certs.begin(), client_certs.end(), &CompareCertExpiration); | 167 std::sort(client_certs.begin(), client_certs.end(), &CompareCertExpiration); |
| 168 return client_certs; | |
| 169 } | |
| 170 | |
| 171 // Searches for matches between |networks| and |certs| and writes matches to | |
| 172 // |matches|. Because this calls NSS functions and is potentially slow, it must | |
| 173 // be run on a worker thread. | |
| 174 void FindCertificateMatches(const net::CertificateList& certs, | |
| 175 std::vector<NetworkAndCertPattern>* networks, | |
| 176 NetworkCertMatches* matches) { | |
| 177 std::vector<CertAndIssuer> client_certs(CreateSortedCertAndIssuerList(certs)); | |
| 172 | 178 |
| 173 for (std::vector<NetworkAndCertPattern>::const_iterator it = | 179 for (std::vector<NetworkAndCertPattern>::const_iterator it = |
| 174 networks->begin(); | 180 networks->begin(); |
| 175 it != networks->end(); ++it) { | 181 it != networks->end(); ++it) { |
| 176 std::vector<CertAndIssuer>::iterator cert_it = std::find_if( | 182 std::vector<CertAndIssuer>::iterator cert_it = |
| 177 client_certs.begin(), client_certs.end(), MatchCertWithPattern(*it)); | 183 std::find_if(client_certs.begin(), |
| 184 client_certs.end(), | |
| 185 MatchCertWithPattern(it->cert_config.pattern)); | |
| 178 std::string pkcs11_id; | 186 std::string pkcs11_id; |
| 187 int slot_id = -1; | |
| 179 if (cert_it == client_certs.end()) { | 188 if (cert_it == client_certs.end()) { |
| 180 VLOG(1) << "Couldn't find a matching client cert for network " | 189 VLOG(1) << "Couldn't find a matching client cert for network " |
| 181 << it->service_path; | 190 << it->service_path; |
| 182 // Leave |pkcs11_id empty| to indicate that no cert was found for this | 191 // Leave |pkcs11_id| empty to indicate that no cert was found for this |
| 183 // network. | 192 // network. |
| 184 } else { | 193 } else { |
| 185 pkcs11_id = CertLoader::GetPkcs11IdForCert(*cert_it->cert); | 194 pkcs11_id = |
| 195 CertLoader::GetPkcs11IdAndSlotForCert(*cert_it->cert, &slot_id); | |
| 186 if (pkcs11_id.empty()) { | 196 if (pkcs11_id.empty()) { |
| 187 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; | 197 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; |
| 188 // So far this error is not expected to happen. We can just continue, in | 198 // So far this error is not expected to happen. We can just continue, in |
| 189 // the worst case the user can remove the problematic cert. | 199 // the worst case the user can remove the problematic cert. |
| 190 continue; | 200 continue; |
| 191 } | 201 } |
| 192 } | 202 } |
| 193 matches->push_back(ClientCertResolver::NetworkAndMatchingCert( | 203 matches->push_back(ClientCertResolver::NetworkAndMatchingCert( |
| 194 it->service_path, it->cert_config.location, pkcs11_id)); | 204 it->service_path, it->cert_config.location, pkcs11_id, slot_id)); |
| 195 } | 205 } |
| 196 } | 206 } |
| 197 | 207 |
| 198 void LogError(const std::string& service_path, | 208 void LogError(const std::string& service_path, |
| 199 const std::string& dbus_error_name, | 209 const std::string& dbus_error_name, |
| 200 const std::string& dbus_error_message) { | 210 const std::string& dbus_error_message) { |
| 201 network_handler::ShillErrorCallbackFunction( | 211 network_handler::ShillErrorCallbackFunction( |
| 202 "ClientCertResolver.SetProperties failed", | 212 "ClientCertResolver.SetProperties failed", |
| 203 service_path, | 213 service_path, |
| 204 network_handler::ErrorCallback(), | 214 network_handler::ErrorCallback(), |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 247 managed_network_config_handler_->AddObserver(this); | 257 managed_network_config_handler_->AddObserver(this); |
| 248 | 258 |
| 249 CertLoader::Get()->AddObserver(this); | 259 CertLoader::Get()->AddObserver(this); |
| 250 } | 260 } |
| 251 | 261 |
| 252 void ClientCertResolver::SetSlowTaskRunnerForTest( | 262 void ClientCertResolver::SetSlowTaskRunnerForTest( |
| 253 const scoped_refptr<base::TaskRunner>& task_runner) { | 263 const scoped_refptr<base::TaskRunner>& task_runner) { |
| 254 slow_task_runner_for_test_ = task_runner; | 264 slow_task_runner_for_test_ = task_runner; |
| 255 } | 265 } |
| 256 | 266 |
| 267 // static | |
| 268 bool ClientCertResolver::ResolveCertificatePatternSync( | |
| 269 const client_cert::ConfigType client_cert_type, | |
| 270 const CertificatePattern& pattern, | |
| 271 base::DictionaryValue* config_properties) { | |
|
stevenjb
2014/07/29 18:31:10
nit: name this shill_properties maybe? I tend to a
| |
| 272 // Prepare and sort the list of known client certs. | |
| 273 std::vector<CertAndIssuer> client_certs( | |
| 274 CreateSortedCertAndIssuerList(CertLoader::Get()->cert_list())); | |
| 275 | |
| 276 // Search for a certificate matching the pattern. | |
| 277 std::vector<CertAndIssuer>::iterator cert_it = std::find_if( | |
| 278 client_certs.begin(), client_certs.end(), MatchCertWithPattern(pattern)); | |
| 279 | |
| 280 if (cert_it == client_certs.end()) { | |
| 281 VLOG(1) << "Couldn't find a matching client cert"; | |
| 282 client_cert::SetEmptyShillProperties(client_cert_type, config_properties); | |
| 283 return false; | |
| 284 } | |
| 285 | |
| 286 int slot_id = -1; | |
| 287 std::string pkcs11_id = | |
| 288 CertLoader::GetPkcs11IdAndSlotForCert(*cert_it->cert, &slot_id); | |
| 289 if (pkcs11_id.empty()) { | |
| 290 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; | |
| 291 // So far this error is not expected to happen. We can just continue, in | |
| 292 // the worst case the user can remove the problematic cert. | |
| 293 return false; | |
| 294 } | |
| 295 client_cert::SetShillProperties( | |
| 296 client_cert_type, slot_id, pkcs11_id, config_properties); | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 257 void ClientCertResolver::NetworkListChanged() { | 300 void ClientCertResolver::NetworkListChanged() { |
| 258 VLOG(2) << "NetworkListChanged."; | 301 VLOG(2) << "NetworkListChanged."; |
| 259 if (!ClientCertificatesLoaded()) | 302 if (!ClientCertificatesLoaded()) |
| 260 return; | 303 return; |
| 261 // Configure only networks that were not configured before. | 304 // Configure only networks that were not configured before. |
| 262 | 305 |
| 263 // We'll drop networks from |resolved_networks_|, which are not known anymore. | 306 // We'll drop networks from |resolved_networks_|, which are not known anymore. |
| 264 std::set<std::string> old_resolved_networks; | 307 std::set<std::string> old_resolved_networks; |
| 265 old_resolved_networks.swap(resolved_networks_); | 308 old_resolved_networks.swap(resolved_networks_); |
| 266 | 309 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 378 matches), | 421 matches), |
| 379 base::Bind(&ClientCertResolver::ConfigureCertificates, | 422 base::Bind(&ClientCertResolver::ConfigureCertificates, |
| 380 weak_ptr_factory_.GetWeakPtr(), | 423 weak_ptr_factory_.GetWeakPtr(), |
| 381 base::Owned(matches))); | 424 base::Owned(matches))); |
| 382 } | 425 } |
| 383 | 426 |
| 384 void ClientCertResolver::ConfigureCertificates(NetworkCertMatches* matches) { | 427 void ClientCertResolver::ConfigureCertificates(NetworkCertMatches* matches) { |
| 385 for (NetworkCertMatches::const_iterator it = matches->begin(); | 428 for (NetworkCertMatches::const_iterator it = matches->begin(); |
| 386 it != matches->end(); ++it) { | 429 it != matches->end(); ++it) { |
| 387 VLOG(1) << "Configuring certificate of network " << it->service_path; | 430 VLOG(1) << "Configuring certificate of network " << it->service_path; |
| 388 CertLoader* cert_loader = CertLoader::Get(); | |
| 389 base::DictionaryValue shill_properties; | 431 base::DictionaryValue shill_properties; |
| 390 // If pkcs11_id is empty, this will clear the key/cert properties of this | 432 if (it->pkcs11_id.empty()) { |
| 391 // network. | 433 client_cert::SetEmptyShillProperties(it->cert_config_type, |
| 392 client_cert::SetShillProperties( | 434 &shill_properties); |
| 393 it->cert_config_type, | 435 } else { |
| 394 base::IntToString(cert_loader->TPMTokenSlotID()), | 436 client_cert::SetShillProperties(it->cert_config_type, |
| 395 TPMTokenLoader::Get()->tpm_user_pin(), | 437 it->key_slot_id, |
| 396 &it->pkcs11_id, | 438 it->pkcs11_id, |
| 397 &shill_properties); | 439 &shill_properties); |
| 440 } | |
| 398 DBusThreadManager::Get()->GetShillServiceClient()-> | 441 DBusThreadManager::Get()->GetShillServiceClient()-> |
| 399 SetProperties(dbus::ObjectPath(it->service_path), | 442 SetProperties(dbus::ObjectPath(it->service_path), |
| 400 shill_properties, | 443 shill_properties, |
| 401 base::Bind(&base::DoNothing), | 444 base::Bind(&base::DoNothing), |
| 402 base::Bind(&LogError, it->service_path)); | 445 base::Bind(&LogError, it->service_path)); |
| 403 network_state_handler_->RequestUpdateForNetwork(it->service_path); | 446 network_state_handler_->RequestUpdateForNetwork(it->service_path); |
| 404 } | 447 } |
| 405 } | 448 } |
| 406 | 449 |
| 407 } // namespace chromeos | 450 } // namespace chromeos |
| OLD | NEW |