Merge pull request #6869 from bigbrett/ios-ca-api

Add support for new Apple trust APIs with WOLFSSL_SYS_CA_CERTS
This commit is contained in:
David Garske
2023-10-18 10:29:41 -07:00
committed by GitHub
7 changed files with 289 additions and 23 deletions

View File

@ -1724,21 +1724,23 @@ if(WOLFSSL_SYS_CA_CERTS)
override_cache(WOLFSSL_SYS_CA_CERTS "no")
elseif(APPLE)
check_include_file("Security/SecTrustSettings.h" HAVE_SECURITY_SECTRUSTSETTINGS_H)
if(NOT HAVE_SECURITY_SECTRUSTSETTINGS_H)
message("Can't enable system CA certs without Security/SecTrustSettings.h.")
override_cache(WOLFSSL_SYS_CA_CERTS "no")
else()
check_include_file("Security/SecCertificate.h" HAVE_SECURITY_SECCERTIFICATE_H)
check_include_file("Security/SecTrust.h" HAVE_SECURITY_SECTRUST_H)
check_include_file("Security/SecPolicy.h" HAVE_SECURITY_SECPOLICY_H)
if(HAVE_SECURITY_SECTRUSTSETTINGS_H OR (HAVE_SECURITY_SECCERTIFICATE_H
AND HAVE_SECURITY_SECTRUST_H
AND HAVE_SECURITY_SECPOLICY_H))
find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation)
if(NOT CORE_FOUNDATION_FRAMEWORK)
message("Can't enable system CA certs without CoreFoundation framework.")
override_cache(WOLFSSL_SYS_CA_CERTS "no")
message(FATAL_ERROR "Can't enable system CA certs without CoreFoundation framework.")
else()
find_library(SECURITY_FRAMEWORK Security)
if(NOT SECURITY_FRAMEWORK)
message("Can't enable system CA certs without Security framework.")
override_cache(WOLFSSL_SYS_CA_CERTS "no")
message(FATAL_ERROR "Can't enable system CA certs without Security framework.")
endif()
endif()
else()
message(FATAL_ERROR "Can't enable system CA certs without Apple Security.framework headers.")
endif()
endif()

View File

