Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(876)

Side by Side Diff: net/cert/cert_verify_proc_mac.cc

Issue 14492003: Work around GTE CyberTrust/Baltimore CyberTrust cross-signing issues (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: With unittests Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | net/cert/cert_verify_proc_unittest.cc » ('j') | net/cert/test_root_certs_mac.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « no previous file | net/cert/cert_verify_proc_unittest.cc » ('j') | net/cert/test_root_certs_mac.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698