Add a function to load system CA certs into a WOLFSSL_CTX.

This new function, wolfSSL_CTX_load_system_CA_certs, currently only supports
Linux-based OS's. It searches through conventional CA directories and once it
finds one, attempts to load CA certs from it. After the first directory is
found, we don't check the others.

This commit also adds a function wolfSSL_get_system_CA_dirs, which returns a
pointer to an array of directories where wolfSSL_CTX_load_system_CA_certs will
look for CA certs. This is used in a unit test, where we only want to expect
success if one of these directories actually exists on the test system.

Finally, this commit adds support for SSL_CTX_set_default_verify_paths to the
compatibility layer. It doesn't model the exact behavior of its OpenSSL
counterpart; it's mostly a wrapper around wolfSSL_CTX_load_system_CA_certs,
manipulating the return value of that function to conform to OpenSSL's
conventions.
This commit is contained in:
Hayden Roche
2022-09-22 15:18:15 -07:00
parent 9d9fa0132e
commit 8cae05348c
5 changed files with 250 additions and 6 deletions

View File

@ -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

113
src/ssl.c
View File

@ -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)

View File

@ -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),

View File

@ -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

View File

@ -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);