| 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 "net/cert/cert_verify_proc_mac.h" | 5 #include "net/cert/cert_verify_proc_mac.h" |
| 6 | 6 |
| 7 #include <CommonCrypto/CommonDigest.h> | 7 #include <CommonCrypto/CommonDigest.h> |
| 8 #include <CoreServices/CoreServices.h> | 8 #include <CoreServices/CoreServices.h> |
| 9 #include <Security/Security.h> | 9 #include <Security/Security.h> |
| 10 | 10 |
| 11 #include <set> | 11 #include <set> |
| 12 #include <string> | 12 #include <string> |
| 13 #include <vector> | 13 #include <vector> |
| 14 | 14 |
| 15 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/mac/mac_logging.h" | 17 #include "base/mac/mac_logging.h" |
| 18 #include "base/mac/mac_util.h" |
| 18 #include "base/mac/scoped_cftyperef.h" | 19 #include "base/mac/scoped_cftyperef.h" |
| 19 #include "base/sha1.h" | 20 #include "base/sha1.h" |
| 20 #include "base/strings/string_piece.h" | 21 #include "base/strings/string_piece.h" |
| 21 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" |
| 22 #include "crypto/mac_security_services_lock.h" | 23 #include "crypto/mac_security_services_lock.h" |
| 23 #include "crypto/sha2.h" | 24 #include "crypto/sha2.h" |
| 24 #include "net/base/hash_value.h" | 25 #include "net/base/hash_value.h" |
| 25 #include "net/base/net_errors.h" | 26 #include "net/base/net_errors.h" |
| 26 #include "net/cert/asn1_util.h" | 27 #include "net/cert/asn1_util.h" |
| 27 #include "net/cert/cert_status_flags.h" | 28 #include "net/cert/cert_status_flags.h" |
| 28 #include "net/cert/cert_verifier.h" | 29 #include "net/cert/cert_verifier.h" |
| 29 #include "net/cert/cert_verify_result.h" | 30 #include "net/cert/cert_verify_result.h" |
| 30 #include "net/cert/crl_set.h" | 31 #include "net/cert/crl_set.h" |
| 32 #include "net/cert/ev_root_ca_metadata.h" |
| 33 #include "net/cert/internal/certificate_policies.h" |
| 34 #include "net/cert/internal/parsed_certificate.h" |
| 31 #include "net/cert/test_keychain_search_list_mac.h" | 35 #include "net/cert/test_keychain_search_list_mac.h" |
| 32 #include "net/cert/test_root_certs.h" | 36 #include "net/cert/test_root_certs.h" |
| 33 #include "net/cert/x509_certificate.h" | 37 #include "net/cert/x509_certificate.h" |
| 34 #include "net/cert/x509_util_mac.h" | 38 #include "net/cert/x509_util_mac.h" |
| 35 | 39 |
| 36 // CSSM functions are deprecated as of OSX 10.7, but have no replacement. | 40 // CSSM functions are deprecated as of OSX 10.7, but have no replacement. |
| 37 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 | 41 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 |
| 38 #pragma clang diagnostic push | 42 #pragma clang diagnostic push |
| 39 #pragma clang diagnostic ignored "-Wdeprecated-declarations" | 43 #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| 40 | 44 |
| 41 // From 10.7.2 libsecurity_keychain-55035/lib/SecTrustPriv.h, for use with | |
| 42 // SecTrustCopyExtendedResult. | |
| 43 #ifndef kSecEVOrganizationName | |
| 44 #define kSecEVOrganizationName CFSTR("Organization") | |
| 45 #endif | |
| 46 | |
| 47 using base::ScopedCFTypeRef; | 45 using base::ScopedCFTypeRef; |
| 48 | 46 |
| 49 namespace net { | 47 namespace net { |
| 50 | 48 |
| 51 namespace { | 49 namespace { |
| 52 | 50 |
| 53 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, | 51 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, |
| 54 CFDictionaryRef*); | 52 CFDictionaryRef*); |
| 55 | 53 |
| 56 int NetErrorFromOSStatus(OSStatus status) { | 54 int NetErrorFromOSStatus(OSStatus status) { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy); | 162 x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy); |
| 165 if (status) | 163 if (status) |
| 166 return status; | 164 return status; |
| 167 CFArrayAppendValue(local_policies, ssl_policy); | 165 CFArrayAppendValue(local_policies, ssl_policy); |
| 168 CFRelease(ssl_policy); | 166 CFRelease(ssl_policy); |
| 169 | 167 |
| 170 // Explicitly add revocation policies, in order to override system | 168 // Explicitly add revocation policies, in order to override system |
| 171 // revocation checking policies and instead respect the application-level | 169 // revocation checking policies and instead respect the application-level |
| 172 // revocation preference. | 170 // revocation preference. |
| 173 status = x509_util::CreateRevocationPolicies( | 171 status = x509_util::CreateRevocationPolicies( |
| 174 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), | 172 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), local_policies); |
| 175 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY), | |
| 176 local_policies); | |
| 177 if (status) | 173 if (status) |
| 178 return status; | 174 return status; |
| 179 | 175 |
| 180 policies->reset(local_policies.release()); | 176 policies->reset(local_policies.release()); |
| 181 return noErr; | 177 return noErr; |
| 182 } | 178 } |
| 183 | 179 |
| 184 // Stores the constructed certificate chain |cert_chain| and information about | 180 // Stores the constructed certificate chain |cert_chain| and information about |
| 185 // the signature algorithms used into |*verify_result|. If the leaf cert in | 181 // the signature algorithms used into |*verify_result|. If the leaf cert in |
| 186 // |cert_chain| contains a weak (MD2, MD4, MD5, SHA-1) signature, stores that | 182 // |cert_chain| contains a weak (MD2, MD4, MD5, SHA-1) signature, stores that |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 } | 261 } |
| 266 if (!verified_cert) { | 262 if (!verified_cert) { |
| 267 NOTREACHED(); | 263 NOTREACHED(); |
| 268 return; | 264 return; |
| 269 } | 265 } |
| 270 | 266 |
| 271 verify_result->verified_cert = | 267 verify_result->verified_cert = |
| 272 X509Certificate::CreateFromHandle(verified_cert, verified_chain); | 268 X509Certificate::CreateFromHandle(verified_cert, verified_chain); |
| 273 } | 269 } |
| 274 | 270 |
| 271 using ExtensionsMap = std::map<net::der::Input, net::ParsedExtension>; |
| 272 |
| 273 // Helper that looks up an extension by OID given a map of extensions. |
| 274 bool GetExtensionValue(const ExtensionsMap& extensions, |
| 275 const net::der::Input& oid, |
| 276 net::der::Input* value) { |
| 277 auto it = extensions.find(oid); |
| 278 if (it == extensions.end()) |
| 279 return false; |
| 280 *value = it->second.value; |
| 281 return true; |
| 282 } |
| 283 |
| 284 // Checks if |*cert| has a Certificate Policies extension containing either |
| 285 // of |ev_policy_oid| or anyPolicy. |
| 286 bool HasPolicyOrAnyPolicy(const ParsedCertificate* cert, |
| 287 const der::Input& ev_policy_oid) { |
| 288 der::Input extension_value; |
| 289 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(), |
| 290 &extension_value)) { |
| 291 return false; |
| 292 } |
| 293 |
| 294 std::vector<der::Input> policies; |
| 295 if (!ParseCertificatePoliciesExtension(extension_value, &policies)) |
| 296 return false; |
| 297 |
| 298 for (const der::Input& policy_oid : policies) { |
| 299 if (policy_oid == ev_policy_oid || policy_oid == AnyPolicy()) |
| 300 return true; |
| 301 } |
| 302 return false; |
| 303 } |
| 304 |
| 305 // Looks for known EV policy OIDs in |cert_input|, if one is found it will be |
| 306 // stored in |*ev_policy_oid| as a DER-encoded OID value (no tag or length). |
| 307 void GetCandidateEVPolicy(const X509Certificate* cert_input, |
| 308 std::string* ev_policy_oid) { |
| 309 ev_policy_oid->clear(); |
| 310 |
| 311 std::string der_cert; |
| 312 if (!X509Certificate::GetDEREncoded(cert_input->os_cert_handle(), |
| 313 &der_cert)) { |
| 314 return; |
| 315 } |
| 316 |
| 317 scoped_refptr<ParsedCertificate> cert( |
| 318 ParsedCertificate::Create(der_cert, {}, nullptr)); |
| 319 if (!cert) |
| 320 return; |
| 321 |
| 322 der::Input extension_value; |
| 323 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(), |
| 324 &extension_value)) { |
| 325 return; |
| 326 } |
| 327 |
| 328 std::vector<der::Input> policies; |
| 329 if (!ParseCertificatePoliciesExtension(extension_value, &policies)) |
| 330 return; |
| 331 |
| 332 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); |
| 333 for (const der::Input& policy_oid : policies) { |
| 334 if (metadata->IsEVPolicyOID(policy_oid)) { |
| 335 *ev_policy_oid = policy_oid.AsString(); |
| 336 return; |
| 337 } |
| 338 } |
| 339 } |
| 340 |
| 341 // Checks that the certificate chain of |cert| has policies consistent with |
| 342 // |ev_policy_oid_string|. The leaf is not checked, as it is assumed that is |
| 343 // where the policy came from. |
| 344 bool CheckCertChainEV(const X509Certificate* cert, |
| 345 const std::string& ev_policy_oid_string) { |
| 346 der::Input ev_policy_oid(&ev_policy_oid_string); |
| 347 X509Certificate::OSCertHandles os_cert_chain = |
| 348 cert->GetIntermediateCertificates(); |
| 349 |
| 350 // Root should have matching policy in EVRootCAMetadata. |
| 351 std::string der_cert; |
| 352 if (!X509Certificate::GetDEREncoded(os_cert_chain.back(), &der_cert)) |
| 353 return false; |
| 354 SHA1HashValue weak_fingerprint; |
| 355 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(der_cert.data()), |
| 356 der_cert.size(), weak_fingerprint.data); |
| 357 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); |
| 358 if (!metadata->HasEVPolicyOID(weak_fingerprint, ev_policy_oid)) |
| 359 return false; |
| 360 |
| 361 // Intermediates should have Certificate Policies extension with the EV policy |
| 362 // or AnyPolicy. |
| 363 for (size_t i = 0; i < os_cert_chain.size() - 1; ++i) { |
| 364 std::string der_cert; |
| 365 if (!X509Certificate::GetDEREncoded(os_cert_chain[i], &der_cert)) |
| 366 return false; |
| 367 scoped_refptr<ParsedCertificate> intermediate_cert( |
| 368 ParsedCertificate::Create(der_cert, {}, nullptr)); |
| 369 if (!intermediate_cert) |
| 370 return false; |
| 371 if (!HasPolicyOrAnyPolicy(intermediate_cert.get(), ev_policy_oid)) |
| 372 return false; |
| 373 } |
| 374 |
| 375 return true; |
| 376 } |
| 377 |
| 275 void AppendPublicKeyHashes(CFArrayRef chain, | 378 void AppendPublicKeyHashes(CFArrayRef chain, |
| 276 HashValueVector* hashes) { | 379 HashValueVector* hashes) { |
| 277 const CFIndex n = CFArrayGetCount(chain); | 380 const CFIndex n = CFArrayGetCount(chain); |
| 278 for (CFIndex i = 0; i < n; i++) { | 381 for (CFIndex i = 0; i < n; i++) { |
| 279 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( | 382 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( |
| 280 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); | 383 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); |
| 281 | 384 |
| 282 CSSM_DATA cert_data; | 385 CSSM_DATA cert_data; |
| 283 OSStatus err = SecCertificateGetData(cert, &cert_data); | 386 OSStatus err = SecCertificateGetData(cert, &cert_data); |
| 284 DCHECK_EQ(err, noErr); | 387 DCHECK_EQ(err, noErr); |
| 285 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), | 388 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), |
| 286 cert_data.Length); | 389 cert_data.Length); |
| 287 base::StringPiece spki_bytes; | 390 base::StringPiece spki_bytes; |
| 288 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) | 391 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) |
| 289 continue; | 392 continue; |
| 290 | 393 |
| 291 HashValue sha1(HASH_VALUE_SHA1); | 394 HashValue sha1(HASH_VALUE_SHA1); |
| 292 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data()); | 395 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data()); |
| 293 hashes->push_back(sha1); | 396 hashes->push_back(sha1); |
| 294 | 397 |
| 295 HashValue sha256(HASH_VALUE_SHA256); | 398 HashValue sha256(HASH_VALUE_SHA256); |
| 296 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data()); | 399 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data()); |
| 297 hashes->push_back(sha256); | 400 hashes->push_back(sha256); |
| 298 } | 401 } |
| 299 } | 402 } |
| 300 | 403 |
| 301 bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) { | 404 enum CRLSetResult { |
| 405 kCRLSetOk, |
| 406 kCRLSetRevoked, |
| 407 kCRLSetUnknown, |
| 408 }; |
| 409 |
| 410 // CheckRevocationWithCRLSet attempts to check each element of |cert_list| |
| 411 // against |crl_set|. It returns: |
| 412 // kCRLSetRevoked: if any element of the chain is known to have been revoked. |
| 413 // kCRLSetUnknown: if there is no fresh information about the leaf |
| 414 // certificate in the chain or if the CRLSet has expired. |
| 415 // |
| 416 // Only the leaf certificate is considered for coverage because some |
| 417 // intermediates have CRLs with no revocations (after filtering) and |
| 418 // those CRLs are pruned from the CRLSet at generation time. This means |
| 419 // that some EV sites would otherwise take the hit of an OCSP lookup for |
| 420 // no reason. |
| 421 // kCRLSetOk: otherwise. |
| 422 CRLSetResult CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) { |
| 302 if (CFArrayGetCount(chain) == 0) | 423 if (CFArrayGetCount(chain) == 0) |
| 303 return true; | 424 return kCRLSetOk; |
| 425 |
| 426 // error is set to true if any errors are found. It causes such chains to be |
| 427 // considered as not covered. |
| 428 bool error = false; |
| 429 // last_covered is set to the coverage state of the previous certificate. The |
| 430 // certificates are iterated over backwards thus, after the iteration, |
| 431 // |last_covered| contains the coverage state of the leaf certificate. |
| 432 bool last_covered = false; |
| 304 | 433 |
| 305 // We iterate from the root certificate down to the leaf, keeping track of | 434 // We iterate from the root certificate down to the leaf, keeping track of |
| 306 // the issuer's SPKI at each step. | 435 // the issuer's SPKI at each step. |
| 307 std::string issuer_spki_hash; | 436 std::string issuer_spki_hash; |
| 308 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) { | 437 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) { |
| 309 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( | 438 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( |
| 310 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); | 439 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); |
| 311 | 440 |
| 312 CSSM_DATA cert_data; | 441 CSSM_DATA cert_data; |
| 313 OSStatus err = SecCertificateGetData(cert, &cert_data); | 442 OSStatus err = SecCertificateGetData(cert, &cert_data); |
| 314 if (err != noErr) { | 443 if (err != noErr) { |
| 315 NOTREACHED(); | 444 NOTREACHED(); |
| 445 error = true; |
| 316 continue; | 446 continue; |
| 317 } | 447 } |
| 318 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), | 448 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), |
| 319 cert_data.Length); | 449 cert_data.Length); |
| 320 base::StringPiece spki; | 450 base::StringPiece spki; |
| 321 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) { | 451 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) { |
| 322 NOTREACHED(); | 452 NOTREACHED(); |
| 453 error = true; |
| 323 continue; | 454 continue; |
| 324 } | 455 } |
| 325 | 456 |
| 326 const std::string spki_hash = crypto::SHA256HashString(spki); | 457 const std::string spki_hash = crypto::SHA256HashString(spki); |
| 327 x509_util::CSSMCachedCertificate cached_cert; | 458 x509_util::CSSMCachedCertificate cached_cert; |
| 328 if (cached_cert.Init(cert) != CSSM_OK) { | 459 if (cached_cert.Init(cert) != CSSM_OK) { |
| 329 NOTREACHED(); | 460 NOTREACHED(); |
| 461 error = true; |
| 330 continue; | 462 continue; |
| 331 } | 463 } |
| 332 x509_util::CSSMFieldValue serial_number; | 464 x509_util::CSSMFieldValue serial_number; |
| 333 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number); | 465 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number); |
| 334 if (err || !serial_number.field()) { | 466 if (err || !serial_number.field()) { |
| 335 NOTREACHED(); | 467 NOTREACHED(); |
| 468 error = true; |
| 336 continue; | 469 continue; |
| 337 } | 470 } |
| 338 | 471 |
| 339 base::StringPiece serial( | 472 base::StringPiece serial( |
| 340 reinterpret_cast<const char*>(serial_number.field()->Data), | 473 reinterpret_cast<const char*>(serial_number.field()->Data), |
| 341 serial_number.field()->Length); | 474 serial_number.field()->Length); |
| 342 | 475 |
| 343 CRLSet::Result result = crl_set->CheckSPKI(spki_hash); | 476 CRLSet::Result result = crl_set->CheckSPKI(spki_hash); |
| 344 | 477 |
| 345 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty()) | 478 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty()) |
| 346 result = crl_set->CheckSerial(serial, issuer_spki_hash); | 479 result = crl_set->CheckSerial(serial, issuer_spki_hash); |
| 347 | 480 |
| 348 issuer_spki_hash = spki_hash; | 481 issuer_spki_hash = spki_hash; |
| 349 | 482 |
| 350 switch (result) { | 483 switch (result) { |
| 351 case CRLSet::REVOKED: | 484 case CRLSet::REVOKED: |
| 352 return false; | 485 return kCRLSetRevoked; |
| 353 case CRLSet::UNKNOWN: | 486 case CRLSet::UNKNOWN: |
| 487 last_covered = false; |
| 488 continue; |
| 354 case CRLSet::GOOD: | 489 case CRLSet::GOOD: |
| 490 last_covered = true; |
| 355 continue; | 491 continue; |
| 356 default: | 492 default: |
| 357 NOTREACHED(); | 493 NOTREACHED(); |
| 358 return false; | 494 error = true; |
| 495 continue; |
| 359 } | 496 } |
| 360 } | 497 } |
| 361 | 498 |
| 362 return true; | 499 if (error || !last_covered || crl_set->IsExpired()) |
| 500 return kCRLSetUnknown; |
| 501 return kCRLSetOk; |
| 363 } | 502 } |
| 364 | 503 |
| 365 // Builds and evaluates a SecTrustRef for the certificate chain contained | 504 // Builds and evaluates a SecTrustRef for the certificate chain contained |
| 366 // in |cert_array|, using the verification policies in |trust_policies|. On | 505 // in |cert_array|, using the verification policies in |trust_policies|. On |
| 367 // success, returns OK, and updates |trust_ref|, |trust_result|, | 506 // success, returns OK, and updates |trust_ref|, |trust_result|, |
| 368 // |verified_chain|, and |chain_info| with the verification results. On | 507 // |verified_chain|, and |chain_info| with the verification results. On |
| 369 // failure, no output parameters are modified. | 508 // failure, no output parameters are modified. |
| 370 // | 509 // |
| 371 // Note: An OK return does not mean that |cert_array| is trusted, merely that | 510 // Note: An OK return does not mean that |cert_array| is trusted, merely that |
| 372 // verification was performed successfully. | 511 // verification was performed successfully. |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 514 } | 653 } |
| 515 | 654 |
| 516 ~OSXKnownRootHelper() {} | 655 ~OSXKnownRootHelper() {} |
| 517 | 656 |
| 518 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_; | 657 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_; |
| 519 }; | 658 }; |
| 520 | 659 |
| 521 base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots = | 660 base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots = |
| 522 LAZY_INSTANCE_INITIALIZER; | 661 LAZY_INSTANCE_INITIALIZER; |
| 523 | 662 |
| 524 } // namespace | 663 // Runs path building & verification loop for |cert|, given |flags|. This is |
| 525 | 664 // split into a separate function so verification can be repeated with different |
| 526 CertVerifyProcMac::CertVerifyProcMac() {} | 665 // flags. This function does not handle EV. |
| 527 | 666 int VerifyWithGivenFlags(X509Certificate* cert, |
| 528 CertVerifyProcMac::~CertVerifyProcMac() {} | 667 const std::string& hostname, |
| 529 | 668 const int flags, |
| 530 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { | 669 CRLSet* crl_set, |
| 531 return false; | 670 CertVerifyResult* verify_result, |
| 532 } | 671 CRLSetResult* completed_chain_crl_result) { |
| 533 | |
| 534 bool CertVerifyProcMac::SupportsOCSPStapling() const { | |
| 535 // TODO(rsleevi): Plumb an OCSP response into the Mac system library. | |
| 536 // https://crbug.com/430714 | |
| 537 return false; | |
| 538 } | |
| 539 | |
| 540 int CertVerifyProcMac::VerifyInternal( | |
| 541 X509Certificate* cert, | |
| 542 const std::string& hostname, | |
| 543 const std::string& ocsp_response, | |
| 544 int flags, | |
| 545 CRLSet* crl_set, | |
| 546 const CertificateList& additional_trust_anchors, | |
| 547 CertVerifyResult* verify_result) { | |
| 548 ScopedCFTypeRef<CFArrayRef> trust_policies; | 672 ScopedCFTypeRef<CFArrayRef> trust_policies; |
| 549 OSStatus status = CreateTrustPolicies(flags, &trust_policies); | 673 OSStatus status = CreateTrustPolicies(flags, &trust_policies); |
| 550 if (status) | 674 if (status) |
| 551 return NetErrorFromOSStatus(status); | 675 return NetErrorFromOSStatus(status); |
| 552 | 676 |
| 677 *completed_chain_crl_result = kCRLSetUnknown; |
| 678 |
| 553 // Serialize all calls that may use the Keychain, to work around various | 679 // Serialize all calls that may use the Keychain, to work around various |
| 554 // issues in OS X 10.6+ with multi-threaded access to Security.framework. | 680 // issues in OS X 10.6+ with multi-threaded access to Security.framework. |
| 555 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); | 681 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); |
| 556 | 682 |
| 557 ScopedCFTypeRef<SecTrustRef> trust_ref; | 683 ScopedCFTypeRef<SecTrustRef> trust_ref; |
| 558 SecTrustResultType trust_result = kSecTrustResultDeny; | 684 SecTrustResultType trust_result = kSecTrustResultDeny; |
| 559 ScopedCFTypeRef<CFArrayRef> completed_chain; | 685 ScopedCFTypeRef<CFArrayRef> completed_chain; |
| 560 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL; | 686 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL; |
| 561 bool candidate_untrusted = true; | 687 bool candidate_untrusted = true; |
| 562 bool candidate_weak = false; | 688 bool candidate_weak = false; |
| 563 bool completed_chain_revoked = false; | |
| 564 | 689 |
| 565 // OS X lacks proper path discovery; it will take the input certs and never | 690 // OS X lacks proper path discovery; it will take the input certs and never |
| 566 // backtrack the graph attempting to discover valid paths. | 691 // backtrack the graph attempting to discover valid paths. |
| 567 // This can create issues in some situations: | 692 // This can create issues in some situations: |
| 568 // - When OS X changes the trust store, there may be a chain | 693 // - When OS X changes the trust store, there may be a chain |
| 569 // A -> B -> C -> D | 694 // A -> B -> C -> D |
| 570 // where OS X trusts D (on some versions) and trusts C (on some versions). | 695 // where OS X trusts D (on some versions) and trusts C (on some versions). |
| 571 // If a server supplies a chain A, B, C (cross-signed by D), then this chain | 696 // If a server supplies a chain A, B, C (cross-signed by D), then this chain |
| 572 // will successfully validate on systems that trust D, but fail for systems | 697 // will successfully validate on systems that trust D, but fail for systems |
| 573 // that trust C. If the server supplies a chain of A -> B, then it forces | 698 // that trust C. If the server supplies a chain of A -> B, then it forces |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 // The CRLSet checking is performed inside the loop in the hope that if a | 822 // The CRLSet checking is performed inside the loop in the hope that if a |
| 698 // path is revoked, it's an older path, and the only reason it was built | 823 // path is revoked, it's an older path, and the only reason it was built |
| 699 // is because the server forced it (by supplying an older or less | 824 // is because the server forced it (by supplying an older or less |
| 700 // desirable intermediate) or because the user had installed a | 825 // desirable intermediate) or because the user had installed a |
| 701 // certificate in their Keychain forcing this path. However, this means | 826 // certificate in their Keychain forcing this path. However, this means |
| 702 // its still possible for a CRLSet block of an intermediate to prevent | 827 // its still possible for a CRLSet block of an intermediate to prevent |
| 703 // access, even when there is a 'good' chain. To fully remedy this, a | 828 // access, even when there is a 'good' chain. To fully remedy this, a |
| 704 // solution might be to have CRLSets contain enough knowledge about what | 829 // solution might be to have CRLSets contain enough knowledge about what |
| 705 // the 'desired' path might be, but for the time being, the | 830 // the 'desired' path might be, but for the time being, the |
| 706 // implementation is kept as 'simple' as it can be. | 831 // implementation is kept as 'simple' as it can be. |
| 707 bool revoked = | 832 CRLSetResult crl_result = kCRLSetUnknown; |
| 708 (crl_set && !CheckRevocationWithCRLSet(temp_chain, crl_set)); | 833 if (crl_set) |
| 834 crl_result = CheckRevocationWithCRLSet(temp_chain, crl_set); |
| 709 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified && | 835 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified && |
| 710 temp_trust_result != kSecTrustResultProceed) || | 836 temp_trust_result != kSecTrustResultProceed) || |
| 711 revoked; | 837 crl_result == kCRLSetRevoked; |
| 712 bool weak_chain = false; | 838 bool weak_chain = false; |
| 713 if (CFArrayGetCount(temp_chain) == 0) { | 839 if (CFArrayGetCount(temp_chain) == 0) { |
| 714 // If the chain is empty, it cannot be trusted or have recoverable | 840 // If the chain is empty, it cannot be trusted or have recoverable |
| 715 // errors. | 841 // errors. |
| 716 DCHECK(untrusted); | 842 DCHECK(untrusted); |
| 717 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result); | 843 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result); |
| 718 } else { | 844 } else { |
| 719 CertVerifyResult temp_verify_result; | 845 CertVerifyResult temp_verify_result; |
| 720 bool leaf_is_weak = false; | 846 bool leaf_is_weak = false; |
| 721 GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result, | 847 GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result, |
| (...skipping 16 matching lines...) Expand all Loading... |
| 738 // old chain. | 864 // old chain. |
| 739 // | 865 // |
| 740 // Note: If the leaf certificate itself is weak, then the only | 866 // Note: If the leaf certificate itself is weak, then the only |
| 741 // consideration is whether or not there is a trusted chain. That's | 867 // consideration is whether or not there is a trusted chain. That's |
| 742 // because no amount of path discovery will fix a weak leaf. | 868 // because no amount of path discovery will fix a weak leaf. |
| 743 if (!trust_ref || (!untrusted && (candidate_untrusted || | 869 if (!trust_ref || (!untrusted && (candidate_untrusted || |
| 744 (candidate_weak && !weak_chain)))) { | 870 (candidate_weak && !weak_chain)))) { |
| 745 trust_ref = temp_ref; | 871 trust_ref = temp_ref; |
| 746 trust_result = temp_trust_result; | 872 trust_result = temp_trust_result; |
| 747 completed_chain = temp_chain; | 873 completed_chain = temp_chain; |
| 748 completed_chain_revoked = revoked; | 874 *completed_chain_crl_result = crl_result; |
| 749 chain_info = temp_chain_info; | 875 chain_info = temp_chain_info; |
| 750 | 876 |
| 751 candidate_untrusted = untrusted; | 877 candidate_untrusted = untrusted; |
| 752 candidate_weak = weak_chain; | 878 candidate_weak = weak_chain; |
| 753 } | 879 } |
| 754 // Short-circuit when a current, trusted chain is found. | 880 // Short-circuit when a current, trusted chain is found. |
| 755 if (!untrusted && !weak_chain) | 881 if (!untrusted && !weak_chain) |
| 756 break; | 882 break; |
| 757 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1); | 883 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1); |
| 758 } | 884 } |
| 759 // Short-circuit when a current, trusted chain is found. | 885 // Short-circuit when a current, trusted chain is found. |
| 760 if (!candidate_untrusted && !candidate_weak) | 886 if (!candidate_untrusted && !candidate_weak) |
| 761 break; | 887 break; |
| 762 } | 888 } |
| 763 | 889 |
| 764 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) | 890 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) |
| 765 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | 891 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; |
| 766 | 892 |
| 767 if (completed_chain_revoked) | 893 if (*completed_chain_crl_result == kCRLSetRevoked) |
| 768 verify_result->cert_status |= CERT_STATUS_REVOKED; | 894 verify_result->cert_status |= CERT_STATUS_REVOKED; |
| 769 | 895 |
| 770 if (CFArrayGetCount(completed_chain) > 0) { | 896 if (CFArrayGetCount(completed_chain) > 0) { |
| 771 bool leaf_is_weak_unused = false; | 897 bool leaf_is_weak_unused = false; |
| 772 GetCertChainInfo(completed_chain, chain_info, verify_result, | 898 GetCertChainInfo(completed_chain, chain_info, verify_result, |
| 773 &leaf_is_weak_unused); | 899 &leaf_is_weak_unused); |
| 774 } | 900 } |
| 775 | 901 |
| 776 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits | 902 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits |
| 777 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds | 903 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds |
| 778 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping | 904 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping |
| 779 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only | 905 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only |
| 780 // error was due to an unsupported key size. | 906 // error was due to an unsupported key size. |
| 781 bool policy_failed = false; | 907 bool policy_failed = false; |
| 908 bool policy_fail_already_mapped = false; |
| 782 bool weak_key_or_signature_algorithm = false; | 909 bool weak_key_or_signature_algorithm = false; |
| 783 | 910 |
| 784 // Evaluate the results | 911 // Evaluate the results |
| 785 OSStatus cssm_result; | 912 OSStatus cssm_result; |
| 786 switch (trust_result) { | 913 switch (trust_result) { |
| 787 case kSecTrustResultUnspecified: | 914 case kSecTrustResultUnspecified: |
| 788 case kSecTrustResultProceed: | 915 case kSecTrustResultProceed: |
| 789 // Certificate chain is valid and trusted ("unspecified" indicates that | 916 // Certificate chain is valid and trusted ("unspecified" indicates that |
| 790 // the user has not explicitly set a trust setting) | 917 // the user has not explicitly set a trust setting) |
| 791 break; | 918 break; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 829 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE | 956 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE |
| 830 // added to the Status Codes. Don't treat this code as an invalid | 957 // added to the Status Codes. Don't treat this code as an invalid |
| 831 // certificate; instead, map it to a weak key. Any truly invalid | 958 // certificate; instead, map it to a weak key. Any truly invalid |
| 832 // certificates will have the major error (cssm_result) set to | 959 // certificates will have the major error (cssm_result) set to |
| 833 // CSSMERR_TP_INVALID_CERTIFICATE, rather than | 960 // CSSMERR_TP_INVALID_CERTIFICATE, rather than |
| 834 // CSSMERR_TP_VERIFY_ACTION_FAILED. | 961 // CSSMERR_TP_VERIFY_ACTION_FAILED. |
| 835 CertStatus mapped_status = 0; | 962 CertStatus mapped_status = 0; |
| 836 if (policy_failed && | 963 if (policy_failed && |
| 837 chain_info[index].StatusCodes[status_code_index] == | 964 chain_info[index].StatusCodes[status_code_index] == |
| 838 CSSMERR_TP_INVALID_CERTIFICATE) { | 965 CSSMERR_TP_INVALID_CERTIFICATE) { |
| 839 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; | 966 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; |
| 967 weak_key_or_signature_algorithm = true; |
| 968 policy_fail_already_mapped = true; |
| 969 } else if (policy_failed && |
| 970 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) && |
| 971 chain_info[index].StatusCodes[status_code_index] == |
| 972 CSSMERR_TP_VERIFY_ACTION_FAILED && |
| 973 base::mac::IsAtLeastOS10_12()) { |
| 974 // On 10.12, using kSecRevocationRequirePositiveResponse flag |
| 975 // causes a CSSMERR_TP_VERIFY_ACTION_FAILED status if revocation |
| 976 // couldn't be checked. (Note: even if the cert had no |
| 977 // crlDistributionPoints or OCSP AIA.) |
| 978 mapped_status = CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; |
| 979 policy_fail_already_mapped = true; |
| 980 } else { |
| 981 mapped_status = CertStatusFromOSStatus( |
| 982 chain_info[index].StatusCodes[status_code_index]); |
| 983 if (mapped_status == CERT_STATUS_WEAK_KEY) { |
| 840 weak_key_or_signature_algorithm = true; | 984 weak_key_or_signature_algorithm = true; |
| 841 } else { | 985 policy_fail_already_mapped = true; |
| 842 mapped_status = CertStatusFromOSStatus( | 986 } |
| 843 chain_info[index].StatusCodes[status_code_index]); | |
| 844 if (mapped_status == CERT_STATUS_WEAK_KEY) | |
| 845 weak_key_or_signature_algorithm = true; | |
| 846 } | 987 } |
| 847 verify_result->cert_status |= mapped_status; | 988 verify_result->cert_status |= mapped_status; |
| 848 } | 989 } |
| 849 } | 990 } |
| 850 if (policy_failed && !weak_key_or_signature_algorithm) { | 991 if (policy_failed && !policy_fail_already_mapped) { |
| 851 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak | 992 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak |
| 852 // key, map it back to an appropriate error code. | 993 // key or problem checking revocation, map it back to an appropriate |
| 994 // error code. |
| 853 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); | 995 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); |
| 854 } | 996 } |
| 855 if (!IsCertStatusError(verify_result->cert_status)) { | 997 if (!IsCertStatusError(verify_result->cert_status)) { |
| 856 LOG(ERROR) << "cssm_result=" << cssm_result; | 998 LOG(ERROR) << "cssm_result=" << cssm_result; |
| 857 verify_result->cert_status |= CERT_STATUS_INVALID; | 999 verify_result->cert_status |= CERT_STATUS_INVALID; |
| 858 NOTREACHED(); | 1000 NOTREACHED(); |
| 859 } | 1001 } |
| 860 break; | 1002 break; |
| 861 | 1003 |
| 862 default: | 1004 default: |
| (...skipping 21 matching lines...) Expand all Loading... |
| 884 // compatible with WinHTTP, which doesn't report this error (bug 3004). | 1026 // compatible with WinHTTP, which doesn't report this error (bug 3004). |
| 885 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM; | 1027 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM; |
| 886 | 1028 |
| 887 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes); | 1029 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes); |
| 888 verify_result->is_issued_by_known_root = | 1030 verify_result->is_issued_by_known_root = |
| 889 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain); | 1031 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain); |
| 890 | 1032 |
| 891 if (IsCertStatusError(verify_result->cert_status)) | 1033 if (IsCertStatusError(verify_result->cert_status)) |
| 892 return MapCertStatusToNetError(verify_result->cert_status); | 1034 return MapCertStatusToNetError(verify_result->cert_status); |
| 893 | 1035 |
| 894 if (flags & CertVerifier::VERIFY_EV_CERT) { | 1036 return OK; |
| 895 // Determine the certificate's EV status using SecTrustCopyExtendedResult(), | 1037 } |
| 896 // which is an internal/private API function added in OS X 10.5.7. | 1038 |
| 897 // Note: "ExtendedResult" means extended validation results. | 1039 } // namespace |
| 898 CFBundleRef bundle = | 1040 |
| 899 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); | 1041 CertVerifyProcMac::CertVerifyProcMac() {} |
| 900 if (bundle) { | 1042 |
| 901 SecTrustCopyExtendedResultFuncPtr copy_extended_result = | 1043 CertVerifyProcMac::~CertVerifyProcMac() {} |
| 902 reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>( | 1044 |
| 903 CFBundleGetFunctionPointerForName(bundle, | 1045 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { |
| 904 CFSTR("SecTrustCopyExtendedResult"))); | 1046 return false; |
| 905 if (copy_extended_result) { | 1047 } |
| 906 CFDictionaryRef ev_dict_temp = NULL; | 1048 |
| 907 status = copy_extended_result(trust_ref, &ev_dict_temp); | 1049 bool CertVerifyProcMac::SupportsOCSPStapling() const { |
| 908 ScopedCFTypeRef<CFDictionaryRef> ev_dict(ev_dict_temp); | 1050 // TODO(rsleevi): Plumb an OCSP response into the Mac system library. |
| 909 ev_dict_temp = NULL; | 1051 // https://crbug.com/430714 |
| 910 if (status == noErr && ev_dict) { | 1052 return false; |
| 911 // In 10.7.3, SecTrustCopyExtendedResult returns noErr and populates | 1053 } |
| 912 // ev_dict even for non-EV certificates, but only EV certificates | 1054 |
| 913 // will cause ev_dict to contain kSecEVOrganizationName. In previous | 1055 int CertVerifyProcMac::VerifyInternal( |
| 914 // releases, SecTrustCopyExtendedResult would only return noErr and | 1056 X509Certificate* cert, |
| 915 // populate ev_dict for EV certificates, but would always include | 1057 const std::string& hostname, |
| 916 // kSecEVOrganizationName in that case, so checking for this key is | 1058 const std::string& ocsp_response, |
| 917 // appropriate for all known versions of SecTrustCopyExtendedResult. | 1059 int flags, |
| 918 // The actual organization name is unneeded here and can be accessed | 1060 CRLSet* crl_set, |
| 919 // through other means. All that matters here is the OS' conception | 1061 const CertificateList& additional_trust_anchors, |
| 920 // of whether or not the certificate is EV. | 1062 CertVerifyResult* verify_result) { |
| 921 if (CFDictionaryContainsKey(ev_dict, | 1063 // Save the input state of |*verify_result|, which may be needed to re-do |
| 922 kSecEVOrganizationName)) { | 1064 // verification with different flags. |
| 923 verify_result->cert_status |= CERT_STATUS_IS_EV; | 1065 const CertVerifyResult input_verify_result(*verify_result); |
| 924 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) | 1066 |
| 925 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | 1067 // If EV verification is enabled, check for EV policy in leaf cert. |
| 926 } | 1068 std::string candidate_ev_policy_oid; |
| 927 } | 1069 if (flags & CertVerifier::VERIFY_EV_CERT) |
| 928 } | 1070 GetCandidateEVPolicy(cert, &candidate_ev_policy_oid); |
| 1071 |
| 1072 CRLSetResult completed_chain_crl_result; |
| 1073 int rv = VerifyWithGivenFlags(cert, hostname, flags, crl_set, verify_result, |
| 1074 &completed_chain_crl_result); |
| 1075 if (rv != OK) |
| 1076 return rv; |
| 1077 |
| 1078 if (!candidate_ev_policy_oid.empty() && |
| 1079 CheckCertChainEV(verify_result->verified_cert.get(), |
| 1080 candidate_ev_policy_oid)) { |
| 1081 // EV policies check out and the verification succeeded. See if revocation |
| 1082 // checking still needs to be done before it can be marked as EV. |
| 1083 if (completed_chain_crl_result == kCRLSetUnknown && |
| 1084 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) && |
| 1085 !(flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)) { |
| 1086 // If this is an EV cert and it wasn't covered by CRLSets and revocation |
| 1087 // checking wasn't already on, try again with revocation forced on. |
| 1088 // |
| 1089 // Restore the input state of |*verify_result|, so that the |
| 1090 // re-verification starts with a clean slate. |
| 1091 *verify_result = input_verify_result; |
| 1092 int tmp_rv = VerifyWithGivenFlags( |
| 1093 verify_result->verified_cert.get(), hostname, |
| 1094 flags | CertVerifier::VERIFY_REV_CHECKING_ENABLED, crl_set, |
| 1095 verify_result, &completed_chain_crl_result); |
| 1096 // If re-verification failed, return those results without setting EV |
| 1097 // status. |
| 1098 if (tmp_rv != OK) |
| 1099 return tmp_rv; |
| 1100 // Otherwise, fall through and add the EV status flag. |
| 929 } | 1101 } |
| 1102 // EV cert and it was covered by CRLSets or revocation checking passed. |
| 1103 verify_result->cert_status |= CERT_STATUS_IS_EV; |
| 930 } | 1104 } |
| 931 | 1105 |
| 932 return OK; | 1106 return OK; |
| 933 } | 1107 } |
| 934 | 1108 |
| 935 } // namespace net | 1109 } // namespace net |
| 936 | 1110 |
| 937 #pragma clang diagnostic pop // "-Wdeprecated-declarations" | 1111 #pragma clang diagnostic pop // "-Wdeprecated-declarations" |
| OLD | NEW |