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 3 // found in the LICENSE file. 4 5 #include "chromeos/network/client_cert_resolver.h" 6 7 #include <cert.h> 8 #include <certt.h> // for (SECCertUsageEnum) certUsageAnyCA 9 #include <pk11pub.h> 10 11 #include <algorithm> 12 #include <string> 13 14 #include "base/bind.h" 15 #include "base/location.h" 16 #include "base/stl_util.h" 17 #include "base/task_runner.h" 18 #include "base/threading/worker_pool.h" 19 #include "base/time/time.h" 20 #include "chromeos/cert_loader.h" 21 #include "chromeos/dbus/dbus_thread_manager.h" 22 #include "chromeos/dbus/shill_service_client.h" 23 #include "chromeos/network/managed_network_configuration_handler.h" 24 #include "chromeos/network/network_state.h" 25 #include "components/onc/onc_constants.h" 26 #include "dbus/object_path.h" 27 #include "net/cert/scoped_nss_types.h" 28 #include "net/cert/x509_certificate.h" 29 30 namespace chromeos { 31 32 // Describes a network |network_path| for which a matching certificate |cert_id| 33 // was found or for which no certificate was found (|cert_id| will be empty). 34 struct ClientCertResolver::NetworkAndMatchingCert { 35 NetworkAndMatchingCert(const std::string& network_path, 36 client_cert::ConfigType config_type, 37 const std::string& cert_id, 38 int slot_id) 39 : service_path(network_path), 40 cert_config_type(config_type), 41 pkcs11_id(cert_id), 42 key_slot_id(slot_id) {} 43 44 std::string service_path; 45 client_cert::ConfigType cert_config_type; 46 47 // The id of the matching certificate or empty if no certificate was found. 48 std::string pkcs11_id; 49 50 // The id of the slot containing the certificate and the private key. 51 int key_slot_id; 52 }; 53 54 typedef std::vector<ClientCertResolver::NetworkAndMatchingCert> 55 NetworkCertMatches; 56 57 namespace { 58 59 // Returns true if |vector| contains |value|. 60 template <class T> 61 bool ContainsValue(const std::vector<T>& vector, const T& value) { 62 return find(vector.begin(), vector.end(), value) != vector.end(); 63 } 64 65 // Returns true if a private key for certificate |cert| is installed. 66 bool HasPrivateKey(const net::X509Certificate& cert) { 67 PK11SlotInfo* slot = PK11_KeyForCertExists(cert.os_cert_handle(), NULL, NULL); 68 if (!slot) 69 return false; 70 71 PK11_FreeSlot(slot); 72 return true; 73 } 74 75 // Describes a certificate which is issued by |issuer| (encoded as PEM). 76 struct CertAndIssuer { 77 CertAndIssuer(const scoped_refptr<net::X509Certificate>& certificate, 78 const std::string& issuer) 79 : cert(certificate), 80 pem_encoded_issuer(issuer) {} 81 82 scoped_refptr<net::X509Certificate> cert; 83 std::string pem_encoded_issuer; 84 }; 85 86 bool CompareCertExpiration(const CertAndIssuer& a, 87 const CertAndIssuer& b) { 88 return (a.cert->valid_expiry() > b.cert->valid_expiry()); 89 } 90 91 // Describes a network that is configured with the certificate pattern 92 // |client_cert_pattern|. 93 struct NetworkAndCertPattern { 94 NetworkAndCertPattern(const std::string& network_path, 95 const client_cert::ClientCertConfig& client_cert_config) 96 : service_path(network_path), 97 cert_config(client_cert_config) {} 98 99 std::string service_path; 100 client_cert::ClientCertConfig cert_config; 101 }; 102 103 // A unary predicate that returns true if the given CertAndIssuer matches the 104 // given certificate pattern. 105 struct MatchCertWithPattern { 106 explicit MatchCertWithPattern(const CertificatePattern& cert_pattern) 107 : pattern(cert_pattern) {} 108 109 bool operator()(const CertAndIssuer& cert_and_issuer) { 110 if (!pattern.issuer().Empty() && 111 !client_cert::CertPrincipalMatches(pattern.issuer(), 112 cert_and_issuer.cert->issuer())) { 113 return false; 114 } 115 if (!pattern.subject().Empty() && 116 !client_cert::CertPrincipalMatches(pattern.subject(), 117 cert_and_issuer.cert->subject())) { 118 return false; 119 } 120 121 const std::vector<std::string>& issuer_ca_pems = pattern.issuer_ca_pems(); 122 if (!issuer_ca_pems.empty() && 123 !ContainsValue(issuer_ca_pems, cert_and_issuer.pem_encoded_issuer)) { 124 return false; 125 } 126 return true; 127 } 128 129 const CertificatePattern pattern; 130 }; 131 132 std::vector<CertAndIssuer> CreateSortedCertAndIssuerList( 133 const net::CertificateList& certs) { 134 // Filter all client certs and determines each certificate's issuer, which is 135 // required for the pattern matching. 136 std::vector<CertAndIssuer> client_certs; 137 for (net::CertificateList::const_iterator it = certs.begin(); 138 it != certs.end(); ++it) { 139 const net::X509Certificate& cert = **it; 140 if (cert.valid_expiry().is_null() || cert.HasExpired() || 141 !HasPrivateKey(cert)) { 142 continue; 143 } 144 net::ScopedCERTCertificate issuer_handle( 145 CERT_FindCertIssuer(cert.os_cert_handle(), PR_Now(), certUsageAnyCA)); 146 if (!issuer_handle) { 147 LOG(ERROR) << "Couldn't find an issuer."; 148 continue; 149 } 150 scoped_refptr<net::X509Certificate> issuer = 151 net::X509Certificate::CreateFromHandle( 152 issuer_handle.get(), 153 net::X509Certificate::OSCertHandles() /* no intermediate certs */); 154 if (!issuer.get()) { 155 LOG(ERROR) << "Couldn't create issuer cert."; 156 continue; 157 } 158 std::string pem_encoded_issuer; 159 if (!net::X509Certificate::GetPEMEncoded(issuer->os_cert_handle(), 160 &pem_encoded_issuer)) { 161 LOG(ERROR) << "Couldn't PEM-encode certificate."; 162 continue; 163 } 164 client_certs.push_back(CertAndIssuer(*it, pem_encoded_issuer)); 165 } 166 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)); 178 179 for (std::vector<NetworkAndCertPattern>::const_iterator it = 180 networks->begin(); 181 it != networks->end(); ++it) { 182 std::vector<CertAndIssuer>::iterator cert_it = 183 std::find_if(client_certs.begin(), 184 client_certs.end(), 185 MatchCertWithPattern(it->cert_config.pattern)); 186 std::string pkcs11_id; 187 int slot_id = -1; 188 if (cert_it == client_certs.end()) { 189 VLOG(1) << "Couldn't find a matching client cert for network " 190 << it->service_path; 191 // Leave |pkcs11_id| empty to indicate that no cert was found for this 192 // network. 193 } else { 194 pkcs11_id = 195 CertLoader::GetPkcs11IdAndSlotForCert(*cert_it->cert, &slot_id); 196 if (pkcs11_id.empty()) { 197 LOG(ERROR) << "Couldn't determine PKCS#11 ID."; 198 // So far this error is not expected to happen. We can just continue, in 199 // the worst case the user can remove the problematic cert. 200 continue; 201 } 202 } 203 matches->push_back(ClientCertResolver::NetworkAndMatchingCert( 204 it->service_path, it->cert_config.location, pkcs11_id, slot_id)); 205 } 206 } 207 208 void LogError(const std::string& service_path, 209 const std::string& dbus_error_name, 210 const std::string& dbus_error_message) { 211 network_handler::ShillErrorCallbackFunction( 212 "ClientCertResolver.SetProperties failed", 213 service_path, 214 network_handler::ErrorCallback(), 215 dbus_error_name, 216 dbus_error_message); 217 } 218 219 bool ClientCertificatesLoaded() { 220 if (!CertLoader::Get()->certificates_loaded()) { 221 VLOG(1) << "Certificates not loaded yet."; 222 return false; 223 } 224 if (!CertLoader::Get()->IsHardwareBacked()) { 225 VLOG(1) << "TPM is not available."; 226 return false; 227 } 228 return true; 229 } 230 231 } // namespace 232 233 ClientCertResolver::ClientCertResolver() 234 : network_state_handler_(NULL), 235 managed_network_config_handler_(NULL), 236 weak_ptr_factory_(this) { 237 } 238 239 ClientCertResolver::~ClientCertResolver() { 240 if (network_state_handler_) 241 network_state_handler_->RemoveObserver(this, FROM_HERE); 242 if (CertLoader::IsInitialized()) 243 CertLoader::Get()->RemoveObserver(this); 244 if (managed_network_config_handler_) 245 managed_network_config_handler_->RemoveObserver(this); 246 } 247 248 void ClientCertResolver::Init( 249 NetworkStateHandler* network_state_handler, 250 ManagedNetworkConfigurationHandler* managed_network_config_handler) { 251 DCHECK(network_state_handler); 252 network_state_handler_ = network_state_handler; 253 network_state_handler_->AddObserver(this, FROM_HERE); 254 255 DCHECK(managed_network_config_handler); 256 managed_network_config_handler_ = managed_network_config_handler; 257 managed_network_config_handler_->AddObserver(this); 258 259 CertLoader::Get()->AddObserver(this); 260 } 261 262 void ClientCertResolver::SetSlowTaskRunnerForTest( 263 const scoped_refptr<base::TaskRunner>& task_runner) { 264 slow_task_runner_for_test_ = task_runner; 265 } 266 267 // static 268 bool ClientCertResolver::ResolveCertificatePatternSync( 269 const client_cert::ConfigType client_cert_type, 270 const CertificatePattern& pattern, 271 base::DictionaryValue* shill_properties) { 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, shill_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, shill_properties); 297 return true; 298 } 299 300 void ClientCertResolver::NetworkListChanged() { 301 VLOG(2) << "NetworkListChanged."; 302 if (!ClientCertificatesLoaded()) 303 return; 304 // Configure only networks that were not configured before. 305 306 // We'll drop networks from |resolved_networks_|, which are not known anymore. 307 std::set<std::string> old_resolved_networks; 308 old_resolved_networks.swap(resolved_networks_); 309 310 NetworkStateHandler::NetworkStateList networks; 311 network_state_handler_->GetNetworkListByType( 312 NetworkTypePattern::Default(), 313 true /* configured_only */, 314 false /* visible_only */, 315 0 /* no limit */, 316 &networks); 317 318 NetworkStateHandler::NetworkStateList networks_to_check; 319 for (NetworkStateHandler::NetworkStateList::const_iterator it = 320 networks.begin(); it != networks.end(); ++it) { 321 const std::string& service_path = (*it)->path(); 322 if (ContainsKey(old_resolved_networks, service_path)) { 323 resolved_networks_.insert(service_path); 324 continue; 325 } 326 networks_to_check.push_back(*it); 327 } 328 329 ResolveNetworks(networks_to_check); 330 } 331 332 void ClientCertResolver::OnCertificatesLoaded( 333 const net::CertificateList& cert_list, 334 bool initial_load) { 335 VLOG(2) << "OnCertificatesLoaded."; 336 if (!ClientCertificatesLoaded()) 337 return; 338 // Compare all networks with all certificates. 339 NetworkStateHandler::NetworkStateList networks; 340 network_state_handler_->GetNetworkListByType( 341 NetworkTypePattern::Default(), 342 true /* configured_only */, 343 false /* visible_only */, 344 0 /* no limit */, 345 &networks); 346 ResolveNetworks(networks); 347 } 348 349 void ClientCertResolver::PolicyApplied(const std::string& service_path) { 350 VLOG(2) << "PolicyApplied " << service_path; 351 if (!ClientCertificatesLoaded()) 352 return; 353 // Compare this network with all certificates. 354 const NetworkState* network = 355 network_state_handler_->GetNetworkStateFromServicePath( 356 service_path, true /* configured_only */); 357 if (!network) { 358 LOG(ERROR) << "service path '" << service_path << "' unknown."; 359 return; 360 } 361 NetworkStateHandler::NetworkStateList networks; 362 networks.push_back(network); 363 ResolveNetworks(networks); 364 } 365 366 void ClientCertResolver::ResolveNetworks( 367 const NetworkStateHandler::NetworkStateList& networks) { 368 scoped_ptr<std::vector<NetworkAndCertPattern> > networks_with_pattern( 369 new std::vector<NetworkAndCertPattern>); 370 371 // Filter networks with ClientCertPattern. As ClientCertPatterns can only be 372 // set by policy, we check there. 373 for (NetworkStateHandler::NetworkStateList::const_iterator it = 374 networks.begin(); it != networks.end(); ++it) { 375 const NetworkState* network = *it; 376 377 // In any case, don't check this network again in NetworkListChanged. 378 resolved_networks_.insert(network->path()); 379 380 // If this network is not configured, it cannot have a ClientCertPattern. 381 if (network->profile_path().empty()) 382 continue; 383 384 const base::DictionaryValue* policy = 385 managed_network_config_handler_->FindPolicyByGuidAndProfile( 386 network->guid(), network->profile_path()); 387 388 if (!policy) { 389 VLOG(1) << "The policy for network " << network->path() << " with GUID " 390 << network->guid() << " is not available yet."; 391 // Skip this network for now. Once the policy is loaded, PolicyApplied() 392 // will retry. 393 continue; 394 } 395 396 VLOG(2) << "Inspecting network " << network->path(); 397 client_cert::ClientCertConfig cert_config; 398 OncToClientCertConfig(*policy, &cert_config); 399 400 // Skip networks that don't have a ClientCertPattern. 401 if (cert_config.client_cert_type != ::onc::client_cert::kPattern) 402 continue; 403 404 networks_with_pattern->push_back( 405 NetworkAndCertPattern(network->path(), cert_config)); 406 } 407 if (networks_with_pattern->empty()) 408 return; 409 410 VLOG(2) << "Start task for resolving client cert patterns."; 411 base::TaskRunner* task_runner = slow_task_runner_for_test_.get(); 412 if (!task_runner) 413 task_runner = 414 base::WorkerPool::GetTaskRunner(true /* task is slow */).get(); 415 416 NetworkCertMatches* matches = new NetworkCertMatches; 417 task_runner->PostTaskAndReply( 418 FROM_HERE, 419 base::Bind(&FindCertificateMatches, 420 CertLoader::Get()->cert_list(), 421 base::Owned(networks_with_pattern.release()), 422 matches), 423 base::Bind(&ClientCertResolver::ConfigureCertificates, 424 weak_ptr_factory_.GetWeakPtr(), 425 base::Owned(matches))); 426 } 427 428 void ClientCertResolver::ConfigureCertificates(NetworkCertMatches* matches) { 429 for (NetworkCertMatches::const_iterator it = matches->begin(); 430 it != matches->end(); ++it) { 431 VLOG(1) << "Configuring certificate of network " << it->service_path; 432 base::DictionaryValue shill_properties; 433 if (it->pkcs11_id.empty()) { 434 client_cert::SetEmptyShillProperties(it->cert_config_type, 435 &shill_properties); 436 } else { 437 client_cert::SetShillProperties(it->cert_config_type, 438 it->key_slot_id, 439 it->pkcs11_id, 440 &shill_properties); 441 } 442 DBusThreadManager::Get()->GetShillServiceClient()-> 443 SetProperties(dbus::ObjectPath(it->service_path), 444 shill_properties, 445 base::Bind(&base::DoNothing), 446 base::Bind(&LogError, it->service_path)); 447 network_state_handler_->RequestUpdateForNetwork(it->service_path); 448 } 449 } 450 451 } // namespace chromeos 452