diff --git a/CMakeLists.txt b/CMakeLists.txt index 800d76cbf..b4cb2bfc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,18 @@ if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang") set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") endif() +if(APPLE) + find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation) + if(NOT CORE_FOUNDATION_FRAMEWORK) + message(FATAL_ERROR "Couldn't find CoreFoundation framework.") + endif() + + find_library(SECURITY_FRAMEWORK Security) + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Couldn't find Security framework.") + endif() +endif() + include(CheckIncludeFile) check_include_file("arpa/inet.h" HAVE_ARPA_INET_H) @@ -1526,6 +1538,10 @@ endif() # TODO: - Fast huge math +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_X86_64_BUILD") +endif() + # SP math all add_option("WOLFSSL_SP_MATH_ALL" "Enable Single Precision math implementation for full algorithm suite (default: enabled)" @@ -1900,6 +1916,10 @@ if(WIN32) # For Windows link ws2_32 target_link_libraries(wolfssl PUBLIC $<$:ws2_32>) +elseif(APPLE) + target_link_libraries(wolfssl PUBLIC + ${CORE_FOUNDATION_FRAMEWORK} + ${SECURITY_FRAMEWORK}) else() # DH requires math (m) library target_link_libraries(wolfssl diff --git a/configure.ac b/configure.ac index c61505198..0fd523e95 100644 --- a/configure.ac +++ b/configure.ac @@ -940,7 +940,6 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_ISSUER_NAMES" fi - # liboqs ENABLED_LIBOQS="no" tryliboqsdir="" @@ -7985,13 +7984,12 @@ then AM_CFLAGS="$AM_CFLAGS -DHAVE___UINT128_T=1" fi - LIB_SOCKET_NSL AX_HARDEN_CC_COMPILER_FLAGS -# if mingw then link to ws2_32 for sockets case $host_os in mingw*) + # if mingw then link to ws2_32 for sockets LDFLAGS="$LDFLAGS -lws2_32" if test "$enable_shared" = "yes" then @@ -8001,6 +7999,10 @@ case $host_os in MINGW_LIB_WARNING="yes" fi fi ;; + *darwin*) + # For Mac we need these frameworks to load system CA certs + LDFLAGS="$LDFLAGS -framework CoreFoundation -framework Security" + ;; esac if test "$enable_shared" = "no"; then diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index cb55ba00a..2f622f31d 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -1267,12 +1267,13 @@ const char** wolfSSL_get_system_CA_dirs(word32* num); \ingroup CertsKeys \brief This function attempts to load CA certificates into a WOLFSSL_CTX - from conventional CA cert directories, which is OS-dependent. + from an OS-dependent CA certificate store. Loaded certificates will be + trusted. \return WOLFSSL_SUCCESS on success. \return WOLFSSL_BAD_PATH if no system CA certs were loaded. - \return WOLFSSL_NOT_IMPLEMENTED if the function isn't supported for the - target OS. + \return WOLFSSL_FAILURE for other failure types (e.g. Windows cert store + wasn't properly closed). \param ctx pointer to the SSL context, created with wolfSSL_CTX_new(). diff --git a/src/ssl.c b/src/ssl.c index 37f8aef27..13e7cb6a8 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -162,6 +162,15 @@ #endif #endif /* !WOLFCRYPT_ONLY || OPENSSL_EXTRA */ +#ifdef _WIN32 +#include +#pragma comment(lib, "crypt32") +#endif + +#ifdef __APPLE__ +# include +#endif + /* * OPENSSL_COMPATIBLE_DEFAULTS: * Enable default behaviour that is compatible with OpenSSL. For example @@ -8041,7 +8050,119 @@ int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, return WS_RETURN_CODE(ret,WOLFSSL_FAILURE); } -#ifndef _WIN32 +#ifdef USE_WINDOWS_API + +static int LoadSystemCaCertsWindows(WOLFSSL_CTX* ctx, byte* loaded) +{ + int ret = WOLFSSL_SUCCESS; + word32 i; + HANDLE handle = NULL; + PCCERT_CONTEXT certCtx = NULL; + LPCSTR storeNames[2] = {"ROOT", "CA"}; + HCRYPTPROV_LEGACY hProv = (HCRYPTPROV_LEGACY)NULL; + + if (ctx == NULL || loaded == NULL) { + ret = WOLFSSL_FAILURE; + } + + for (i = 0; ret == WOLFSSL_SUCCESS && + i < sizeof(storeNames)/sizeof(*storeNames); ++i) { + handle = CertOpenSystemStoreA(hProv, storeNames[i]); + if (handle != NULL) { + while (certCtx = CertEnumCertificatesInStore(handle, + certCtx)) { + if (certCtx->dwCertEncodingType == X509_ASN_ENCODING) { + if (ProcessBuffer(ctx, certCtx->pbCertEncoded, + certCtx->cbCertEncoded, WOLFSSL_FILETYPE_ASN1, + CA_TYPE, NULL, NULL, 0, + GET_VERIFY_SETTING_CTX(ctx)) == WOLFSSL_SUCCESS) { + /* + * Set "loaded" as long as we've loaded one CA + * cert. + */ + *loaded = 1; + } + } + } + } + else { + WOLFSSL_MSG_EX("Failed to open cert store %s.", storeNames[i]); + } + + if (handle != NULL && !CertCloseStore(handle, 0)) { + WOLFSSL_MSG_EX("Failed to close cert store %s.", storeNames[i]); + ret = WOLFSSL_FAILURE; + } + } + + return ret; +} + +#elif defined(__APPLE__) + +static int LoadSystemCaCertsMac(WOLFSSL_CTX* ctx, byte* loaded) +{ + int ret = WOLFSSL_SUCCESS; + word32 i; + const unsigned int trustDomains[] = { + kSecTrustSettingsDomainUser, + kSecTrustSettingsDomainAdmin, + kSecTrustSettingsDomainSystem + }; + CFArrayRef certs; + OSStatus stat; + CFIndex numCerts; + CFDataRef der; + CFIndex j; + + if (ctx == NULL || loaded == NULL) { + ret = WOLFSSL_FAILURE; + } + + for (i = 0; ret == WOLFSSL_SUCCESS && + i < sizeof(trustDomains)/sizeof(*trustDomains); ++i) { + stat = SecTrustSettingsCopyCertificates(trustDomains[i], &certs); + + if (stat == errSecSuccess) { + numCerts = CFArrayGetCount(certs); + for (j = 0; j < numCerts; ++j) { + der = SecCertificateCopyData((SecCertificateRef) + CFArrayGetValueAtIndex(certs, j)); + if (der != NULL) { + if (ProcessBuffer(ctx, CFDataGetBytePtr(der), + CFDataGetLength(der), WOLFSSL_FILETYPE_ASN1, + CA_TYPE, NULL, NULL, 0, + GET_VERIFY_SETTING_CTX(ctx)) == WOLFSSL_SUCCESS) { + /* + * Set "loaded" as long as we've loaded one CA + * cert. + */ + *loaded = 1; + } + + CFRelease(der); + } + } + + CFRelease(certs); + } + else if (stat == errSecNoTrustSettings) { + WOLFSSL_MSG_EX("No trust settings for domain %d, moving to next " + "domain.", trustDomains[i]); + } + else { + WOLFSSL_MSG_EX("SecTrustSettingsCopyCertificates failed with" + " status %d.", stat); + ret = WOLFSSL_FAILURE; + break; + } + } + + return ret; +} + +#else + /* Potential system CA certs directories on Linux distros. */ static const char* systemCaDirs[] = { "/etc/ssl/certs", /* Debian, Ubuntu, Gentoo, others */ @@ -8063,53 +8184,61 @@ const char** wolfSSL_get_system_CA_dirs(word32* num) return ret; } -#endif /* !_WIN32 */ + +static int LoadSystemCaCertsNix(WOLFSSL_CTX* ctx, byte* loaded) { + int ret = WOLFSSL_SUCCESS; + word32 i; + + if (ctx == NULL || loaded == NULL) { + ret = WOLFSSL_FAILURE; + } + + for (i = 0; ret == WOLFSSL_SUCCESS && + i < sizeof(systemCaDirs)/sizeof(*systemCaDirs); ++i) { + WOLFSSL_MSG_EX("Attempting to load system CA certs from %s.", + systemCaDirs[i]); + /* + * We want to keep trying to load more CAs even if one cert in + * the directory is bad and can't be used (e.g. if one is expired), + * so we use WOLFSSL_LOAD_FLAG_IGNORE_ERR. + */ + if (wolfSSL_CTX_load_verify_locations_ex(ctx, NULL, systemCaDirs[i], + WOLFSSL_LOAD_FLAG_IGNORE_ERR) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG_EX("Failed to load CA certs from %s, trying " + "next possible location.", systemCaDirs[i]); + } + else { + WOLFSSL_MSG_EX("Loaded CA certs from %s.", + systemCaDirs[i]); + *loaded = 1; + /* Stop searching after we've loaded one directory. */ + break; + } + } + + return ret; +} + +#endif int wolfSSL_CTX_load_system_CA_certs(WOLFSSL_CTX* ctx) { int ret; -#ifndef _WIN32 - word32 i; byte loaded = 0; -#endif WOLFSSL_ENTER("wolfSSL_CTX_load_system_CA_certs"); -#ifdef _WIN32 - (void)ctx; - ret = WOLFSSL_NOT_IMPLEMENTED; +#ifdef USE_WINDOWS_API + ret = LoadSystemCaCertsWindows(ctx, &loaded); +#elif defined(__APPLE__) + ret = LoadSystemCaCertsMac(ctx, &loaded); #else - if (ctx != NULL) { - for (i = 0; i < sizeof(systemCaDirs)/sizeof(*systemCaDirs); ++i) { - WOLFSSL_MSG_EX("Attempting to load system CA certs from %s.", - systemCaDirs[i]); - /* - * We want to keep trying to load more CAs even if one cert in - * the directory is bad and can't be used (e.g. if one is expired), - * so we use WOLFSSL_LOAD_FLAG_IGNORE_ERR. - */ - if (wolfSSL_CTX_load_verify_locations_ex(ctx, NULL, systemCaDirs[i], - WOLFSSL_LOAD_FLAG_IGNORE_ERR) != WOLFSSL_SUCCESS) { - WOLFSSL_MSG_EX("Failed to load CA certs from %s, trying " - "next possible location.", systemCaDirs[i]); - } - else { - WOLFSSL_MSG_EX("Loaded CA certs from %s.", - systemCaDirs[i]); - loaded = 1; - /* Stop searching after we've loaded one directory. */ - break; - } - } - } + ret = LoadSystemCaCertsNix(ctx, &loaded); +#endif - if (loaded) { - ret = WOLFSSL_SUCCESS; - } - else { + if (ret == WOLFSSL_SUCCESS && !loaded) { ret = WOLFSSL_BAD_PATH; } -#endif WOLFSSL_LEAVE("wolfSSL_CTX_load_system_CA_certs", ret); @@ -16249,13 +16378,6 @@ cleanup: */ ret = WOLFSSL_SUCCESS; } - else if (ret != WOLFSSL_SUCCESS) { - /* - * All other failure types map to WOLFSSL_FAILURE (0), same as - * OpenSSL. - */ - ret = WOLFSSL_FAILURE; - } WOLFSSL_LEAVE("wolfSSL_CTX_set_default_verify_paths", ret); diff --git a/tests/api.c b/tests/api.c index 12b7f099a..e7d5b4aac 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1352,31 +1352,26 @@ static int test_wolfSSL_CTX_load_system_CA_certs(void) #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) WOLFSSL_CTX* ctx; + byte dirValid = 0; ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); if (ctx == NULL) { + fprintf(stderr, "wolfSSL_CTX_new failed.\n"); ret = -1; } if (ret == 0) { -#ifdef _WIN32 - if (wolfSSL_CTX_load_system_CA_certs(ctx) != WOLFSSL_NOT_IMPLEMENTED) { - ret = -1; - } -#ifdef OPENSSL_EXTRA - if (wolfSSL_CTX_set_default_verify_paths(ctx) != WOLFSSL_FAILURE) { - ret = -1; - } -#endif /* OPENSSL_EXTRA */ -#else + #if defined(USE_WINDOWS_API) || defined(__APPLE__) + dirValid = 1; + #else word32 numDirs; const char** caDirs = wolfSSL_get_system_CA_dirs(&numDirs); if (caDirs == NULL || numDirs == 0) { + fprintf(stderr, "wolfSSL_get_system_CA_dirs failed.\n"); ret = -1; } else { ReadDirCtx dirCtx; - byte dirValid = 0; word32 i; for (i = 0; i < numDirs; ++i) { @@ -1387,29 +1382,26 @@ static int test_wolfSSL_CTX_load_system_CA_certs(void) break; } } - - /* - * If the directory isn't empty, we should be able to load CA - * certs from it. - */ - if (dirValid && wolfSSL_CTX_load_system_CA_certs(ctx) != - WOLFSSL_SUCCESS) { - ret = -1; - } - #ifdef OPENSSL_EXTRA - /* - * Even if we don't have a valid directory to load system CA - * certs from, the OpenSSL compat layer function should return - * success. - */ - if (wolfSSL_CTX_set_default_verify_paths(ctx) - != WOLFSSL_SUCCESS) { - ret = -1; - } - #endif /* OPENSSL_EXTRA */ } -#endif /* _WIN32 */ + #endif } + /* + * If the directory isn't empty, we should be able to load CA + * certs from it. On Windows/Mac, we assume the CA cert stores are + * usable. + */ + if (ret == 0 && dirValid && wolfSSL_CTX_load_system_CA_certs(ctx) != + WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_CTX_load_system_CA_certs failed.\n"); + ret = -1; + } +#ifdef OPENSSL_EXTRA + if (ret == 0 && + wolfSSL_CTX_set_default_verify_paths(ctx) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_CTX_set_default_verify_paths failed.\n"); + ret = -1; + } +#endif /* OPENSSL_EXTRA */ wolfSSL_CTX_free(ctx); #endif /* !NO_FILESYSTEM && !NO_CERTS && !NO_WOLFSSL_CLIENT */