Apple native cert validation: add WOLFSSL_TEST_APPLE_CERT_VALIDATION feature macro that forces system CA certs on and makes all CA certs added to CM via xxx_load_verify_xxx APIs to instead be loaded as system trust anchors when used for TLS cert verification

This commit is contained in:
Brett
2025-06-06 13:40:07 -06:00
committed by Ruby Martin
parent 981ba4b14c
commit 7232b3a6bb
3 changed files with 244 additions and 25 deletions

View File

@@ -2915,6 +2915,12 @@ void SSL_CtxResourceFree(WOLFSSL_CTX* ctx)
ctx->x509Chain = NULL; ctx->x509Chain = NULL;
} }
#endif #endif
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
if (ctx->testTrustedCAs != NULL) {
CFRelease(ctx->testTrustedCAs);
ctx->testTrustedCAs = NULL;
}
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
#endif /* !NO_CERTS */ #endif /* !NO_CERTS */
#ifdef HAVE_TLS_EXTENSIONS #ifdef HAVE_TLS_EXTENSIONS
@@ -42777,6 +42783,79 @@ cleanup:
return secCert; return secCert;
} }
static int DisplaySecTrustError(CFErrorRef error, SecTrustRef trust)
{
CFStringRef desc;
CFStringRef domain;
SecTrustResultType trustResult;
CFDictionaryRef info;
/* Description */
desc = CFErrorCopyDescription(error);
if (desc) {
char buffer[256];
if (CFStringGetCString(desc, buffer, sizeof(buffer),
kCFStringEncodingUTF8)) {
WOLFSSL_MSG_EX("SecTrustEvaluateWithError Error description: %s\n",
buffer);
}
CFRelease(desc);
}
/* Domain */
domain = CFErrorGetDomain(error);
if (domain) {
char domainStr[128];
if (CFStringGetCString(domain, domainStr, sizeof(domainStr),
kCFStringEncodingUTF8)) {
WOLFSSL_MSG_EX("SecTrustEvaluateWithError Domain: %s\n", domainStr);
}
}
/* Get additional trust result info */
if (SecTrustGetTrustResult(trust, &trustResult) == errSecSuccess) {
WOLFSSL_MSG_EX("SecTrustResultType: %d\n", trustResult);
/* Optional: decode the enum */
switch (trustResult) {
case kSecTrustResultInvalid:
WOLFSSL_MSG("TrustResult: Invalid\n");
break;
case kSecTrustResultProceed:
WOLFSSL_MSG("TrustResult: Proceed\n");
break;
case kSecTrustResultDeny:
WOLFSSL_MSG("TrustResult: Deny\n");
break;
case kSecTrustResultUnspecified:
WOLFSSL_MSG("TrustResult: Unspecified (implicitly trusted)\n");
break;
case kSecTrustResultRecoverableTrustFailure:
WOLFSSL_MSG("TrustResult: Recoverable trust failure\n");
break;
case kSecTrustResultFatalTrustFailure:
WOLFSSL_MSG("TrustResult: Fatal trust failure\n");
break;
case kSecTrustResultOtherError:
WOLFSSL_MSG("TrustResult: Other error\n");
break;
default:
WOLFSSL_MSG("TrustResult: Unknown\n");
break;
}
}
else {
WOLFSSL_MSG("SecTrustGetTrustResult failed\n");
}
info = CFErrorCopyUserInfo(error);
if (info) {
printf("Trust error info dump:\n");
CFShow(info);
CFRelease(info);
}
return 0;
}
/* /*
* Validates a chain of certificates using the Apple system trust APIs * Validates a chain of certificates using the Apple system trust APIs
@@ -42793,13 +42872,13 @@ cleanup:
* wolfSSL's built-in certificate validation mechanisms anymore. We instead * wolfSSL's built-in certificate validation mechanisms anymore. We instead
* must call into the Security Framework APIs to authenticate peer certificates * must call into the Security Framework APIs to authenticate peer certificates
*/ */
static int DoAppleNativeCertValidation(WOLFSSL* ssl, static int DoAppleNativeCertValidation(WOLFSSL* ssl,
const WOLFSSL_BUFFER_INFO* certs, const WOLFSSL_BUFFER_INFO* certs,
int totalCerts) int totalCerts)
{ {
int i; int i;
int ret; int ret;
OSStatus status; OSStatus status;
CFMutableArrayRef certArray = NULL; CFMutableArrayRef certArray = NULL;
SecCertificateRef secCert = NULL; SecCertificateRef secCert = NULL;
SecTrustRef trust = NULL; SecTrustRef trust = NULL;
@@ -42808,8 +42887,7 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl,
WOLFSSL_ENTER("DoAppleNativeCertValidation"); WOLFSSL_ENTER("DoAppleNativeCertValidation");
certArray = CFArrayCreateMutable(kCFAllocatorDefault, certArray = CFArrayCreateMutable(kCFAllocatorDefault, totalCerts,
totalCerts,
&kCFTypeArrayCallBacks); &kCFTypeArrayCallBacks);
if (!certArray) { if (!certArray) {
WOLFSSL_MSG("Error: can't allocate CFArray for certificates"); WOLFSSL_MSG("Error: can't allocate CFArray for certificates");
@@ -42818,8 +42896,8 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl,
} }
for (i = 0; i < totalCerts; i++) { for (i = 0; i < totalCerts; i++) {
secCert = ConvertToSecCertificateRef(certs[i].buffer, secCert =
(int)certs[i].length); ConvertToSecCertificateRef(certs[i].buffer, (int)certs[i].length);
if (!secCert) { if (!secCert) {
WOLFSSL_MSG("Error: can't convert DER cert to SecCertificateRef"); WOLFSSL_MSG("Error: can't convert DER cert to SecCertificateRef");
ret = 0; ret = 0;
@@ -42833,35 +42911,74 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl,
} }
/* Create trust object for SecCertifiate Ref */ /* Create trust object for SecCertifiate Ref */
if (ssl->buffers.domainName.buffer && if (ssl->buffers.domainName.buffer && ssl->buffers.domainName.length > 0) {
ssl->buffers.domainName.length > 0) {
/* Create policy with specified value to require host name match */ /* Create policy with specified value to require host name match */
hostname = CFStringCreateWithCString(kCFAllocatorDefault, hostname = CFStringCreateWithCString(
(const char*)ssl->buffers.domainName.buffer, kCFAllocatorDefault, (const char*)ssl->buffers.domainName.buffer,
kCFStringEncodingUTF8); kCFStringEncodingUTF8);
} }
if (hostname != NULL) { if (hostname != NULL) {
policy = SecPolicyCreateSSL(true, hostname); policy = SecPolicyCreateSSL(true, hostname);
} else { }
else {
policy = SecPolicyCreateSSL(true, NULL); policy = SecPolicyCreateSSL(true, NULL);
} }
status = SecTrustCreateWithCertificates(certArray, policy, &trust); status = SecTrustCreateWithCertificates(certArray, policy, &trust);
if (status != errSecSuccess) { if (status != errSecSuccess) {
WOLFSSL_MSG_EX("Error creating trust object, " WOLFSSL_MSG_EX("Error creating trust object, "
"SecTrustCreateWithCertificates returned %d",status); "SecTrustCreateWithCertificates returned %d",
status);
ret = 0; ret = 0;
goto cleanup; goto cleanup;
} }
#if defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
/* TEST ONLY CODE:
* Set accumulated list of trusted CA certificates as trust anchors */
if (ssl->ctx->testTrustedCAs != NULL) {
status = SecTrustSetAnchorCertificates(trust, ssl->ctx->testTrustedCAs);
if (status != errSecSuccess) {
WOLFSSL_MSG_EX("Error setting anchor certificates: %d", status);
ret = 0;
goto cleanup;
}
}
#endif
/* Evaluate the certificate's authenticity */ /* Evaluate the certificate's authenticity */
if (SecTrustEvaluateWithError(trust, NULL) == 1) { WOLFSSL_MSG("Performing Apple native cert validation via "
WOLFSSL_MSG("Cert chain is trusted"); "SecTrustEvaluateWithError");
ret = 1; CFErrorRef error = NULL;
ret = SecTrustEvaluateWithError(trust, &error);
if (ret != 1) {
if (error) {
CFIndex code;
code = CFErrorGetCode(error);
WOLFSSL_MSG_EX("SecTrustEvaluateWithError failed with code: %ld\n",
code);
DisplaySecTrustError(error, trust);
#if WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
/* TEST ONLY CODE:
* wolfSSL API tests use a cert with a validity period that is too
* long for the Apple system trust APIs
* (See: https://support.apple.com/en-us/103769)
* therefore we should skip over this particular error */
if (code == errSecCertificateValidityPeriodTooLong) {
WOLFSSL_MSG("Skipping certificate validity period error");
ret = 1;
}
#endif
CFRelease(error);
}
else {
WOLFSSL_MSG(
"SecTrustEvaluateWithError failed with unknown error.\n");
}
} }
else { else {
WOLFSSL_MSG("Cert chain trust evaluation failed" WOLFSSL_MSG("SecTrustEvaluateWithError succeeded");
"SecTrustEvaluateWithError returned 0");
ret = 0;
} }
/* Cleanup */ /* Cleanup */
@@ -42883,6 +43000,38 @@ cleanup:
return ret; return ret;
} }
#if defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
int wolfSSL_TestAppleNativeCertValidation_AppendCA(WOLFSSL_CTX* ctx,
const byte* derCert,
int derLen)
{
SecCertificateRef certRef;
if (derCert == NULL || derLen == 0) {
return WOLFSSL_FAILURE;
}
/* Create the base array for trust anchors if it doesn't exist */
if (ctx->testTrustedCAs == NULL) {
ctx->testTrustedCAs =
CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!ctx->testTrustedCAs) {
return WOLFSSL_FAILURE;
}
}
certRef = ConvertToSecCertificateRef(derCert, derLen);
if (!certRef) {
return false;
}
CFArrayAppendValue(ctx->testTrustedCAs, certRef);
CFRelease(certRef);
return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */ #endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */
#undef ERROR_OUT #undef ERROR_OUT

View File

@@ -42,9 +42,14 @@
#endif #endif
#endif #endif
#if defined(__APPLE__) && defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) #if defined(__APPLE__)
#if defined(HAVE_SECURITY_SECTRUSTSETTINGS_H)
#include <Security/SecTrustSettings.h> #include <Security/SecTrustSettings.h>
#endif #endif /* HAVE_SECURITY_SECTRUSTSETTINGS_H */
#if WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
#include <CoreFoundation/CoreFoundation.h>
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
#endif /* __APPLE__ */
#endif /* WOLFSSL_SYS_CA_CERTS */ #endif /* WOLFSSL_SYS_CA_CERTS */
@@ -2153,8 +2158,40 @@ static int ProcessBufferCertHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
/* CA certificate to verify with. */ /* CA certificate to verify with. */
if (type == CA_TYPE) { if (type == CA_TYPE) {
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
word32 derLen;
byte* derBuf;
if (ctx->doAppleNativeCertValidationFlag == 1) {
derLen = der->length;
derBuf = (byte*)XMALLOC(derLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (derBuf == NULL) {
return MEMORY_E;
}
XMEMCPY(derBuf, der->buffer, derLen);
}
else {
(void)derLen;
(void)derBuf;
}
#endif
/* verify CA unless user set to no verify */ /* verify CA unless user set to no verify */
ret = AddCA(ctx->cm, &der, WOLFSSL_USER_CA, verify); ret = AddCA(ctx->cm, &der, WOLFSSL_USER_CA, verify);
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
if (ret == 1 && ctx->doAppleNativeCertValidationFlag == 1) {
WOLFSSL_MSG("Appending CA to cert list for native cert validation test");
ret = wolfSSL_TestAppleNativeCertValidation_AppendCA(ctx, derBuf, (int)derLen);
if (ret == WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Clearing CA table for native cert validation test");
/* Clear the CA table so we can ensure they won't be used for
* verification */
ret = wolfSSL_CertManagerUnloadCAs(ctx->cm);
if (ret == WOLFSSL_SUCCESS) {
ret = 0;
}
}
XFREE(derBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif /* !WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
if (ret == 1) { if (ret == 1) {
ret = 0; ret = 0;
} }
@@ -2978,6 +3015,12 @@ int wolfSSL_CTX_load_verify_locations_ex(WOLFSSL_CTX* ctx, const char* file,
ret = NOT_COMPILED_IN; ret = NOT_COMPILED_IN;
(void)flags; (void)flags;
#endif #endif
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
if (ret == 1) {
wolfSSL_CTX_load_system_CA_certs(ctx);
}
#endif
} }
return ret; return ret;
@@ -3422,6 +3465,12 @@ int wolfSSL_CTX_der_load_verify_locations(WOLFSSL_CTX* ctx, const char* file,
GET_VERIFY_SETTING_CTX(ctx)); GET_VERIFY_SETTING_CTX(ctx));
} }
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
if (ret == 1) {
wolfSSL_CTX_load_system_CA_certs(ctx);
}
#endif
/* Return 1 on success or 0 on failure. */ /* Return 1 on success or 0 on failure. */
return WS_RC(ret); return WS_RC(ret);
} }
@@ -3950,6 +3999,12 @@ int wolfSSL_CTX_load_verify_buffer_ex(WOLFSSL_CTX* ctx, const unsigned char* in,
} }
#endif #endif
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
if (ret == 1) {
wolfSSL_CTX_load_system_CA_certs(ctx);
}
#endif
WOLFSSL_LEAVE("wolfSSL_CTX_load_verify_buffer_ex", ret); WOLFSSL_LEAVE("wolfSSL_CTX_load_verify_buffer_ex", ret);
return ret; return ret;
} }

View File

@@ -300,6 +300,10 @@
#include <wolfssl/sniffer.h> #include <wolfssl/sniffer.h>
#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
#include <CoreFoundation/CoreFoundation.h>
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -4242,6 +4246,10 @@ struct WOLFSSL_CTX {
#if defined(WOLFSSL_SYS_CRYPTO_POLICY) #if defined(WOLFSSL_SYS_CRYPTO_POLICY)
int secLevel; /* The security level of system-wide crypto policy. */ int secLevel; /* The security level of system-wide crypto policy. */
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */ #endif /* WOLFSSL_SYS_CRYPTO_POLICY */
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
CFMutableArrayRef testTrustedCAs;
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
}; };
WOLFSSL_LOCAL WOLFSSL_LOCAL
@@ -4278,6 +4286,13 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif #endif
#endif #endif
#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
WOLFSSL_API
int wolfSSL_TestAppleNativeCertValidation_AppendCA(WOLFSSL_CTX* ctx,
const byte* derCert,
int derLen);
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
/* All cipher suite related info /* All cipher suite related info
* Keep as a constant size (no ifdefs) for session export */ * Keep as a constant size (no ifdefs) for session export */
typedef struct CipherSpecs { typedef struct CipherSpecs {