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