@ -8351,14 +8351,24 @@ then
case $host_os in
*darwin*)
AC_CHECK_HEADERS([Security/SecTrustSettings.h],
# Creates the HAVE_SECURITY_SECXXX_H macros in config.h
AC_CHECK_HEADERS([Security/SecTrustSettings.h])
AC_CHECK_HEADERS([Security/SecCertificate.h])
AC_CHECK_HEADERS([Security/SecTrust.h])
AC_CHECK_HEADERS([Security/SecPolicy.h])
# Either Security/SecTrustSettings (for MacOS cert loading), or the
# trio of Security/SecCertificate.h, Security/SecTrust.h, and
# Security/SecPolicy.h (for native trust APIs other apple devices)
# must be present
AS_IF([test -n "$ac_cv_header_Security_SecTrustSettings_h" \
|| (test -n "$ac_cv_header_Security_SecCertificate_h" \
&& test -n "$ac_cv_header_Security_SecTrust_h" \
&& test -n "$ac_cv_header_Security_SecPolicy_h")],
[
# For Mac we need these frameworks to load system CA certs
LDFLAGS="$LDFLAGS -framework CoreFoundation -framework Security"
],
[
AC_MSG_NOTICE([Can't enable system CA certs without Security/SecTrustSettings.h])
ENABLED_SYS_CA_CERTS="no"
AC_MSG_ERROR([Unable to find Apple Security.framework headers])
]
)
;;

View File

@ -1236,7 +1236,9 @@ int wolfSSL_CTX_load_verify_locations_ex(WOLFSSL_CTX* ctx, const char* file,
\brief This function returns a pointer to an array of strings representing
directories wolfSSL will search for system CA certs when
wolfSSL_CTX_load_system_CA_certs is called.
wolfSSL_CTX_load_system_CA_certs is called. On systems that don't store
certificates in an accessible system directory (such as Apple platforms),
this function will always return NULL.
\return Valid pointer on success.
\return NULL pointer on failure.
@ -1266,10 +1268,19 @@ const char** wolfSSL_get_system_CA_dirs(word32* num);
/*!
\ingroup CertsKeys
\brief This function attempts to load CA certificates into a WOLFSSL_CTX
from an OS-dependent CA certificate store. Loaded certificates will be
trusted. The platforms supported and tested are: Linux (Debian, Ubuntu,
Gentoo, Fedora, RHEL), Windows 10/11, Android, Apple OS X and iOS.
\brief On most platforms (including Linux and Windows), this function
attempts to load CA certificates into a WOLFSSL_CTX from an OS-dependent
CA certificate store. Loaded certificates will be trusted.
On Apple platforms (excluding macOS), certificates can't be obtained from
the system, and therefore cannot be loaded into the wolfSSL certificate
manager. For these platforms, this function enables TLS connections bound to
the WOLFSSL_CTX to use the native system trust APIs to verify authenticity
of the peer certificate chain if the authenticity of the peer cannot first
be authenticated against certificates loaded by the user.
The platforms supported and tested are: Linux (Debian, Ubuntu,
Gentoo, Fedora, RHEL), Windows 10/11, Android, macOS, and iOS.
\return WOLFSSL_SUCCESS on success.
\return WOLFSSL_BAD_PATH if no system CA certs were loaded.

View File

@ -217,6 +217,14 @@ WOLFSSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS
static int _DtlsCheckWindow(WOLFSSL* ssl);
#endif
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
#include <Security/SecCertificate.h>
#include <Security/SecTrust.h>
#include <Security/SecPolicy.h>
static int DoAppleNativeCertValidation(const WOLFSSL_BUFFER_INFO* certs,
int totalCerts);
#endif /* #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
#ifdef WOLFSSL_DTLS13
#ifndef WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT
#define WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT 0
@ -271,6 +279,7 @@ static int SSL_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz,
const unsigned char* secret, int secretSz, void* ctx);
#endif
/* Label string for client random. */
#define SSC_CR "CLIENT_RANDOM"
@ -2426,6 +2435,11 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap)
maxq10xx_SetupPkCallbacks(ctx, &method->version);
#endif /* WOLFSSL_MAXQ10XX_TLS */
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
/* Should only be set when wolfSSL_CTX_load_system_CA_certs() is called */
ctx->doAppleNativeCertValidationFlag = 0;
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
return ret;
}
@ -14205,6 +14219,24 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
#endif /* WOLFSSL_ALT_CERT_CHAINS */
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
/* If we are using native Apple CA validation, it is okay
* for a CA cert to fail validation here, as we will verify
* the entire chain when we hit the peer (leaf) cert */
if (ssl->ctx->doAppleNativeCertValidationFlag) {
WOLFSSL_MSG("Bypassing errors to allow for Apple native"
" CA validation");
ret = 0; /* clear errors and continue */
args->verifyErr = 0;
#if defined(OPENSSL_EXTRA) \
|| defined(OPENSSL_EXTRA_X509_SMALL)
ssl->peerVerifyRet = 0;
#endif
/* do not add to certificate manager */
skipAddCA = 1;
}
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
/* Do verify callback */
ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);
if (ssl->options.verifyNone &&
@ -14273,7 +14305,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
FreeDecodedCert(args->dCert);
args->dCertInit = 0;
args->count--;
} /* while (count > 0 && !args->haveTrustPeer) */
} /* while (count > 1 && !args->haveTrustPeer) */
} /* if (count > 0) */
/* Check for error */
@ -14394,6 +14426,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
else {
WOLFSSL_MSG("Failed to verify Peer's cert");
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
if (ssl->peerVerifyRet == 0) { /* Return first cert error here */
if (ret == ASN_BEFORE_DATE_E) {
@ -14411,6 +14444,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
}
#endif
if (ssl->verifyCallback) {
WOLFSSL_MSG(
"\tCallback override available, will continue");
@ -14419,6 +14453,18 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
if (args->fatal)
DoCertFatalAlert(ssl, ret);
}
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
/* Disregard failure to verify peer cert, as we will verify
* the whole chain with the native API later */
else if (ssl->ctx->doAppleNativeCertValidationFlag) {
WOLFSSL_MSG("\tApple native CA validation override"
" available, will continue");
/* check if fatal error */
args->fatal = (args->verifyErr) ? 1 : 0;
if (args->fatal)
DoCertFatalAlert(ssl, ret);
}
#endif/*defined(__APPLE__)&& defined(WOLFSSL_SYS_CA_CERTS)*/
else {
WOLFSSL_MSG("\tNo callback override available, fatal");
args->fatal = 1;
@ -15178,6 +15224,22 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
#endif
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
/* If we can't validate the peer cert chain against the CAs loaded
* into wolfSSL, try to validate against the system certificates
* using Apple's native trust APIs */
if ((ret != 0) && (ssl->ctx->doAppleNativeCertValidationFlag)) {
if (DoAppleNativeCertValidation(args->certs,
args->totalCerts)) {
WOLFSSL_MSG("Apple native cert chain validation SUCCESS");
ret = 0;
}
else {
WOLFSSL_MSG("Apple native cert chain validation FAIL");
}
}
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
/* Do verify callback */
ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);
@ -39353,6 +39415,139 @@ int wolfSSL_sk_BY_DIR_entry_push(WOLF_STACK_OF(WOLFSSL_BY_DIR_entry)* sk,
#endif /* OPENSSL_ALL */
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
/*
* Converts a DER formatted certificate to a SecCertificateRef
*
* @param derCert pointer to the DER formatted certificate
* @param derLen length of the DER formatted cert, in bytes
*
* @return The newly created SecCertificateRef. Must be freed by caller when
* no longer in use
*/
static SecCertificateRef ConvertToSecCertificateRef(const byte* derCert,
int derLen)
{
CFDataRef derData = NULL;
SecCertificateRef secCert = NULL;
WOLFSSL_ENTER("ConvertToSecCertificateRef");
/* Create a CFDataRef from the DER encoded certificate */
derData = CFDataCreate(kCFAllocatorDefault, derCert, derLen);
if (!derData) {
WOLFSSL_MSG("Error: can't create CFDataRef object for DER cert");
goto cleanup;
}
/* Create a SecCertificateRef from the CFDataRef */
secCert = SecCertificateCreateWithData(kCFAllocatorDefault, derData);
if (!secCert) {
WOLFSSL_MSG("Error: can't create SecCertificateRef from CFDataRef");
goto cleanup;
}
cleanup:
if (derData) {
CFRelease(derData);
}
WOLFSSL_LEAVE("ConvertToSecCertificateRef", !!secCert);
return secCert;
}
/*
* Validates a chain of certificates using the Apple system trust APIs
*
* @param certs pointer to the certificate chain to validate
* @param totalCerts the number of certificates in certs
*
* @return 1 if chain is valid and trusted
* @return 0 if chain is invalid or untrusted
*
* As of MacOS 14.0 we are still able to access system certificates and load
* them manually into wolfSSL. For other apple devices, apple has removed the
* ability to obtain certificates from the trust store, so we can't use
* wolfSSL's built-in certificate validation mechanisms anymore. We instead
* must call into the Security Framework APIs to authenticate peer certificates
*/
static int DoAppleNativeCertValidation(const WOLFSSL_BUFFER_INFO* certs,
int totalCerts)
{
int i;
int ret;
OSStatus status;
CFMutableArrayRef certArray = NULL;
SecCertificateRef secCert = NULL;
SecTrustRef trust = NULL;
SecPolicyRef policy = NULL ;
WOLFSSL_ENTER("DoAppleNativeCertValidation");
certArray = CFArrayCreateMutable(kCFAllocatorDefault,
totalCerts,
&kCFTypeArrayCallBacks);
if (!certArray) {
WOLFSSL_MSG("Error: can't allocate CFArray for certificates");
ret = 0;
goto cleanup;
}
for (i = 0; i < totalCerts; i++) {
secCert = ConvertToSecCertificateRef(certs[i].buffer, certs[i].length);
if (!secCert) {
WOLFSSL_MSG("Error: can't convert DER cert to SecCertificateRef");
ret = 0;
goto cleanup;
}
else {
CFArrayAppendValue(certArray, secCert);
/* Release, since the array now holds the reference */
CFRelease(secCert);
}
}
/* Create trust object for SecCertifiate Ref */
policy = SecPolicyCreateSSL(true, NULL);
status = SecTrustCreateWithCertificates(certArray, policy, &trust);
if (status != errSecSuccess) {
WOLFSSL_MSG_EX("Error creating trust object, "
"SecTrustCreateWithCertificates returned %d",status);
ret = 0;
goto cleanup;
}
/* Evaluate the certificate's authenticity */
if (SecTrustEvaluateWithError(trust, NULL) == 1) {
WOLFSSL_MSG("Cert chain is trusted");
ret = 1;
}
else {
WOLFSSL_MSG("Cert chain trust evaluation failed"
"SecTrustEvaluateWithError returned 0");
ret = 0;
}
/* Cleanup */
cleanup:
if (certArray) {
CFRelease(certArray);
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
WOLFSSL_LEAVE("DoAppleNativeCertValidation", ret);
return ret;
}
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
#undef ERROR_OUT

View File

@ -8403,6 +8403,18 @@ static int LoadSystemCaCertsWindows(WOLFSSL_CTX* ctx, byte* loaded)
#elif defined(__APPLE__)
#if defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) \
&& !defined(WOLFSSL_APPLE_NATIVE_CERT_VALIDATION)
/*
* Manually obtains certificates from the system trust store and loads them
* directly into wolfSSL "the old way".
*
* As of MacOS 14.0 we are still able to use this method to access system
* certificates. Accessiblity of this API is indicated by the presence of the
* Security/SecTrustSettings.h header. In the likely event that Apple removes
* access to this API on Macs, this function should be removed and the
* DoAppleNativeCertValidation() routine should be used for all devices.
*/
static int LoadSystemCaCertsMac(WOLFSSL_CTX* ctx, byte* loaded)
{
int ret = WOLFSSL_SUCCESS;
@ -8463,6 +8475,7 @@ static int LoadSystemCaCertsMac(WOLFSSL_CTX* ctx, byte* loaded)
return ret;
}
#endif /* defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) */
#else
@ -8536,11 +8549,48 @@ int wolfSSL_CTX_load_system_CA_certs(WOLFSSL_CTX* ctx)
WOLFSSL_ENTER("wolfSSL_CTX_load_system_CA_certs");
#ifdef USE_WINDOWS_API
ret = LoadSystemCaCertsWindows(ctx, &loaded);
#elif defined(__APPLE__)
#if defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) \
&& !defined(WOLFSSL_APPLE_NATIVE_CERT_VALIDATION)
/* As of MacOS 14.0 we are still able to access system certificates and
* load them manually into wolfSSL "the old way". Accessiblity of this API
* is indicated by the presence of the Security/SecTrustSettings.h header */
ret = LoadSystemCaCertsMac(ctx, &loaded);
#elif defined(WOLFSSL_APPLE_NATIVE_CERT_VALIDATION) \
|| (defined(HAVE_SECURITY_SECCERTIFICATE_H) \
&& defined(HAVE_SECURITY_SECTRUST_H) \
&& defined(HAVE_SECURITY_SECPOLICY_H))
/* For other Apple devices, Apple has removed the ability to obtain
* certificates from the trust store, so we can't use wolfSSL's built-in
* certificate validation mechanisms anymore. We instead must call into the
* Security Framework APIs to authenticate peer certificates when received.
* (see src/internal.c:DoAppleNativeCertValidation()).
* Thus, there is no CA "loading" required, but to keep behavior consistent
* with the current API (not using system CA certs unless this function has
* been called), we simply set a flag indicating that the new apple trust
* verification routine should be used later */
ctx->doAppleNativeCertValidationFlag = 1;
ret = WOLFSSL_SUCCESS;
loaded = 1;
#else
/* HAVE_SECURITY_SECXXX_H macros are set by autotools or CMake when searching
* system for the required SDK headers. If building with user_settings.h, you
* will need to manually define WOLFSSL_APPLE_NATIVE_CERT_VALIDATION
* and ensure the appropriate Security.framework headers and libraries are
* visible to your compiler */
#error "WOLFSSL_SYS_CA_CERTS on Apple devices requires Security.framework" \
" header files to be detected, or a manual override with" \
" WOLFSSL_APPLE_NATIVE_CERT_VALIDATION"
#endif
#else
ret = LoadSystemCaCertsNix(ctx, &loaded);
#endif
if (ret == WOLFSSL_SUCCESS && !loaded) {

View File

@ -3922,6 +3922,9 @@ struct WOLFSSL_CTX {
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
WOLFSSL_EchConfig* echConfigs;
#endif
#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
byte doAppleNativeCertValidationFlag:1;
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
};
WOLFSSL_LOCAL

View File

@ -3143,11 +3143,6 @@ extern void uITRON4_free(void *p) ;
/* Turning off WOLFSSL_SYS_CA_CERTS b/c NO_CERTS is defined */
#undef WOLFSSL_SYS_CA_CERTS
#endif
#if defined(__APPLE__) && !defined(HAVE_SECURITY_SECTRUSTSETTINGS_H)
/* Turning off WOLFSSL_SYS_CA_CERTS b/c no Security/SecTrustSettings.h header */
#undef WOLFSSL_SYS_CA_CERTS
#endif
#endif /* WOLFSSL_SYS_CA_CERTS */
#if defined(SESSION_CACHE_DYNAMIC_MEM) && defined(PERSIST_SESSION_CACHE)