Chromium Code Reviews| 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 |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 int n = CFArrayGetCount(chain); | 334 int n = CFArrayGetCount(chain); |
| 335 if (n < 1) | 335 if (n < 1) |
| 336 return false; | 336 return false; |
| 337 SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>( | 337 SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>( |
| 338 const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1))); | 338 const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1))); |
| 339 SHA1HashValue hash = X509Certificate::CalculateFingerprint(root_ref); | 339 SHA1HashValue hash = X509Certificate::CalculateFingerprint(root_ref); |
| 340 return IsSHA1HashInSortedArray( | 340 return IsSHA1HashInSortedArray( |
| 341 hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes)); | 341 hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes)); |
| 342 } | 342 } |
| 343 | 343 |
| 344 } // namespace | 344 // Builds and evaluates a SecTrustRef for the certificate chain contained |
| 345 | 345 // in |cert_array|, using the verification policies in |trust_policies|. On |
| 346 CertVerifyProcMac::CertVerifyProcMac() {} | 346 // success, returns OK, and updates |trust_ref|, |trust_result|, |
| 347 | 347 // |verified_chain|, and |chain_info| with the verification results. |
|
wtc
2013/04/29 19:14:57
It would be useful to point out that on failure, t
Ryan Sleevi
2013/04/29 21:28:23
Good point, will do.
| |
| 348 CertVerifyProcMac::~CertVerifyProcMac() {} | 348 // |
| 349 | 349 // Note: An OK return does not mean that |cert_array| is trusted, merely that |
| 350 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { | 350 // verification was performed successfully. |
| 351 return false; | 351 // |
| 352 } | 352 // This function should only be called while the Mac Security Services lock is |
| 353 | 353 // held. |
| 354 int CertVerifyProcMac::VerifyInternal( | 354 int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array, |
| 355 X509Certificate* cert, | 355 CFArrayRef trust_policies, |
| 356 const std::string& hostname, | 356 int flags, |
| 357 int flags, | 357 ScopedCFTypeRef<SecTrustRef>* trust_ref, |
| 358 CRLSet* crl_set, | 358 SecTrustResultType* trust_result, |
| 359 const CertificateList& additional_trust_anchors, | 359 ScopedCFTypeRef<CFArrayRef>* verified_chain, |
| 360 CertVerifyResult* verify_result) { | 360 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) { |
| 361 ScopedCFTypeRef<CFArrayRef> trust_policies; | 361 SecTrustRef tmp_trust = NULL; |
| 362 OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies); | 362 OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies, |
| 363 &tmp_trust); | |
| 363 if (status) | 364 if (status) |
| 364 return NetErrorFromOSStatus(status); | 365 return NetErrorFromOSStatus(status); |
| 365 | 366 ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust); |
| 366 // Create and configure a SecTrustRef, which takes our certificate(s) | |
| 367 // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an | |
| 368 // array of certificates, the first of which is the certificate we're | |
| 369 // verifying, and the subsequent (optional) certificates are used for | |
| 370 // chain building. | |
| 371 ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert()); | |
| 372 | |
| 373 // Serialize all calls that may use the Keychain, to work around various | |
| 374 // issues in OS X 10.6+ with multi-threaded access to Security.framework. | |
| 375 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); | |
| 376 | |
| 377 SecTrustRef trust_ref = NULL; | |
| 378 status = SecTrustCreateWithCertificates(cert_array, trust_policies, | |
| 379 &trust_ref); | |
| 380 if (status) | |
| 381 return NetErrorFromOSStatus(status); | |
| 382 ScopedCFTypeRef<SecTrustRef> scoped_trust_ref(trust_ref); | |
| 383 | 367 |
| 384 if (TestRootCerts::HasInstance()) { | 368 if (TestRootCerts::HasInstance()) { |
| 385 status = TestRootCerts::GetInstance()->FixupSecTrustRef(trust_ref); | 369 status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust); |
| 386 if (status) | 370 if (status) |
| 387 return NetErrorFromOSStatus(status); | 371 return NetErrorFromOSStatus(status); |
| 388 } | 372 } |
| 389 | 373 |
| 390 CSSM_APPLE_TP_ACTION_DATA tp_action_data; | 374 CSSM_APPLE_TP_ACTION_DATA tp_action_data; |
| 391 memset(&tp_action_data, 0, sizeof(tp_action_data)); | 375 memset(&tp_action_data, 0, sizeof(tp_action_data)); |
| 392 tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION; | 376 tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION; |
| 393 // Allow CSSM to download any missing intermediate certificates if an | 377 // Allow CSSM to download any missing intermediate certificates if an |
| 394 // authorityInfoAccess extension or issuerAltName extension is present. | 378 // authorityInfoAccess extension or issuerAltName extension is present. |
| 395 tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET | | 379 tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET | |
| 396 CSSM_TP_ACTION_TRUST_SETTINGS; | 380 CSSM_TP_ACTION_TRUST_SETTINGS; |
| 397 | 381 |
| 398 // Note: For EV certificates, the Apple TP will handle setting these flags | 382 // Note: For EV certificates, the Apple TP will handle setting these flags |
| 399 // as part of EV evaluation. | 383 // as part of EV evaluation. |
| 400 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) { | 384 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) { |
| 401 // Require a positive result from an OCSP responder or a CRL (or both) | 385 // Require a positive result from an OCSP responder or a CRL (or both) |
| 402 // for every certificate in the chain. The Apple TP automatically | 386 // for every certificate in the chain. The Apple TP automatically |
| 403 // excludes the self-signed root from this requirement. If a certificate | 387 // excludes the self-signed root from this requirement. If a certificate |
| 404 // is missing both a crlDistributionPoints extension and an | 388 // is missing both a crlDistributionPoints extension and an |
| 405 // authorityInfoAccess extension with an OCSP responder URL, then we | 389 // authorityInfoAccess extension with an OCSP responder URL, then we |
| 406 // will get a kSecTrustResultRecoverableTrustFailure back from | 390 // will get a kSecTrustResultRecoverableTrustFailure back from |
| 407 // SecTrustEvaluate(), with a | 391 // SecTrustEvaluate(), with a |
| 408 // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case, | 392 // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case, |
| 409 // we'll set our own result to include | 393 // we'll set our own result to include |
| 410 // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are | 394 // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are |
| 411 // present, and a check fails (server unavailable, OCSP retry later, | 395 // present, and a check fails (server unavailable, OCSP retry later, |
| 412 // signature mismatch), then we'll set our own result to include | 396 // signature mismatch), then we'll set our own result to include |
| 413 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION. | 397 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION. |
| 414 tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT; | 398 tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT; |
| 415 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
| 416 | 399 |
| 417 // Note, even if revocation checking is disabled, SecTrustEvaluate() will | 400 // Note, even if revocation checking is disabled, SecTrustEvaluate() will |
| 418 // modify the OCSP options so as to attempt OCSP checking if it believes a | 401 // modify the OCSP options so as to attempt OCSP checking if it believes a |
| 419 // certificate may chain to an EV root. However, because network fetches | 402 // certificate may chain to an EV root. However, because network fetches |
| 420 // are disabled in CreateTrustPolicies() when revocation checking is | 403 // are disabled in CreateTrustPolicies() when revocation checking is |
| 421 // disabled, these will only go against the local cache. | 404 // disabled, these will only go against the local cache. |
| 422 } | 405 } |
| 423 | 406 |
| 424 CFDataRef action_data_ref = | 407 CFDataRef action_data_ref = |
| 425 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, | 408 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, |
| 426 reinterpret_cast<UInt8*>(&tp_action_data), | 409 reinterpret_cast<UInt8*>(&tp_action_data), |
| 427 sizeof(tp_action_data), kCFAllocatorNull); | 410 sizeof(tp_action_data), kCFAllocatorNull); |
| 428 if (!action_data_ref) | 411 if (!action_data_ref) |
| 429 return ERR_OUT_OF_MEMORY; | 412 return ERR_OUT_OF_MEMORY; |
| 430 ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref); | 413 ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref); |
| 431 status = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT, | 414 status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT, |
| 432 action_data_ref); | 415 action_data_ref); |
| 433 if (status) | 416 if (status) |
| 434 return NetErrorFromOSStatus(status); | 417 return NetErrorFromOSStatus(status); |
| 435 | 418 |
| 436 // Verify the certificate. A non-zero result from SecTrustGetResult() | 419 // Verify the certificate. A non-zero result from SecTrustGetResult() |
| 437 // indicates that some fatal error occurred and the chain couldn't be | 420 // indicates that some fatal error occurred and the chain couldn't be |
| 438 // processed, not that the chain contains no errors. We need to examine the | 421 // processed, not that the chain contains no errors. We need to examine the |
| 439 // output of SecTrustGetResult() to determine that. | 422 // output of SecTrustGetResult() to determine that. |
| 440 SecTrustResultType trust_result; | 423 SecTrustResultType tmp_trust_result; |
| 441 status = SecTrustEvaluate(trust_ref, &trust_result); | 424 status = SecTrustEvaluate(tmp_trust, &tmp_trust_result); |
| 442 if (status) | 425 if (status) |
| 443 return NetErrorFromOSStatus(status); | 426 return NetErrorFromOSStatus(status); |
| 444 CFArrayRef completed_chain = NULL; | 427 CFArrayRef tmp_verified_chain = NULL; |
| 445 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info; | 428 CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info; |
| 446 status = SecTrustGetResult(trust_ref, &trust_result, &completed_chain, | 429 status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain, |
| 447 &chain_info); | 430 &tmp_chain_info); |
| 448 if (status) | 431 if (status) |
| 449 return NetErrorFromOSStatus(status); | 432 return NetErrorFromOSStatus(status); |
| 450 ScopedCFTypeRef<CFArrayRef> scoped_completed_chain(completed_chain); | 433 |
| 434 trust_ref->swap(scoped_tmp_trust); | |
| 435 *trust_result = tmp_trust_result; | |
| 436 verified_chain->reset(tmp_verified_chain); | |
| 437 *chain_info = tmp_chain_info; | |
| 438 | |
| 439 return OK; | |
| 440 } | |
| 441 | |
| 442 // OS X ships with both "GTE CyberTrust Global Root" and "Baltimore CyberTrust | |
| 443 // Root" as part of its trusted root store. However, a cross-certified version | |
| 444 // of the "Baltimore CyberTrust Root" exists that chains to "GTE CyberTrust | |
| 445 // Global Root". When OS X/Security.framework attempts to evaluate such a | |
| 446 // certificate chain, it disregards the "Baltimore CyberTrust Root" that exists | |
| 447 // within Keychain and instead attempts to terminate the chain in the "GTE | |
| 448 // CyberTrust Global Root". However, the GTE root is scheduled to be removed in | |
| 449 // a future OS X update (for sunsetting purposes), and once removed, such | |
| 450 // chains will fail validation, even though a trust anchor still exists. | |
| 451 // | |
| 452 // Rather than over-generalizing a solution that may mask a number of TLS | |
| 453 // misconfigurations, attempt to specifically match the affected | |
| 454 // cross-certified certificate and remove it from certificate chain processing. | |
| 455 bool IsBadBaltimoreGTECertificate(SecCertificateRef cert) { | |
| 456 // Matches the GTE-signed Baltimore CyberTrust Root | |
| 457 // https://cacert.omniroot.com/Baltimore-to-GTE-04-12.pem | |
| 458 static const SHA1HashValue kBadBaltimoreHashNew = | |
| 459 { { 0x4D, 0x34, 0xEA, 0x92, 0x76, 0x4B, 0x3A, 0x31, 0x49, 0x11, | |
| 460 0x99, 0x52, 0xF4, 0x19, 0x30, 0xCA, 0x11, 0x34, 0x83, 0x61 } }; | |
| 461 // Matches the legacy GTE-signed Baltimore CyberTrust Root | |
| 462 // https://cacert.omniroot.com/gte-2-2025.pem | |
| 463 static const SHA1HashValue kBadBaltimoreHashOld = | |
| 464 { { 0x54, 0xD8, 0xCB, 0x49, 0x1F, 0xA1, 0x6D, 0xF8, 0x87, 0xDC, | |
| 465 0x94, 0xA9, 0x34, 0xCC, 0x83, 0x6B, 0xDA, 0xA8, 0xA3, 0x69 } }; | |
| 466 | |
| 467 SHA1HashValue fingerprint = X509Certificate::CalculateFingerprint(cert); | |
| 468 | |
| 469 return fingerprint.Equals(kBadBaltimoreHashNew) || | |
| 470 fingerprint.Equals(kBadBaltimoreHashOld); | |
| 471 } | |
| 472 | |
| 473 // Attempts to re-verify |cert_array| after adjusting the inputs to work around | |
| 474 // known issues in OS X. To be used if BuildAndEvaluateSecTrustRef fails to | |
| 475 // return a positive result for verification. | |
| 476 // | |
| 477 // This function should only be called while the Mac Security Services lock is | |
| 478 // held. | |
| 479 void RetrySecTrustEvaluateWithAdjustedChain( | |
| 480 CFArrayRef cert_array, | |
| 481 CFArrayRef trust_policies, | |
| 482 int flags, | |
| 483 ScopedCFTypeRef<SecTrustRef>* trust_ref, | |
| 484 SecTrustResultType* trust_result, | |
| 485 ScopedCFTypeRef<CFArrayRef>* verified_chain, | |
| 486 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) { | |
| 487 CFIndex count = CFArrayGetCount(*verified_chain); | |
| 488 CFIndex slice_point = 0; | |
| 489 | |
| 490 for (CFIndex i = 1; i < count; ++i) { | |
| 491 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( | |
| 492 const_cast<void*>(CFArrayGetValueAtIndex(*verified_chain, i))); | |
| 493 if (cert == NULL) | |
| 494 return; // Strange times; can't fix things up. | |
| 495 | |
| 496 if (IsBadBaltimoreGTECertificate(cert)) { | |
| 497 slice_point = i; | |
| 498 break; | |
| 499 } | |
| 500 } | |
| 501 if (slice_point == 0) | |
| 502 return; // Nothing to do. | |
| 503 | |
| 504 ScopedCFTypeRef<CFMutableArrayRef> adjusted_cert_array( | |
| 505 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); | |
| 506 // Note: This excludes the certificate at |slice_point|. | |
| 507 CFArrayAppendArray(adjusted_cert_array, cert_array, | |
| 508 CFRangeMake(0, slice_point)); | |
|
wtc
2013/04/29 19:14:57
Another option is to only remove the cross-signed
Ryan Sleevi
2013/04/29 21:28:23
Well, the only cert(s) that should follow slice_po
wtc
2013/04/29 22:53:42
You are right. I didn't think this through. I thou
| |
| 509 | |
| 510 // Ignore the result; failure will preserve the old verification results. | |
| 511 BuildAndEvaluateSecTrustRef( | |
| 512 adjusted_cert_array, trust_policies, flags, trust_ref, trust_result, | |
| 513 verified_chain, chain_info); | |
| 514 } | |
| 515 | |
| 516 } // namespace | |
| 517 | |
| 518 CertVerifyProcMac::CertVerifyProcMac() {} | |
| 519 | |
| 520 CertVerifyProcMac::~CertVerifyProcMac() {} | |
| 521 | |
| 522 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { | |
| 523 return false; | |
| 524 } | |
| 525 | |
| 526 int CertVerifyProcMac::VerifyInternal( | |
| 527 X509Certificate* cert, | |
| 528 const std::string& hostname, | |
| 529 int flags, | |
| 530 CRLSet* crl_set, | |
| 531 const CertificateList& additional_trust_anchors, | |
| 532 CertVerifyResult* verify_result) { | |
| 533 ScopedCFTypeRef<CFArrayRef> trust_policies; | |
| 534 OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies); | |
| 535 if (status) | |
| 536 return NetErrorFromOSStatus(status); | |
| 537 | |
| 538 // Create and configure a SecTrustRef, which takes our certificate(s) | |
| 539 // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an | |
| 540 // array of certificates, the first of which is the certificate we're | |
| 541 // verifying, and the subsequent (optional) certificates are used for | |
| 542 // chain building. | |
| 543 ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert()); | |
| 544 | |
| 545 // Serialize all calls that may use the Keychain, to work around various | |
| 546 // issues in OS X 10.6+ with multi-threaded access to Security.framework. | |
| 547 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); | |
| 548 | |
| 549 ScopedCFTypeRef<SecTrustRef> trust_ref; | |
| 550 SecTrustResultType trust_result = kSecTrustResultDeny; | |
| 551 ScopedCFTypeRef<CFArrayRef> completed_chain; | |
| 552 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL; | |
| 553 | |
| 554 int rv = BuildAndEvaluateSecTrustRef( | |
| 555 cert_array, trust_policies, flags, &trust_ref, &trust_result, | |
| 556 &completed_chain, &chain_info); | |
| 557 if (rv != OK) | |
| 558 return rv; | |
| 559 if (trust_result != kSecTrustResultUnspecified && | |
| 560 trust_result != kSecTrustResultProceed) { | |
| 561 RetrySecTrustEvaluateWithAdjustedChain( | |
| 562 cert_array, trust_policies, flags, &trust_ref, &trust_result, | |
| 563 &completed_chain, &chain_info); | |
| 564 } | |
| 565 | |
| 566 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) | |
| 567 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
| 451 | 568 |
| 452 if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set)) | 569 if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set)) |
| 453 verify_result->cert_status |= CERT_STATUS_REVOKED; | 570 verify_result->cert_status |= CERT_STATUS_REVOKED; |
| 454 | 571 |
| 455 GetCertChainInfo(scoped_completed_chain.get(), chain_info, verify_result); | 572 GetCertChainInfo(completed_chain, chain_info, verify_result); |
| 456 | 573 |
| 457 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits | 574 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits |
| 458 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds | 575 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds |
| 459 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping | 576 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping |
| 460 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only | 577 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only |
| 461 // error was due to an unsupported key size. | 578 // error was due to an unsupported key size. |
| 462 bool policy_failed = false; | 579 bool policy_failed = false; |
| 463 bool weak_key = false; | 580 bool weak_key = false; |
| 464 | 581 |
| 465 // Evaluate the results | 582 // Evaluate the results |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 588 } | 705 } |
| 589 } | 706 } |
| 590 } | 707 } |
| 591 } | 708 } |
| 592 } | 709 } |
| 593 | 710 |
| 594 return OK; | 711 return OK; |
| 595 } | 712 } |
| 596 | 713 |
| 597 } // namespace net | 714 } // namespace net |
| OLD | NEW |