diff --git a/src/internal.c b/src/internal.c index 17a4001dd..b8c90b981 100644 --- a/src/internal.c +++ b/src/internal.c @@ -2915,6 +2915,12 @@ void SSL_CtxResourceFree(WOLFSSL_CTX* ctx) ctx->x509Chain = NULL; } #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 */ #ifdef HAVE_TLS_EXTENSIONS @@ -42777,6 +42783,79 @@ cleanup: 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 @@ -42793,13 +42872,13 @@ cleanup: * wolfSSL's built-in certificate validation mechanisms anymore. We instead * must call into the Security Framework APIs to authenticate peer certificates */ -static int DoAppleNativeCertValidation(WOLFSSL* ssl, - const WOLFSSL_BUFFER_INFO* certs, - int totalCerts) +static int DoAppleNativeCertValidation(WOLFSSL* ssl, + const WOLFSSL_BUFFER_INFO* certs, + int totalCerts) { - int i; - int ret; - OSStatus status; + int i; + int ret; + OSStatus status; CFMutableArrayRef certArray = NULL; SecCertificateRef secCert = NULL; SecTrustRef trust = NULL; @@ -42808,8 +42887,7 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl, WOLFSSL_ENTER("DoAppleNativeCertValidation"); - certArray = CFArrayCreateMutable(kCFAllocatorDefault, - totalCerts, + certArray = CFArrayCreateMutable(kCFAllocatorDefault, totalCerts, &kCFTypeArrayCallBacks); if (!certArray) { WOLFSSL_MSG("Error: can't allocate CFArray for certificates"); @@ -42818,8 +42896,8 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl, } for (i = 0; i < totalCerts; i++) { - secCert = ConvertToSecCertificateRef(certs[i].buffer, - (int)certs[i].length); + secCert = + ConvertToSecCertificateRef(certs[i].buffer, (int)certs[i].length); if (!secCert) { WOLFSSL_MSG("Error: can't convert DER cert to SecCertificateRef"); ret = 0; @@ -42833,35 +42911,74 @@ static int DoAppleNativeCertValidation(WOLFSSL* ssl, } /* Create trust object for SecCertifiate Ref */ - if (ssl->buffers.domainName.buffer && - ssl->buffers.domainName.length > 0) { + if (ssl->buffers.domainName.buffer && ssl->buffers.domainName.length > 0) { /* Create policy with specified value to require host name match */ - hostname = CFStringCreateWithCString(kCFAllocatorDefault, - (const char*)ssl->buffers.domainName.buffer, - kCFStringEncodingUTF8); + hostname = CFStringCreateWithCString( + kCFAllocatorDefault, (const char*)ssl->buffers.domainName.buffer, + kCFStringEncodingUTF8); } if (hostname != NULL) { policy = SecPolicyCreateSSL(true, hostname); - } else { + } + else { policy = SecPolicyCreateSSL(true, NULL); } status = SecTrustCreateWithCertificates(certArray, policy, &trust); if (status != errSecSuccess) { WOLFSSL_MSG_EX("Error creating trust object, " - "SecTrustCreateWithCertificates returned %d",status); + "SecTrustCreateWithCertificates returned %d", + status); ret = 0; 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 */ - if (SecTrustEvaluateWithError(trust, NULL) == 1) { - WOLFSSL_MSG("Cert chain is trusted"); - ret = 1; + WOLFSSL_MSG("Performing Apple native cert validation via " + "SecTrustEvaluateWithError"); + 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 { - WOLFSSL_MSG("Cert chain trust evaluation failed" - "SecTrustEvaluateWithError returned 0"); - ret = 0; + WOLFSSL_MSG("SecTrustEvaluateWithError succeeded"); } /* Cleanup */ @@ -42883,6 +43000,38 @@ cleanup: 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) */ #undef ERROR_OUT diff --git a/src/ssl_load.c b/src/ssl_load.c index d803b4093..1a380391b 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -42,9 +42,14 @@ #endif #endif -#if defined(__APPLE__) && defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) +#if defined(__APPLE__) +#if defined(HAVE_SECURITY_SECTRUSTSETTINGS_H) #include -#endif +#endif /* HAVE_SECURITY_SECTRUSTSETTINGS_H */ +#if WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION +#include +#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */ +#endif /* __APPLE__ */ #endif /* WOLFSSL_SYS_CA_CERTS */ @@ -2153,8 +2158,40 @@ static int ProcessBufferCertHandleDer(WOLFSSL_CTX* ctx, WOLFSSL* ssl, /* CA certificate to verify with. */ 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 */ 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) { ret = 0; } @@ -2978,6 +3015,12 @@ int wolfSSL_CTX_load_verify_locations_ex(WOLFSSL_CTX* ctx, const char* file, ret = NOT_COMPILED_IN; (void)flags; #endif + +#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION + if (ret == 1) { + wolfSSL_CTX_load_system_CA_certs(ctx); + } +#endif } return ret; @@ -3422,6 +3465,12 @@ int wolfSSL_CTX_der_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, 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 WS_RC(ret); } @@ -3950,6 +3999,12 @@ int wolfSSL_CTX_load_verify_buffer_ex(WOLFSSL_CTX* ctx, const unsigned char* in, } #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); return ret; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ca9d4620b..0caf390c2 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -300,6 +300,10 @@ #include #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */ +#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION + #include +#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */ + #ifdef __cplusplus extern "C" { #endif @@ -4242,6 +4246,10 @@ struct WOLFSSL_CTX { #if defined(WOLFSSL_SYS_CRYPTO_POLICY) int secLevel; /* The security level of system-wide 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 @@ -4278,6 +4286,13 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #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 * Keep as a constant size (no ifdefs) for session export */ typedef struct CipherSpecs {