// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/x509_util_mac.h" #include #include "base/logging.h" #include "base/strings/sys_string_conversions.h" #include "net/cert/x509_certificate.h" #include "third_party/apple_apsl/cssmapplePriv.h" #include "third_party/boringssl/src/include/openssl/pool.h" namespace net { // CSSM functions are deprecated as of OSX 10.7, but have no replacement. // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" namespace x509_util { namespace { // Creates a SecPolicyRef for the given OID, with optional value. OSStatus CreatePolicy(const CSSM_OID* policy_oid, void* option_data, size_t option_length, SecPolicyRef* policy) { SecPolicySearchRef search; OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_oid, NULL, &search); if (err) return err; err = SecPolicySearchCopyNext(search, policy); CFRelease(search); if (err) return err; if (option_data) { CSSM_DATA options_data = { option_length, reinterpret_cast(option_data) }; err = SecPolicySetValue(*policy, &options_data); if (err) { CFRelease(*policy); return err; } } return noErr; } } // namespace bool IsValidSecCertificate(SecCertificateRef cert_handle) { const CSSM_X509_NAME* sanity_check = NULL; OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check); return status == noErr && sanity_check; } base::ScopedCFTypeRef CreateSecCertificateFromBytes( const uint8_t* data, size_t length) { CSSM_DATA cert_data; cert_data.Data = const_cast(data); cert_data.Length = length; base::ScopedCFTypeRef cert_handle; OSStatus status = SecCertificateCreateFromData(&cert_data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, cert_handle.InitializeInto()); if (status != noErr) return base::ScopedCFTypeRef(); if (!IsValidSecCertificate(cert_handle.get())) return base::ScopedCFTypeRef(); return cert_handle; } base::ScopedCFTypeRef CreateSecCertificateFromX509Certificate(const X509Certificate* cert) { return CreateSecCertificateFromBytes( CRYPTO_BUFFER_data(cert->os_cert_handle()), CRYPTO_BUFFER_len(cert->os_cert_handle())); } scoped_refptr CreateX509CertificateFromSecCertificate( SecCertificateRef sec_cert, const std::vector& sec_chain) { return CreateX509CertificateFromSecCertificate(sec_cert, sec_chain, {}); } scoped_refptr CreateX509CertificateFromSecCertificate( SecCertificateRef sec_cert, const std::vector& sec_chain, X509Certificate::UnsafeCreateOptions options) { CSSM_DATA der_data; if (!sec_cert || SecCertificateGetData(sec_cert, &der_data) != noErr) return nullptr; bssl::UniquePtr cert_handle( X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast(der_data.Data), der_data.Length)); if (!cert_handle) return nullptr; std::vector> intermediates; X509Certificate::OSCertHandles intermediates_raw; for (const SecCertificateRef& sec_intermediate : sec_chain) { if (!sec_intermediate || SecCertificateGetData(sec_intermediate, &der_data) != noErr) { return nullptr; } bssl::UniquePtr intermediate_cert_handle( X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast(der_data.Data), der_data.Length)); if (!intermediate_cert_handle) return nullptr; intermediates_raw.push_back(intermediate_cert_handle.get()); intermediates.push_back(std::move(intermediate_cert_handle)); } scoped_refptr result( X509Certificate::CreateFromHandleUnsafeOptions( cert_handle.get(), intermediates_raw, options)); return result; } bool IsSelfSigned(SecCertificateRef cert_handle) { CSSMCachedCertificate cached_cert; OSStatus status = cached_cert.Init(cert_handle); if (status != noErr) return false; CSSMFieldValue subject; status = cached_cert.GetField(&CSSMOID_X509V1SubjectNameStd, &subject); if (status != CSSM_OK || !subject.field()) return false; CSSMFieldValue issuer; status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, &issuer); if (status != CSSM_OK || !issuer.field()) return false; if (subject.field()->Length != issuer.field()->Length || memcmp(subject.field()->Data, issuer.field()->Data, issuer.field()->Length) != 0) { return false; } CSSM_CL_HANDLE cl_handle = CSSM_INVALID_HANDLE; status = SecCertificateGetCLHandle(cert_handle, &cl_handle); if (status) return false; CSSM_DATA cert_data; status = SecCertificateGetData(cert_handle, &cert_data); if (status) return false; if (CSSM_CL_CertVerify(cl_handle, 0, &cert_data, &cert_data, NULL, 0)) return false; return true; } SHA256HashValue CalculateFingerprint256(SecCertificateRef cert) { SHA256HashValue sha256; memset(sha256.data, 0, sizeof(sha256.data)); CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(cert, &cert_data); if (status) return sha256; DCHECK(cert_data.Data); DCHECK_NE(cert_data.Length, 0U); CC_SHA256(cert_data.Data, cert_data.Length, sha256.data); return sha256; } OSStatus CreateSSLClientPolicy(SecPolicyRef* policy) { *policy = SecPolicyCreateSSL(false /* server */, nullptr); return *policy ? noErr : errSecNoPolicyModule; } OSStatus CreateSSLServerPolicy(const std::string& hostname, SecPolicyRef* policy) { base::ScopedCFTypeRef hostname_cfstring; if (!hostname.empty()) { hostname_cfstring.reset(base::SysUTF8ToCFStringRef(hostname)); if (!hostname_cfstring) return errSecNoPolicyModule; } *policy = SecPolicyCreateSSL(true /* server */, hostname_cfstring.get()); return *policy ? noErr : errSecNoPolicyModule; } OSStatus CreateBasicX509Policy(SecPolicyRef* policy) { *policy = SecPolicyCreateBasicX509(); return *policy ? noErr : errSecNoPolicyModule; } OSStatus CreateRevocationPolicies(bool enable_revocation_checking, CFMutableArrayRef policies) { if (__builtin_available(macos 10.12, *)) { // On Sierra, it's not possible to disable network revocation checking // without also breaking AIA. If revocation checking isn't explicitly // enabled, just don't add a revocation policy. if (!enable_revocation_checking) return noErr; // If revocation checking is requested, enable checking and require positive // results. Note that this will fail if there are certs with no // CRLDistributionPoints or OCSP AIA urls, which differs from the behavior // of |enable_revocation_checking| on pre-10.12. There does not appear to be // a way around this, but it shouldn't matter much in practice since // revocation checking is generally used with EV certs, where it is expected // that all certs include revocation mechanisms. SecPolicyRef revocation_policy = SecPolicyCreateRevocation(kSecRevocationUseAnyAvailableMethod | kSecRevocationRequirePositiveResponse); if (!revocation_policy) return errSecNoPolicyModule; CFArrayAppendValue(policies, revocation_policy); CFRelease(revocation_policy); return noErr; } OSStatus status = noErr; // In order to bypass the system revocation checking settings, the // SecTrustRef must have at least one revocation policy associated with it. // Since it is not known prior to verification whether the Apple TP will // consider a certificate as an EV candidate, the default policy used is a // CRL policy, since it does not communicate over the network. // If the TP believes the leaf is an EV cert, it will explicitly add an // OCSP policy to perform the online checking, and if it doesn't believe // that the leaf is EV, then the default CRL policy will effectively no-op. // This behaviour is used to implement EV-only revocation checking. if (enable_revocation_checking) { CSSM_APPLE_TP_CRL_OPTIONS tp_crl_options; memset(&tp_crl_options, 0, sizeof(tp_crl_options)); tp_crl_options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; // Only allow network CRL fetches if the caller explicitly requests // online revocation checking. Note that, as of OS X 10.7.2, the system // will set force this flag on according to system policies, so // online revocation checks cannot be completely disabled. // Starting with OS X 10.12, if a CRL policy is added without the // FETCH_CRL_FROM_NET flag, AIA fetching is disabled. tp_crl_options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET; SecPolicyRef crl_policy; status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_CRL, &tp_crl_options, sizeof(tp_crl_options), &crl_policy); if (status) return status; CFArrayAppendValue(policies, crl_policy); CFRelease(crl_policy); } // If revocation checking is explicitly enabled, then add an OCSP policy // and allow network access. If both revocation checking is // disabled, then the added OCSP policy will be prevented from // accessing the network. This is done because the TP will force an OCSP // policy to be present when it believes the certificate is EV. CSSM_APPLE_TP_OCSP_OPTIONS tp_ocsp_options; memset(&tp_ocsp_options, 0, sizeof(tp_ocsp_options)); tp_ocsp_options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION; if (enable_revocation_checking) { // The default for the OCSP policy is to fetch responses via the network, // unlike the CRL policy default. The policy is further modified to // prefer OCSP over CRLs, if both are specified on the certificate. This // is because an OCSP response is both sufficient and typically // significantly smaller than the CRL counterpart. tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT; } else { // Effectively disable OCSP checking by making it impossible to get an // OCSP response. Even if the Apple TP forces OCSP, no checking will // be able to succeed. If this happens, the Apple TP will report an error // that OCSP was unavailable, but this will be handled and suppressed in // X509Certificate::Verify(). tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_DISABLE_NET | CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE; } SecPolicyRef ocsp_policy; status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_OCSP, &tp_ocsp_options, sizeof(tp_ocsp_options), &ocsp_policy); if (status) return status; CFArrayAppendValue(policies, ocsp_policy); CFRelease(ocsp_policy); return status; } CSSMFieldValue::CSSMFieldValue() : cl_handle_(CSSM_INVALID_HANDLE), oid_(NULL), field_(NULL) { } CSSMFieldValue::CSSMFieldValue(CSSM_CL_HANDLE cl_handle, const CSSM_OID* oid, CSSM_DATA_PTR field) : cl_handle_(cl_handle), oid_(const_cast(oid)), field_(field) { } CSSMFieldValue::~CSSMFieldValue() { Reset(CSSM_INVALID_HANDLE, NULL, NULL); } void CSSMFieldValue::Reset(CSSM_CL_HANDLE cl_handle, CSSM_OID_PTR oid, CSSM_DATA_PTR field) { if (cl_handle_ && oid_ && field_) CSSM_CL_FreeFieldValue(cl_handle_, oid_, field_); cl_handle_ = cl_handle; oid_ = oid; field_ = field; } CSSMCachedCertificate::CSSMCachedCertificate() : cl_handle_(CSSM_INVALID_HANDLE), cached_cert_handle_(CSSM_INVALID_HANDLE) { } CSSMCachedCertificate::~CSSMCachedCertificate() { if (cl_handle_ && cached_cert_handle_) CSSM_CL_CertAbortCache(cl_handle_, cached_cert_handle_); } OSStatus CSSMCachedCertificate::Init(SecCertificateRef os_cert_handle) { DCHECK(!cl_handle_ && !cached_cert_handle_); DCHECK(os_cert_handle); CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(os_cert_handle, &cert_data); if (status) return status; status = SecCertificateGetCLHandle(os_cert_handle, &cl_handle_); if (status) { DCHECK(!cl_handle_); return status; } status = CSSM_CL_CertCache(cl_handle_, &cert_data, &cached_cert_handle_); if (status) DCHECK(!cached_cert_handle_); return status; } OSStatus CSSMCachedCertificate::GetField(const CSSM_OID* field_oid, CSSMFieldValue* field) const { DCHECK(cl_handle_); DCHECK(cached_cert_handle_); CSSM_OID_PTR oid = const_cast(field_oid); CSSM_DATA_PTR field_ptr = NULL; CSSM_HANDLE results_handle = CSSM_INVALID_HANDLE; uint32_t field_value_count = 0; CSSM_RETURN status = CSSM_CL_CertGetFirstCachedFieldValue( cl_handle_, cached_cert_handle_, oid, &results_handle, &field_value_count, &field_ptr); if (status) return status; // Note: |field_value_count| may be > 1, indicating that more than one // value is present. This may happen with extensions, but for current // usages, only the first value is returned. CSSM_CL_CertAbortQuery(cl_handle_, results_handle); field->Reset(cl_handle_, oid, field_ptr); return CSSM_OK; } } // namespace x509_util #pragma clang diagnostic pop // "-Wdeprecated-declarations" } // namespace net