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

Unified Diff: net/cert/cert_verify_proc_mac.cc

Issue 2101303005: CertVerifyProcMac: Add Keychain re-ordering hack, check CRLsets in path pruning loop. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review changes and url_request_unittest fixes Created 4 years, 6 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 side-by-side diff with in-line comments
Download patch
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 07a49a234b0103594aa4673eb3a7506c0e9eaa53..abbae63d07716ad549191464f695b9128eeba64d 100644
--- a/net/cert/cert_verify_proc_mac.cc
+++ b/net/cert/cert_verify_proc_mac.cc
@@ -28,6 +28,7 @@
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/crl_set.h"
+#include "net/cert/test_keychain_search_list_mac.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util_mac.h"
@@ -148,21 +149,19 @@ CertStatus CertStatusFromOSStatus(OSStatus status) {
}
// Creates a series of SecPolicyRefs to be added to a SecTrustRef used to
-// validate a certificate for an SSL server. |hostname| contains the name of
-// the SSL server that the certificate should be verified against. |flags| is
-// a bitwise-OR of VerifyFlags that can further alter how trust is validated,
-// such as how revocation is checked. If successful, returns noErr, and
-// stores the resultant array of SecPolicyRefs in |policies|.
-OSStatus CreateTrustPolicies(const std::string& hostname,
- int flags,
- ScopedCFTypeRef<CFArrayRef>* policies) {
+// validate a certificate for an SSL server. |flags| is a bitwise-OR of
+// VerifyFlags that can further alter how trust is validated, such as how
+// revocation is checked. If successful, returns noErr, and stores the
+// resultant array of SecPolicyRefs in |policies|.
+OSStatus CreateTrustPolicies(int flags, ScopedCFTypeRef<CFArrayRef>* policies) {
ScopedCFTypeRef<CFMutableArrayRef> local_policies(
CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
if (!local_policies)
return memFullErr;
SecPolicyRef ssl_policy;
- OSStatus status = x509_util::CreateSSLServerPolicy(hostname, &ssl_policy);
+ OSStatus status =
+ x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy);
if (status)
return status;
CFArrayAppendValue(local_policies, ssl_policy);
@@ -377,6 +376,7 @@ bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) {
int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
CFArrayRef trust_policies,
int flags,
+ CFArrayRef keychain_search_list,
ScopedCFTypeRef<SecTrustRef>* trust_ref,
SecTrustResultType* trust_result,
ScopedCFTypeRef<CFArrayRef>* verified_chain,
@@ -394,6 +394,12 @@ int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
return NetErrorFromOSStatus(status);
}
+ if (keychain_search_list) {
+ status = SecTrustSetKeychains(tmp_trust, keychain_search_list);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ }
+
CSSM_APPLE_TP_ACTION_DATA tp_action_data;
memset(&tp_action_data, 0, sizeof(tp_action_data));
tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
@@ -540,18 +546,10 @@ int CertVerifyProcMac::VerifyInternal(
const CertificateList& additional_trust_anchors,
CertVerifyResult* verify_result) {
ScopedCFTypeRef<CFArrayRef> trust_policies;
- OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
+ OSStatus status = CreateTrustPolicies(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<CFMutableArrayRef> 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());
@@ -562,6 +560,7 @@ int CertVerifyProcMac::VerifyInternal(
CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
bool candidate_untrusted = true;
bool candidate_weak = false;
+ bool completed_chain_revoked = false;
// OS X lacks proper path discovery; it will take the input certs and never
// backtrack the graph attempting to discover valid paths.
@@ -597,6 +596,10 @@ int CertVerifyProcMac::VerifyInternal(
// present in the AIA of A is signed using a strong algorithm. Since a
// 'strong' chain exists, it would be desirable to prefer this chain.
//
+ // - A user keychain may contain a less desirable intermediate or root.
+ // OS X gives the user keychains higher priority than the system keychain,
+ // so it may build a weak chain.
+ //
// Because of this, the code below first attempts to validate the peer's
// identity using the supplied chain. If it is not trusted (e.g. the OS only
// trusts C, but the version of C signed by D was sent, and D is not trusted),
@@ -605,73 +608,129 @@ int CertVerifyProcMac::VerifyInternal(
// chain is found, it is used, otherwise, the algorithm continues until only
// the peer's certificate remains.
//
+ // If the loop does not find a trusted chain, the loop will be repeated with
+ // the keychain search order altered to give priority to the System Roots
+ // keychain.
+ //
// This does cause a performance hit for these users, but only in cases where
// OS X is building weaker chains than desired, or when it would otherwise
// fail the connection.
- while (CFArrayGetCount(cert_array) > 0) {
- ScopedCFTypeRef<SecTrustRef> temp_ref;
- SecTrustResultType temp_trust_result = kSecTrustResultDeny;
- ScopedCFTypeRef<CFArrayRef> temp_chain;
- CSSM_TP_APPLE_EVIDENCE_INFO* temp_chain_info = NULL;
-
- int rv = BuildAndEvaluateSecTrustRef(cert_array, trust_policies, flags,
- &temp_ref, &temp_trust_result,
- &temp_chain, &temp_chain_info);
- if (rv != OK)
- return rv;
-
- bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
- temp_trust_result != kSecTrustResultProceed);
- bool weak_chain = false;
- if (CFArrayGetCount(temp_chain) == 0) {
- // If the chain is empty, it cannot be trusted or have recoverable
- // errors.
- DCHECK(untrusted);
- DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result);
- } else {
- CertVerifyResult temp_verify_result;
- bool leaf_is_weak = false;
- GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result,
- &leaf_is_weak);
- weak_chain = !leaf_is_weak &&
- (temp_verify_result.has_md2 || temp_verify_result.has_md4 ||
- temp_verify_result.has_md5 || temp_verify_result.has_sha1);
+ for (bool try_reordered_keychain : {false, true}) {
Ryan Sleevi 2016/08/12 19:50:17 NICE! :)
+ ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
+ if (TestKeychainSearchList::HasInstance()) {
+ CFArrayRef keychain_search_list;
+ status = TestKeychainSearchList::GetInstance()->CopySearchList(
+ &keychain_search_list);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ scoped_alternate_keychain_search_list.reset(keychain_search_list);
}
Ryan Sleevi 2016/08/12 19:50:17 Could you add some additional documentation here e
mattm 2016/08/16 01:41:34 Done.
- // Set the result to the current chain if:
- // - This is the first verification attempt. This ensures that if
- // everything is awful (e.g. it may just be an untrusted cert), that
- // what is reported is exactly what was sent by the server
- // - If the current chain is trusted, and the old chain was not trusted,
- // then prefer this chain. This ensures that if there is at least a
- // valid path to a trust anchor, it's preferred over reporting an error.
- // - If the current chain is trusted, and the old chain is trusted, but
- // the old chain contained weak algorithms while the current chain only
- // contains strong algorithms, then prefer the current chain over the
- // old chain.
- //
- // Note: If the leaf certificate itself is weak, then the only
- // consideration is whether or not there is a trusted chain. That's
- // because no amount of path discovery will fix a weak leaf.
- if (!trust_ref || (!untrusted && (candidate_untrusted ||
- (candidate_weak && !weak_chain)))) {
- trust_ref = temp_ref;
- trust_result = temp_trust_result;
- completed_chain = temp_chain;
- chain_info = temp_chain_info;
-
- candidate_untrusted = untrusted;
- candidate_weak = weak_chain;
+ if (try_reordered_keychain) {
+ if (!scoped_alternate_keychain_search_list) {
+ CFArrayRef keychain_search_list;
+ status = SecKeychainCopySearchList(&keychain_search_list);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ scoped_alternate_keychain_search_list.reset(keychain_search_list);
+ }
+ CFMutableArrayRef mutable_keychain_search_list = CFArrayCreateMutableCopy(
+ kCFAllocatorDefault,
+ CFArrayGetCount(scoped_alternate_keychain_search_list.get()) + 1,
+ scoped_alternate_keychain_search_list.get());
+ if (!mutable_keychain_search_list)
+ return ERR_OUT_OF_MEMORY;
+ scoped_alternate_keychain_search_list.reset(mutable_keychain_search_list);
+
+ SecKeychainRef keychain;
+ // Get a reference to the System Roots keychain. This is a gross hack,
+ // but the path is known to be valid on OS X 10.9-10.11.
+ status = SecKeychainOpen(
+ "/System/Library/Keychains/SystemRootCertificates.keychain",
+ &keychain);
Ryan Sleevi 2016/08/12 19:50:17 DESIGN: Is it worth adding a unit test for this ca
mattm 2016/08/16 01:41:34 Done.
+ if (status)
+ return NetErrorFromOSStatus(status);
+ ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
+
+ CFArrayInsertValueAtIndex(mutable_keychain_search_list, 0, keychain);
+ }
+
+ ScopedCFTypeRef<CFMutableArrayRef> cert_array(
+ cert->CreateOSCertChainForCert());
+
+ while (CFArrayGetCount(cert_array) > 0) {
Ryan Sleevi 2016/08/12 19:50:17 I'm a little nervous that this is moved far away f
mattm 2016/08/16 01:41:34 Done.
+ ScopedCFTypeRef<SecTrustRef> temp_ref;
+ SecTrustResultType temp_trust_result = kSecTrustResultDeny;
+ ScopedCFTypeRef<CFArrayRef> temp_chain;
+ CSSM_TP_APPLE_EVIDENCE_INFO* temp_chain_info = NULL;
+
+ int rv = BuildAndEvaluateSecTrustRef(
+ cert_array, trust_policies, flags,
+ scoped_alternate_keychain_search_list.get(), &temp_ref,
+ &temp_trust_result, &temp_chain, &temp_chain_info);
+ if (rv != OK)
+ return rv;
+
+ bool revoked =
+ (crl_set && !CheckRevocationWithCRLSet(temp_chain, crl_set));
Ryan Sleevi 2016/08/12 19:50:17 I suspect we should do with some documentation her
mattm 2016/08/16 01:41:34 That would be great since I'm not sure what the nu
Ryan Sleevi 2016/08/17 02:28:12 // Check to see if the path |temp_chain| has been
mattm 2016/08/17 03:50:57 Ah, that. Done.
+ bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
+ temp_trust_result != kSecTrustResultProceed) ||
+ revoked;
+ bool weak_chain = false;
+ if (CFArrayGetCount(temp_chain) == 0) {
+ // If the chain is empty, it cannot be trusted or have recoverable
+ // errors.
+ DCHECK(untrusted);
+ DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result);
+ } else {
+ CertVerifyResult temp_verify_result;
+ bool leaf_is_weak = false;
+ GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result,
+ &leaf_is_weak);
+ weak_chain =
+ !leaf_is_weak &&
+ (temp_verify_result.has_md2 || temp_verify_result.has_md4 ||
+ temp_verify_result.has_md5 || temp_verify_result.has_sha1);
+ }
+ // Set the result to the current chain if:
+ // - This is the first verification attempt. This ensures that if
+ // everything is awful (e.g. it may just be an untrusted cert), that
+ // what is reported is exactly what was sent by the server
+ // - If the current chain is trusted, and the old chain was not trusted,
+ // then prefer this chain. This ensures that if there is at least a
+ // valid path to a trust anchor, it's preferred over reporting an error.
+ // - If the current chain is trusted, and the old chain is trusted, but
+ // the old chain contained weak algorithms while the current chain only
+ // contains strong algorithms, then prefer the current chain over the
+ // old chain.
+ //
+ // Note: If the leaf certificate itself is weak, then the only
+ // consideration is whether or not there is a trusted chain. That's
+ // because no amount of path discovery will fix a weak leaf.
+ if (!trust_ref || (!untrusted && (candidate_untrusted ||
+ (candidate_weak && !weak_chain)))) {
+ trust_ref = temp_ref;
+ trust_result = temp_trust_result;
+ completed_chain = temp_chain;
+ completed_chain_revoked = revoked;
+ chain_info = temp_chain_info;
+
+ candidate_untrusted = untrusted;
+ candidate_weak = weak_chain;
+ }
+ // Short-circuit when a current, trusted chain is found.
+ if (!untrusted && !weak_chain)
+ break;
+ CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1);
}
// Short-circuit when a current, trusted chain is found.
- if (!untrusted && !weak_chain)
+ if (!candidate_untrusted && !candidate_weak)
break;
- CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1);
}
if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
- if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set))
+ if (completed_chain_revoked)
verify_result->cert_status |= CERT_STATUS_REVOKED;
if (CFArrayGetCount(completed_chain) > 0) {

Powered by Google App Engine
This is Rietveld 408576698