| Index: net/cert/cert_verify_proc_mac.cc
|
| diff --git a/net/cert/cert_verify_proc_mac.cc b/net/cert/cert_verify_proc_mac.cc
|
| index 77346df3b7973e148ecdf1bcd5fd893cc75e2fc7..da284d43c28c1166e8e130fcd3bc2e0bb695dd88 100644
|
| --- a/net/cert/cert_verify_proc_mac.cc
|
| +++ b/net/cert/cert_verify_proc_mac.cc
|
| @@ -345,48 +345,33 @@ bool IsIssuedByKnownRoot(CFArrayRef chain) {
|
| hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
|
| }
|
|
|
| -} // namespace
|
| -
|
| -CertVerifyProcMac::CertVerifyProcMac() {}
|
| -
|
| -CertVerifyProcMac::~CertVerifyProcMac() {}
|
| -
|
| -bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
|
| - return false;
|
| -}
|
| -
|
| -int CertVerifyProcMac::VerifyInternal(
|
| - X509Certificate* cert,
|
| - const std::string& hostname,
|
| - int flags,
|
| - CRLSet* crl_set,
|
| - const CertificateList& additional_trust_anchors,
|
| - CertVerifyResult* verify_result) {
|
| - ScopedCFTypeRef<CFArrayRef> trust_policies;
|
| - OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
|
| +// Builds and evaluates a SecTrustRef for the certificate chain contained
|
| +// in |cert_array|, using the verification policies in |trust_policies|. On
|
| +// success, returns OK, and updates |trust_ref|, |trust_result|,
|
| +// |verified_chain|, and |chain_info| with the verification results. On
|
| +// failure, no output parameters are modified.
|
| +//
|
| +// Note: An OK return does not mean that |cert_array| is trusted, merely that
|
| +// verification was performed successfully.
|
| +//
|
| +// This function should only be called while the Mac Security Services lock is
|
| +// held.
|
| +int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
|
| + CFArrayRef trust_policies,
|
| + int flags,
|
| + ScopedCFTypeRef<SecTrustRef>* trust_ref,
|
| + SecTrustResultType* trust_result,
|
| + ScopedCFTypeRef<CFArrayRef>* verified_chain,
|
| + CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
|
| + SecTrustRef tmp_trust = NULL;
|
| + OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies,
|
| + &tmp_trust);
|
| if (status)
|
| return NetErrorFromOSStatus(status);
|
| -
|
| - // Create and configure a SecTrustRef, which takes our certificate(s)
|
| - // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
|
| - // array of certificates, the first of which is the certificate we're
|
| - // verifying, and the subsequent (optional) certificates are used for
|
| - // chain building.
|
| - ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert());
|
| -
|
| - // Serialize all calls that may use the Keychain, to work around various
|
| - // issues in OS X 10.6+ with multi-threaded access to Security.framework.
|
| - base::AutoLock lock(crypto::GetMacSecurityServicesLock());
|
| -
|
| - SecTrustRef trust_ref = NULL;
|
| - status = SecTrustCreateWithCertificates(cert_array, trust_policies,
|
| - &trust_ref);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| - ScopedCFTypeRef<SecTrustRef> scoped_trust_ref(trust_ref);
|
| + ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust);
|
|
|
| if (TestRootCerts::HasInstance()) {
|
| - status = TestRootCerts::GetInstance()->FixupSecTrustRef(trust_ref);
|
| + status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust);
|
| if (status)
|
| return NetErrorFromOSStatus(status);
|
| }
|
| @@ -416,7 +401,6 @@ int CertVerifyProcMac::VerifyInternal(
|
| // signature mismatch), then we'll set our own result to include
|
| // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
|
| tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
|
| - verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
|
|
|
| // Note, even if revocation checking is disabled, SecTrustEvaluate() will
|
| // modify the OCSP options so as to attempt OCSP checking if it believes a
|
| @@ -432,7 +416,7 @@ int CertVerifyProcMac::VerifyInternal(
|
| if (!action_data_ref)
|
| return ERR_OUT_OF_MEMORY;
|
| ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref);
|
| - status = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT,
|
| + status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT,
|
| action_data_ref);
|
| if (status)
|
| return NetErrorFromOSStatus(status);
|
| @@ -441,22 +425,156 @@ int CertVerifyProcMac::VerifyInternal(
|
| // indicates that some fatal error occurred and the chain couldn't be
|
| // processed, not that the chain contains no errors. We need to examine the
|
| // output of SecTrustGetResult() to determine that.
|
| - SecTrustResultType trust_result;
|
| - status = SecTrustEvaluate(trust_ref, &trust_result);
|
| + SecTrustResultType tmp_trust_result;
|
| + status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
|
| if (status)
|
| return NetErrorFromOSStatus(status);
|
| - CFArrayRef completed_chain = NULL;
|
| - CSSM_TP_APPLE_EVIDENCE_INFO* chain_info;
|
| - status = SecTrustGetResult(trust_ref, &trust_result, &completed_chain,
|
| - &chain_info);
|
| + CFArrayRef tmp_verified_chain = NULL;
|
| + CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info;
|
| + status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain,
|
| + &tmp_chain_info);
|
| if (status)
|
| return NetErrorFromOSStatus(status);
|
| - ScopedCFTypeRef<CFArrayRef> scoped_completed_chain(completed_chain);
|
| +
|
| + trust_ref->swap(scoped_tmp_trust);
|
| + *trust_result = tmp_trust_result;
|
| + verified_chain->reset(tmp_verified_chain);
|
| + *chain_info = tmp_chain_info;
|
| +
|
| + return OK;
|
| +}
|
| +
|
| +// OS X ships with both "GTE CyberTrust Global Root" and "Baltimore CyberTrust
|
| +// Root" as part of its trusted root store. However, a cross-certified version
|
| +// of the "Baltimore CyberTrust Root" exists that chains to "GTE CyberTrust
|
| +// Global Root". When OS X/Security.framework attempts to evaluate such a
|
| +// certificate chain, it disregards the "Baltimore CyberTrust Root" that exists
|
| +// within Keychain and instead attempts to terminate the chain in the "GTE
|
| +// CyberTrust Global Root". However, the GTE root is scheduled to be removed in
|
| +// a future OS X update (for sunsetting purposes), and once removed, such
|
| +// chains will fail validation, even though a trust anchor still exists.
|
| +//
|
| +// Rather than over-generalizing a solution that may mask a number of TLS
|
| +// misconfigurations, attempt to specifically match the affected
|
| +// cross-certified certificate and remove it from certificate chain processing.
|
| +bool IsBadBaltimoreGTECertificate(SecCertificateRef cert) {
|
| + // Matches the GTE-signed Baltimore CyberTrust Root
|
| + // https://cacert.omniroot.com/Baltimore-to-GTE-04-12.pem
|
| + static const SHA1HashValue kBadBaltimoreHashNew =
|
| + { { 0x4D, 0x34, 0xEA, 0x92, 0x76, 0x4B, 0x3A, 0x31, 0x49, 0x11,
|
| + 0x99, 0x52, 0xF4, 0x19, 0x30, 0xCA, 0x11, 0x34, 0x83, 0x61 } };
|
| + // Matches the legacy GTE-signed Baltimore CyberTrust Root
|
| + // https://cacert.omniroot.com/gte-2-2025.pem
|
| + static const SHA1HashValue kBadBaltimoreHashOld =
|
| + { { 0x54, 0xD8, 0xCB, 0x49, 0x1F, 0xA1, 0x6D, 0xF8, 0x87, 0xDC,
|
| + 0x94, 0xA9, 0x34, 0xCC, 0x83, 0x6B, 0xDA, 0xA8, 0xA3, 0x69 } };
|
| +
|
| + SHA1HashValue fingerprint = X509Certificate::CalculateFingerprint(cert);
|
| +
|
| + return fingerprint.Equals(kBadBaltimoreHashNew) ||
|
| + fingerprint.Equals(kBadBaltimoreHashOld);
|
| +}
|
| +
|
| +// Attempts to re-verify |cert_array| after adjusting the inputs to work around
|
| +// known issues in OS X. To be used if BuildAndEvaluateSecTrustRef fails to
|
| +// return a positive result for verification.
|
| +//
|
| +// This function should only be called while the Mac Security Services lock is
|
| +// held.
|
| +void RetrySecTrustEvaluateWithAdjustedChain(
|
| + CFArrayRef cert_array,
|
| + CFArrayRef trust_policies,
|
| + int flags,
|
| + ScopedCFTypeRef<SecTrustRef>* trust_ref,
|
| + SecTrustResultType* trust_result,
|
| + ScopedCFTypeRef<CFArrayRef>* verified_chain,
|
| + CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
|
| + CFIndex count = CFArrayGetCount(*verified_chain);
|
| + CFIndex slice_point = 0;
|
| +
|
| + for (CFIndex i = 1; i < count; ++i) {
|
| + SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
|
| + const_cast<void*>(CFArrayGetValueAtIndex(*verified_chain, i)));
|
| + if (cert == NULL)
|
| + return; // Strange times; can't fix things up.
|
| +
|
| + if (IsBadBaltimoreGTECertificate(cert)) {
|
| + slice_point = i;
|
| + break;
|
| + }
|
| + }
|
| + if (slice_point == 0)
|
| + return; // Nothing to do.
|
| +
|
| + ScopedCFTypeRef<CFMutableArrayRef> adjusted_cert_array(
|
| + CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
|
| + // Note: This excludes the certificate at |slice_point|.
|
| + CFArrayAppendArray(adjusted_cert_array, cert_array,
|
| + CFRangeMake(0, slice_point));
|
| +
|
| + // Ignore the result; failure will preserve the old verification results.
|
| + BuildAndEvaluateSecTrustRef(
|
| + adjusted_cert_array, trust_policies, flags, trust_ref, trust_result,
|
| + verified_chain, chain_info);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +CertVerifyProcMac::CertVerifyProcMac() {}
|
| +
|
| +CertVerifyProcMac::~CertVerifyProcMac() {}
|
| +
|
| +bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
|
| + return false;
|
| +}
|
| +
|
| +int CertVerifyProcMac::VerifyInternal(
|
| + X509Certificate* cert,
|
| + const std::string& hostname,
|
| + int flags,
|
| + CRLSet* crl_set,
|
| + const CertificateList& additional_trust_anchors,
|
| + CertVerifyResult* verify_result) {
|
| + ScopedCFTypeRef<CFArrayRef> trust_policies;
|
| + OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
|
| + if (status)
|
| + return NetErrorFromOSStatus(status);
|
| +
|
| + // Create and configure a SecTrustRef, which takes our certificate(s)
|
| + // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
|
| + // array of certificates, the first of which is the certificate we're
|
| + // verifying, and the subsequent (optional) certificates are used for
|
| + // chain building.
|
| + ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert());
|
| +
|
| + // Serialize all calls that may use the Keychain, to work around various
|
| + // issues in OS X 10.6+ with multi-threaded access to Security.framework.
|
| + base::AutoLock lock(crypto::GetMacSecurityServicesLock());
|
| +
|
| + ScopedCFTypeRef<SecTrustRef> trust_ref;
|
| + SecTrustResultType trust_result = kSecTrustResultDeny;
|
| + ScopedCFTypeRef<CFArrayRef> completed_chain;
|
| + CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
|
| +
|
| + int rv = BuildAndEvaluateSecTrustRef(
|
| + cert_array, trust_policies, flags, &trust_ref, &trust_result,
|
| + &completed_chain, &chain_info);
|
| + if (rv != OK)
|
| + return rv;
|
| + if (trust_result != kSecTrustResultUnspecified &&
|
| + trust_result != kSecTrustResultProceed) {
|
| + RetrySecTrustEvaluateWithAdjustedChain(
|
| + cert_array, trust_policies, flags, &trust_ref, &trust_result,
|
| + &completed_chain, &chain_info);
|
| + }
|
| +
|
| + if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
|
| + verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
|
|
|
| if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set))
|
| verify_result->cert_status |= CERT_STATUS_REVOKED;
|
|
|
| - GetCertChainInfo(scoped_completed_chain.get(), chain_info, verify_result);
|
| + GetCertChainInfo(completed_chain, chain_info, verify_result);
|
|
|
| // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits
|
| // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds
|
|
|