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