Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1721,21 +1721,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()

Expand Down
18 changes: 14 additions & 4 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -8257,14 +8257,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])
]
)
;;
Expand Down
21 changes: 16 additions & 5 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
197 changes: 196 additions & 1 deletion src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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) {
Expand All @@ -14411,6 +14444,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
}
#endif

if (ssl->verifyCallback) {
WOLFSSL_MSG(
"\tCallback override available, will continue");
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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

Expand Down
Loading