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

Unified Diff: net/base/x509_certificate_mac.cc

Issue 3112013: Move chain building/verification out of X509Certificate (Closed)
Patch Set: Rebase to trunk - Without OpenSSL fixes Created 10 years, 2 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/base/x509_certificate_mac.cc
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 2dbdb753c14767d0e05da3a2fbbf4be476dc9f4f..da7813f1780fb58013f60a43bb1d36c214154a19 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -12,163 +12,12 @@
#include "base/pickle.h"
#include "base/scoped_cftyperef.h"
#include "base/sys_string_conversions.h"
-#include "net/base/cert_status_flags.h"
-#include "net/base/cert_verify_result.h"
-#include "net/base/net_errors.h"
using base::Time;
namespace net {
-class MacTrustedCertificates {
- public:
- // Sets the trusted root certificate used by tests. Call with |cert| set
- // to NULL to clear the test certificate.
- void SetTestCertificate(X509Certificate* cert) {
- AutoLock lock(lock_);
- test_certificate_ = cert;
- }
-
- // Returns an array containing the trusted certificates for use with
- // SecTrustSetAnchorCertificates(). Returns NULL if the system-supplied
- // list of trust anchors is acceptable (that is, there is not test
- // certificate available). Ownership follows the Create Rule (caller
- // is responsible for calling CFRelease on the non-NULL result).
- CFArrayRef CopyTrustedCertificateArray() {
- AutoLock lock(lock_);
-
- if (!test_certificate_)
- return NULL;
-
- // Failure to copy the anchor certificates or add the test certificate
- // is non-fatal; SecTrustEvaluate() will use the system anchors instead.
- CFArrayRef anchor_array;
- OSStatus status = SecTrustCopyAnchorCertificates(&anchor_array);
- if (status)
- return NULL;
- scoped_cftyperef<CFArrayRef> scoped_anchor_array(anchor_array);
- CFMutableArrayRef merged_array = CFArrayCreateMutableCopy(
- kCFAllocatorDefault, 0, anchor_array);
- if (!merged_array)
- return NULL;
- CFArrayAppendValue(merged_array, test_certificate_->os_cert_handle());
-
- return merged_array;
- }
- private:
- friend struct DefaultSingletonTraits<MacTrustedCertificates>;
-
- // Obtain an instance of MacTrustedCertificates via the singleton
- // interface.
- MacTrustedCertificates() : test_certificate_(NULL) { }
-
- // An X509Certificate object that may be appended to the list of
- // system trusted anchors.
- scoped_refptr<X509Certificate> test_certificate_;
-
- // The trusted cache may be accessed from multiple threads.
- mutable Lock lock_;
-
- DISALLOW_COPY_AND_ASSIGN(MacTrustedCertificates);
-};
-
-void SetMacTestCertificate(X509Certificate* cert) {
- Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert);
-}
-
namespace {
-
-typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
- CFDictionaryRef*);
-
-int NetErrorFromOSStatus(OSStatus status) {
- switch (status) {
- case noErr:
- return OK;
- case errSecNotAvailable:
- case errSecNoCertificateModule:
- case errSecNoPolicyModule:
- return ERR_NOT_IMPLEMENTED;
- case errSecAuthFailed:
- return ERR_ACCESS_DENIED;
- default:
- LOG(ERROR) << "Unknown error " << status << " mapped to net::ERR_FAILED";
- return ERR_FAILED;
- }
-}
-
-int CertStatusFromOSStatus(OSStatus status) {
- switch (status) {
- case noErr:
- return 0;
-
- case CSSMERR_TP_INVALID_ANCHOR_CERT:
- case CSSMERR_TP_NOT_TRUSTED:
- case CSSMERR_TP_INVALID_CERT_AUTHORITY:
- return CERT_STATUS_AUTHORITY_INVALID;
-
- case CSSMERR_TP_CERT_EXPIRED:
- case CSSMERR_TP_CERT_NOT_VALID_YET:
- // "Expired" and "not yet valid" collapse into a single status.
- return CERT_STATUS_DATE_INVALID;
-
- case CSSMERR_TP_CERT_REVOKED:
- case CSSMERR_TP_CERT_SUSPENDED:
- return CERT_STATUS_REVOKED;
-
- case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
- return CERT_STATUS_COMMON_NAME_INVALID;
-
- case CSSMERR_APPLETP_CRL_NOT_FOUND:
- case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
- case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
- return CERT_STATUS_NO_REVOCATION_MECHANISM;
-
- case CSSMERR_APPLETP_CRL_NOT_TRUSTED:
- case CSSMERR_APPLETP_CRL_SERVER_DOWN:
- case CSSMERR_APPLETP_CRL_NOT_VALID_YET:
- case CSSMERR_APPLETP_NETWORK_FAILURE:
- case CSSMERR_APPLETP_OCSP_BAD_RESPONSE:
- case CSSMERR_APPLETP_OCSP_NO_SIGNER:
- case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED:
- case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED:
- case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ:
- case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR:
- case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER:
- // We asked for a revocation check, but didn't get it.
- return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
-
- default:
- // Failure was due to something Chromium doesn't define a
- // specific status for (such as basic constraints violation, or
- // unknown critical extension)
- return CERT_STATUS_INVALID;
- }
-}
-
-bool OverrideHostnameMismatch(const std::string& hostname,
- std::vector<std::string>* dns_names) {
- // SecTrustEvaluate() does not check dotted IP addresses. If
- // hostname is provided as, say, 127.0.0.1, then the error
- // CSSMERR_APPLETP_HOSTNAME_MISMATCH will always be returned,
- // even if the certificate contains 127.0.0.1 as one of its names.
- // We, however, want to allow that behavior. SecTrustEvaluate()
- // only checks for digits and dots when considering whether a
- // hostname is an IP address, so IPv6 and hex addresses go through
- // its normal comparison.
- bool is_dotted_ip = true;
- bool override_hostname_mismatch = false;
- for (std::string::const_iterator c = hostname.begin();
- c != hostname.end() && is_dotted_ip; ++c)
- is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.';
- if (is_dotted_ip) {
- for (std::vector<std::string>::const_iterator name = dns_names->begin();
- name != dns_names->end() && !override_hostname_mismatch; ++name)
- override_hostname_mismatch = (*name == hostname);
- }
- return override_hostname_mismatch;
-}
-
struct CSSMFields {
CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
~CSSMFields() {
@@ -295,6 +144,31 @@ void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle,
}
}
+// Serializes the |cert_handle| into |pickle|, such that it may be unserialized
+// by ReadCertHandleFromPickle(). Returns FALSE on failure.
+bool WriteCertHandleToPickle(X509Certificate::OSCertHandle cert_handle,
+ Pickle* pickle) {
+ CSSM_DATA cert_data;
+ OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
+ if (status) {
+ return false;
+ }
+ return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data),
+ cert_data.Length);
+}
+
+// Reads a previously serialized OSCertHandle from the specified |pickle|. If
+// an error is encountered, returns NULL.
+SecCertificateRef ReadCertHandleFromPickle(const Pickle& pickle,
+ void** pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle.ReadData(pickle_iter, &data, &length))
+ return NULL;
+
+ return X509Certificate::CreateOSCertHandleFromBytes(data, length);
+}
+
// Creates a SecPolicyRef for the given OID, with optional value.
OSStatus CreatePolicy(const CSSM_OID* policy_OID,
void* option_data,
@@ -486,233 +360,6 @@ X509Certificate::CreateOSCertListHandle() const {
return cert_list;
}
-int X509Certificate::Verify(const std::string& hostname, int flags,
- CertVerifyResult* verify_result) const {
- verify_result->Reset();
-
- // Create an SSL SecPolicyRef, and configure it to perform hostname
- // validation. The hostname check does 99% of what we want, with the
- // exception of dotted IPv4 addreses, which we handle ourselves below.
- CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
- CSSM_APPLE_TP_SSL_OPTS_VERSION,
- hostname.size(),
- hostname.data(),
- 0
- };
- SecPolicyRef ssl_policy;
- OSStatus status = CreatePolicy(&CSSMOID_APPLE_TP_SSL,
- &tp_ssl_options,
- sizeof(tp_ssl_options),
- &ssl_policy);
- if (status)
- return NetErrorFromOSStatus(status);
- scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
-
- // 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.
- scoped_cftyperef<CFArrayRef> cert_list(CreateOSCertListHandle());
-
- // From here on, only one thread can be active at a time. We have had a number
- // of sporadic crashes in the SecTrustEvaluate call below, way down inside
- // Apple's cert code, which we suspect are caused by a thread-safety issue.
- // So as a speculative fix allow only one thread to use SecTrust on this cert.
- AutoLock lock(verification_lock_);
-
- SecTrustRef trust_ref = NULL;
- status = SecTrustCreateWithCertificates(cert_list, ssl_policy, &trust_ref);
- if (status)
- return NetErrorFromOSStatus(status);
- scoped_cftyperef<SecTrustRef> scoped_trust_ref(trust_ref);
-
- // Set the trusted anchor certificates for the SecTrustRef by merging the
- // system trust anchors and the test root certificate.
- CFArrayRef anchor_array =
- Singleton<MacTrustedCertificates>::get()->CopyTrustedCertificateArray();
- scoped_cftyperef<CFArrayRef> scoped_anchor_array(anchor_array);
- if (anchor_array) {
- status = SecTrustSetAnchorCertificates(trust_ref, anchor_array);
- if (status)
- return NetErrorFromOSStatus(status);
- }
-
- if (flags & VERIFY_REV_CHECKING_ENABLED) {
- // When called with VERIFY_REV_CHECKING_ENABLED, we ask SecTrustEvaluate()
- // to apply OCSP and CRL checking, but we're still subject to the global
- // settings, which are configured in the Keychain Access application (in
- // the Certificates tab of the Preferences dialog). If the user has
- // revocation disabled (which is the default), then we will get
- // kSecTrustResultRecoverableTrustFailure back from SecTrustEvaluate()
- // with one of a number of sub error codes indicating that revocation
- // checking did not occur. In that case, we'll set our own result to include
- // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
- //
- // NOTE: This does not apply to EV certificates, which always get
- // revocation checks regardless of the global settings.
- verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
- CSSM_APPLE_TP_ACTION_DATA tp_action_data = { CSSM_APPLE_TP_ACTION_VERSION };
- tp_action_data.ActionFlags = CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
- CFDataRef action_data_ref =
- CFDataCreate(NULL, reinterpret_cast<UInt8*>(&tp_action_data),
- sizeof(tp_action_data));
- if (!action_data_ref)
- return ERR_OUT_OF_MEMORY;
- scoped_cftyperef<CFDataRef> scoped_action_data_ref(action_data_ref);
- status = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT,
- action_data_ref);
- if (status)
- return NetErrorFromOSStatus(status);
- }
-
- // Verify the certificate. A non-zero result from SecTrustGetResult()
- // 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);
- 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);
- if (status)
- return NetErrorFromOSStatus(status);
- scoped_cftyperef<CFArrayRef> scoped_completed_chain(completed_chain);
-
- // Evaluate the results
- OSStatus cssm_result;
- bool got_certificate_error = false;
- switch (trust_result) {
- case kSecTrustResultUnspecified:
- case kSecTrustResultProceed:
- // Certificate chain is valid and trusted ("unspecified" indicates that
- // the user has not explicitly set a trust setting)
- break;
-
- case kSecTrustResultDeny:
- case kSecTrustResultConfirm:
- // Certificate chain is explicitly untrusted. For kSecTrustResultConfirm,
- // we're following what Secure Transport does and treating it as
- // "deny".
- verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
- break;
-
- case kSecTrustResultRecoverableTrustFailure:
- // Certificate chain has a failure that can be overridden by the user.
- status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
- if (status)
- return NetErrorFromOSStatus(status);
- switch (cssm_result) {
- case CSSMERR_TP_NOT_TRUSTED:
- case CSSMERR_TP_INVALID_ANCHOR_CERT:
- verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
- break;
- case CSSMERR_TP_CERT_EXPIRED:
- case CSSMERR_TP_CERT_NOT_VALID_YET:
- verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
- break;
- case CSSMERR_TP_CERT_REVOKED:
- case CSSMERR_TP_CERT_SUSPENDED:
- verify_result->cert_status |= CERT_STATUS_REVOKED;
- break;
- default:
- // Look for specific per-certificate errors below.
- break;
- }
- // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
- // structure which can catch multiple errors from each certificate.
- for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
- index < chain_count; ++index) {
- if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
- chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
- verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
- for (uint32 status_code_index = 0;
- status_code_index < chain_info[index].NumStatusCodes;
- ++status_code_index) {
- got_certificate_error = true;
- int cert_status = CertStatusFromOSStatus(
- chain_info[index].StatusCodes[status_code_index]);
- if (cert_status == CERT_STATUS_COMMON_NAME_INVALID) {
- std::vector<std::string> names;
- GetDNSNames(&names);
- if (OverrideHostnameMismatch(hostname, &names)) {
- cert_status = 0;
- }
- }
- verify_result->cert_status |= cert_status;
- }
- }
- // Be paranoid and ensure that we recorded at least one certificate
- // status on receiving kSecTrustResultRecoverableTrustFailure. The
- // call to SecTrustGetCssmResultCode() should pick up when the chain
- // is not trusted and the loop through CSSM_TP_APPLE_EVIDENCE_INFO
- // should pick up everything else, but let's be safe.
- if (!verify_result->cert_status && !got_certificate_error) {
- verify_result->cert_status |= CERT_STATUS_INVALID;
- NOTREACHED();
- }
- break;
-
- default:
- status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
- if (status)
- return NetErrorFromOSStatus(status);
- verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
- if (!verify_result->cert_status) {
- verify_result->cert_status |= CERT_STATUS_INVALID;
- }
- break;
- }
-
- // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
- // compatible with Windows, which in turn implements this behavior to be
- // compatible with WinHTTP, which doesn't report this error (bug 3004).
- verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
-
- if (IsCertStatusError(verify_result->cert_status))
- return MapCertStatusToNetError(verify_result->cert_status);
-
- if (flags & VERIFY_EV_CERT) {
- // Determine the certificate's EV status using SecTrustCopyExtendedResult(),
- // which we need to look up because the function wasn't added until
- // Mac OS X 10.5.7.
- CFBundleRef bundle =
- CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
- if (bundle) {
- SecTrustCopyExtendedResultFuncPtr copy_extended_result =
- reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>(
- CFBundleGetFunctionPointerForName(bundle,
- CFSTR("SecTrustCopyExtendedResult")));
- if (copy_extended_result) {
- CFDictionaryRef ev_dict = NULL;
- status = copy_extended_result(trust_ref, &ev_dict);
- if (!status && ev_dict) {
- // The returned dictionary contains the EV organization name from the
- // server certificate, which we don't need at this point (and we
- // have other ways to access, anyway). All we care is that
- // SecTrustCopyExtendedResult() returned noErr and a non-NULL
- // dictionary.
- CFRelease(ev_dict);
- verify_result->cert_status |= CERT_STATUS_IS_EV;
- }
- }
- }
- }
-
- return OK;
-}
-
-bool X509Certificate::VerifyEV() const {
- // We don't call this private method, but we do need to implement it because
- // it's defined in x509_certificate.h. We perform EV checking in the
- // Verify() above.
- NOTREACHED();
- return false;
-}
-
// static
bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
X509Certificate::OSCertHandle b) {

Powered by Google App Engine
This is Rietveld 408576698