mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 14:26:09 +03:00
378 lines
14 KiB
C++
378 lines
14 KiB
C++
|
// 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 <CommonCrypto/CommonDigest.h>
|
||
|
|
||
|
#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<uint8_t*>(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<SecCertificateRef> CreateSecCertificateFromBytes(
|
||
|
const uint8_t* data,
|
||
|
size_t length) {
|
||
|
CSSM_DATA cert_data;
|
||
|
cert_data.Data = const_cast<uint8_t*>(data);
|
||
|
cert_data.Length = length;
|
||
|
|
||
|
base::ScopedCFTypeRef<SecCertificateRef> cert_handle;
|
||
|
OSStatus status = SecCertificateCreateFromData(&cert_data, CSSM_CERT_X_509v3,
|
||
|
CSSM_CERT_ENCODING_DER,
|
||
|
cert_handle.InitializeInto());
|
||
|
if (status != noErr)
|
||
|
return base::ScopedCFTypeRef<SecCertificateRef>();
|
||
|
if (!IsValidSecCertificate(cert_handle.get()))
|
||
|
return base::ScopedCFTypeRef<SecCertificateRef>();
|
||
|
return cert_handle;
|
||
|
}
|
||
|
|
||
|
base::ScopedCFTypeRef<SecCertificateRef>
|
||
|
CreateSecCertificateFromX509Certificate(const X509Certificate* cert) {
|
||
|
return CreateSecCertificateFromBytes(CRYPTO_BUFFER_data(cert->cert_buffer()),
|
||
|
CRYPTO_BUFFER_len(cert->cert_buffer()));
|
||
|
}
|
||
|
|
||
|
scoped_refptr<X509Certificate> CreateX509CertificateFromSecCertificate(
|
||
|
SecCertificateRef sec_cert,
|
||
|
const std::vector<SecCertificateRef>& sec_chain) {
|
||
|
return CreateX509CertificateFromSecCertificate(sec_cert, sec_chain, {});
|
||
|
}
|
||
|
|
||
|
scoped_refptr<X509Certificate> CreateX509CertificateFromSecCertificate(
|
||
|
SecCertificateRef sec_cert,
|
||
|
const std::vector<SecCertificateRef>& sec_chain,
|
||
|
X509Certificate::UnsafeCreateOptions options) {
|
||
|
CSSM_DATA der_data;
|
||
|
if (!sec_cert || SecCertificateGetData(sec_cert, &der_data) != noErr)
|
||
|
return nullptr;
|
||
|
bssl::UniquePtr<CRYPTO_BUFFER> cert_handle(
|
||
|
X509Certificate::CreateCertBufferFromBytes(
|
||
|
reinterpret_cast<const char*>(der_data.Data), der_data.Length));
|
||
|
if (!cert_handle)
|
||
|
return nullptr;
|
||
|
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
|
||
|
for (const SecCertificateRef& sec_intermediate : sec_chain) {
|
||
|
if (!sec_intermediate ||
|
||
|
SecCertificateGetData(sec_intermediate, &der_data) != noErr) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
bssl::UniquePtr<CRYPTO_BUFFER> intermediate_cert_handle(
|
||
|
X509Certificate::CreateCertBufferFromBytes(
|
||
|
reinterpret_cast<const char*>(der_data.Data), der_data.Length));
|
||
|
if (!intermediate_cert_handle)
|
||
|
return nullptr;
|
||
|
intermediates.push_back(std::move(intermediate_cert_handle));
|
||
|
}
|
||
|
scoped_refptr<X509Certificate> result(
|
||
|
X509Certificate::CreateFromBufferUnsafeOptions(
|
||
|
std::move(cert_handle), std::move(intermediates), 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<CFStringRef> 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<CSSM_OID_PTR>(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<CSSM_OID_PTR>(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
|