| 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) {
|
|
|