diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index f5d9532a0..cb55ba00a 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -1231,6 +1231,69 @@ int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, int wolfSSL_CTX_load_verify_locations_ex(WOLFSSL_CTX* ctx, const char* file, const char* path, unsigned int flags); +/*! + \ingroup CertsKeys + + \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. + + \return Valid pointer on success. + \return NULL pointer on failure. + + \param num pointer to a word32 that will be populated with the length of the + array of strings. + + _Example_ + \code + WOLFSSL_CTX* ctx; + const char** dirs; + word32 numDirs; + + dirs = wolfSSL_get_system_CA_dirs(&numDirs); + for (int i = 0; i < numDirs; ++i) { + printf("Potential system CA dir: %s\n", dirs[i]); + } + ... + \endcode + + \sa wolfSSL_CTX_load_system_CA_certs + \sa wolfSSL_CTX_load_verify_locations + \sa wolfSSL_CTX_load_verify_locations_ex +*/ +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. + + \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. + + \param ctx pointer to the SSL context, created with wolfSSL_CTX_new(). + + _Example_ + \code + int ret = 0; + WOLFSSL_CTX* ctx; + ... + ret = wolfSSL_CTX_load_system_CA_certs(ctx,); + if (ret != WOLFSSL_SUCCESS) { + // error loading system CA certs + } + ... + \endcode + + \sa wolfSSL_get_system_CA_dirs + \sa wolfSSL_CTX_load_verify_locations + \sa wolfSSL_CTX_load_verify_locations_ex +*/ +int wolfSSL_CTX_load_system_CA_certs(WOLFSSL_CTX* ctx); + /*! \ingroup Setup diff --git a/src/ssl.c b/src/ssl.c index 00b156fb6..36823fcdb 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8041,6 +8041,80 @@ int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, return WS_RETURN_CODE(ret,WOLFSSL_FAILURE); } +#ifndef _WIN32 +/* Potential system CA certs directories on Linux distros. */ +static const char* systemCaDirs[] = { + "/etc/ssl/certs", /* Debian, Ubuntu, Gentoo, others */ + "/etc/pki/ca-trust/source/anchors", /* Fedora, RHEL */ + "/etc/pki/tls/certs" /* Older RHEL */ +}; + +const char** wolfSSL_get_system_CA_dirs(word32* num) +{ + const char** ret; + + if (num == NULL) { + ret = NULL; + } + else { + ret = systemCaDirs; + *num = sizeof(systemCaDirs)/sizeof(*systemCaDirs); + } + + return ret; +} +#endif /* !_WIN32 */ + +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; +#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; + } + } + } + + if (loaded) { + ret = WOLFSSL_SUCCESS; + } + else { + ret = WOLFSSL_BAD_PATH; + } +#endif + + WOLFSSL_LEAVE("wolfSSL_CTX_load_system_CA_certs", ret); + + return ret; +} #ifdef WOLFSSL_TRUST_PEER_CERT /* Used to specify a peer cert to match when connecting @@ -15961,15 +16035,42 @@ cleanup: #ifdef OPENSSL_EXTRA - #ifndef NO_WOLFSSL_STUB + #ifndef NO_FILESYSTEM + /* + * This is an OpenSSL compatibility layer function, but it doesn't mirror + * the exact functionality of its OpenSSL counterpart. We don't support the + * notion of an "OpenSSL directory," nor do we support the environment + * variables SSL_CERT_DIR or SSL_CERT_FILE. This function is simply a + * wrapper around our native wolfSSL_CTX_load_system_CA_certs function. This + * function does conform to OpenSSL's return value conventions, though. + */ int wolfSSL_CTX_set_default_verify_paths(WOLFSSL_CTX* ctx) { - /* TODO:, not needed in goahead */ - (void)ctx; - WOLFSSL_STUB("SSL_CTX_set_default_verify_paths"); - return SSL_NOT_IMPLEMENTED; + int ret; + + WOLFSSL_ENTER("wolfSSL_CTX_set_default_verify_paths"); + + ret = wolfSSL_CTX_load_system_CA_certs(ctx); + if (ret == WOLFSSL_BAD_PATH) { + /* + * OpenSSL doesn't treat the lack of a system CA cert directory as a + * failure. We do the same here. + */ + 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); + + return ret; } - #endif + #endif /* !NO_FILESYSTEM */ #if defined(WOLFCRYPT_HAVE_SRP) && !defined(NO_SHA256) \ && !defined(WC_NO_RNG) diff --git a/tests/api.c b/tests/api.c index 8eb06a988..5f529338d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1346,6 +1346,77 @@ static int test_wolfSSL_CTX_load_verify_locations(void) return 0; } +static int test_wolfSSL_CTX_load_system_CA_certs(void) +{ + int ret = 0; + +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx; + + ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (ctx == NULL) { + 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 + word32 numDirs; + const char** caDirs = wolfSSL_get_system_CA_dirs(&numDirs); + + if (caDirs == NULL || numDirs == 0) { + ret = -1; + } + else { + ReadDirCtx dirCtx; + byte dirValid = 0; + word32 i; + + for (i = 0; i < numDirs; ++i) { + if (wc_ReadDirFirst(&dirCtx, caDirs[i], NULL) == 0) { + /* Directory isn't empty. */ + dirValid = 1; + wc_ReadDirClose(&dirCtx); + 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 */ + } + + wolfSSL_CTX_free(ctx); +#endif /* !NO_FILESYSTEM && !NO_CERTS && !NO_WOLFSSL_CLIENT */ + + return ret; +} + #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) static int test_cm_load_ca_buffer(const byte* cert_buf, size_t cert_sz, int file_type) { @@ -58611,6 +58682,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_CTX_use_certificate_buffer), TEST_DECL(test_wolfSSL_CTX_use_PrivateKey_file), TEST_DECL(test_wolfSSL_CTX_load_verify_locations), + TEST_DECL(test_wolfSSL_CTX_load_system_CA_certs), TEST_DECL(test_wolfSSL_CertManagerCheckOCSPResponse), TEST_DECL(test_wolfSSL_CheckOCSPResponse), TEST_DECL(test_wolfSSL_CertManagerLoadCABuffer), diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index f3868dd76..88a6e95b5 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -267,6 +267,7 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #else #define SSL_CTX_load_verify_locations wolfSSL_CTX_load_verify_locations #endif + #define SSL_CTX_set_default_verify_paths wolfSSL_CTX_set_default_verify_paths #define SSL_CTX_use_certificate_chain_file wolfSSL_CTX_use_certificate_chain_file #define SSL_CTX_use_RSAPrivateKey_file wolfSSL_CTX_use_RSAPrivateKey_file diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 9e6682588..c87315ef7 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1070,6 +1070,13 @@ WOLFSSL_API int wolfSSL_CTX_load_verify_locations_ex( WOLFSSL_CTX* ctx, const char* file, const char* path, word32 flags); WOLFSSL_ABI WOLFSSL_API int wolfSSL_CTX_load_verify_locations( WOLFSSL_CTX* ctx, const char* file, const char* path); +#ifndef _WIN32 +WOLFSSL_API const char** wolfSSL_get_system_CA_dirs(word32* num); +#endif /* !_WIN32 */ +WOLFSSL_API int wolfSSL_CTX_load_system_CA_certs(WOLFSSL_CTX* ctx); +#ifdef OPENSSL_EXTRA +WOLFSSL_API int wolfSSL_CTX_set_default_verify_paths(WOLFSSL_CTX* ctx); +#endif /* OPENSSL_EXTRA */ #ifdef WOLFSSL_TRUST_PEER_CERT WOLFSSL_API int wolfSSL_CTX_trust_peer_cert( WOLFSSL_CTX* ctx, const char* file, int type);