ba98aa0a8c
C11 atexit cleanup handler are used to call cleanup routines for WLog and SSL on program termination. This removes false positives when using ASAN runtime memory debugger
449 lines
10 KiB
C
449 lines
10 KiB
C
/**
|
|
* WinPR: Windows Portable Runtime
|
|
* OpenSSL Library Initialization
|
|
*
|
|
* Copyright 2014 Thincast Technologies GmbH
|
|
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <winpr/config.h>
|
|
|
|
#include <winpr/crt.h>
|
|
#include <winpr/synch.h>
|
|
#include <winpr/ssl.h>
|
|
#include <winpr/thread.h>
|
|
#include <winpr/crypto.h>
|
|
|
|
#ifdef WITH_OPENSSL
|
|
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
#include <openssl/provider.h>
|
|
#endif
|
|
|
|
#include "../log.h"
|
|
#define TAG WINPR_TAG("utils.ssl")
|
|
|
|
static BOOL g_winpr_openssl_initialized_by_winpr = FALSE;
|
|
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
static OSSL_PROVIDER* s_winpr_openssl_provider_fips = NULL;
|
|
static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = NULL;
|
|
static OSSL_PROVIDER* s_winpr_openssl_provider_default = NULL;
|
|
#endif
|
|
|
|
/**
|
|
* Note from OpenSSL 1.1.0 "CHANGES":
|
|
* OpenSSL now uses a new threading API. It is no longer necessary to
|
|
* set locking callbacks to use OpenSSL in a multi-threaded environment.
|
|
*/
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
#define WINPR_OPENSSL_LOCKING_REQUIRED 1
|
|
|
|
static int g_winpr_openssl_num_locks = 0;
|
|
static HANDLE* g_winpr_openssl_locks = NULL;
|
|
|
|
struct CRYPTO_dynlock_value
|
|
{
|
|
HANDLE mutex;
|
|
};
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
static unsigned long _winpr_openssl_id(void)
|
|
{
|
|
return (unsigned long)GetCurrentThreadId();
|
|
}
|
|
#endif
|
|
|
|
static void _winpr_openssl_locking(int mode, int type, const char* file, int line)
|
|
{
|
|
if (mode & CRYPTO_LOCK)
|
|
{
|
|
WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE);
|
|
}
|
|
else
|
|
{
|
|
ReleaseMutex(g_winpr_openssl_locks[type]);
|
|
}
|
|
}
|
|
|
|
static struct CRYPTO_dynlock_value* _winpr_openssl_dynlock_create(const char* file, int line)
|
|
{
|
|
struct CRYPTO_dynlock_value* dynlock;
|
|
|
|
if (!(dynlock = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value))))
|
|
return NULL;
|
|
|
|
if (!(dynlock->mutex = CreateMutex(NULL, FALSE, NULL)))
|
|
{
|
|
free(dynlock);
|
|
return NULL;
|
|
}
|
|
|
|
return dynlock;
|
|
}
|
|
|
|
static void _winpr_openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value* dynlock,
|
|
const char* file, int line)
|
|
{
|
|
if (mode & CRYPTO_LOCK)
|
|
{
|
|
WaitForSingleObject(dynlock->mutex, INFINITE);
|
|
}
|
|
else
|
|
{
|
|
ReleaseMutex(dynlock->mutex);
|
|
}
|
|
}
|
|
|
|
static void _winpr_openssl_dynlock_destroy(struct CRYPTO_dynlock_value* dynlock, const char* file,
|
|
int line)
|
|
{
|
|
CloseHandle(dynlock->mutex);
|
|
free(dynlock);
|
|
}
|
|
|
|
static BOOL _winpr_openssl_initialize_locking(void)
|
|
{
|
|
int i, count;
|
|
|
|
/* OpenSSL static locking */
|
|
|
|
if (CRYPTO_get_locking_callback())
|
|
{
|
|
WLog_WARN(TAG, "OpenSSL static locking callback is already set");
|
|
}
|
|
else
|
|
{
|
|
if ((count = CRYPTO_num_locks()) > 0)
|
|
{
|
|
HANDLE* locks;
|
|
|
|
if (!(locks = calloc(count, sizeof(HANDLE))))
|
|
{
|
|
WLog_ERR(TAG, "error allocating lock table");
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (!(locks[i] = CreateMutex(NULL, FALSE, NULL)))
|
|
{
|
|
WLog_ERR(TAG, "error creating lock #%d", i);
|
|
|
|
while (i--)
|
|
{
|
|
if (locks[i])
|
|
CloseHandle(locks[i]);
|
|
}
|
|
|
|
free(locks);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
g_winpr_openssl_locks = locks;
|
|
g_winpr_openssl_num_locks = count;
|
|
CRYPTO_set_locking_callback(_winpr_openssl_locking);
|
|
}
|
|
}
|
|
|
|
/* OpenSSL dynamic locking */
|
|
|
|
if (CRYPTO_get_dynlock_create_callback() || CRYPTO_get_dynlock_lock_callback() ||
|
|
CRYPTO_get_dynlock_destroy_callback())
|
|
{
|
|
WLog_WARN(TAG, "dynamic locking callbacks are already set");
|
|
}
|
|
else
|
|
{
|
|
CRYPTO_set_dynlock_create_callback(_winpr_openssl_dynlock_create);
|
|
CRYPTO_set_dynlock_lock_callback(_winpr_openssl_dynlock_lock);
|
|
CRYPTO_set_dynlock_destroy_callback(_winpr_openssl_dynlock_destroy);
|
|
}
|
|
|
|
/* Use the deprecated CRYPTO_get_id_callback() if building against OpenSSL < 1.0.0 */
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
if (CRYPTO_get_id_callback())
|
|
{
|
|
WLog_WARN(TAG, "OpenSSL id_callback is already set");
|
|
}
|
|
else
|
|
{
|
|
CRYPTO_set_id_callback(_winpr_openssl_id);
|
|
}
|
|
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL _winpr_openssl_cleanup_locking(void)
|
|
{
|
|
/* undo our static locking modifications */
|
|
if (CRYPTO_get_locking_callback() == _winpr_openssl_locking)
|
|
{
|
|
int i;
|
|
CRYPTO_set_locking_callback(NULL);
|
|
|
|
for (i = 0; i < g_winpr_openssl_num_locks; i++)
|
|
{
|
|
CloseHandle(g_winpr_openssl_locks[i]);
|
|
}
|
|
|
|
g_winpr_openssl_num_locks = 0;
|
|
free(g_winpr_openssl_locks);
|
|
g_winpr_openssl_locks = NULL;
|
|
}
|
|
|
|
/* unset our dynamic locking callbacks */
|
|
|
|
if (CRYPTO_get_dynlock_create_callback() == _winpr_openssl_dynlock_create)
|
|
{
|
|
CRYPTO_set_dynlock_create_callback(NULL);
|
|
}
|
|
|
|
if (CRYPTO_get_dynlock_lock_callback() == _winpr_openssl_dynlock_lock)
|
|
{
|
|
CRYPTO_set_dynlock_lock_callback(NULL);
|
|
}
|
|
|
|
if (CRYPTO_get_dynlock_destroy_callback() == _winpr_openssl_dynlock_destroy)
|
|
{
|
|
CRYPTO_set_dynlock_destroy_callback(NULL);
|
|
}
|
|
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
|
|
if (CRYPTO_get_id_callback() == _winpr_openssl_id)
|
|
{
|
|
CRYPTO_set_id_callback(NULL);
|
|
}
|
|
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#endif /* OpenSSL < 1.1.0 */
|
|
|
|
static BOOL winpr_enable_fips(DWORD flags)
|
|
{
|
|
if (flags & WINPR_SSL_INIT_ENABLE_FIPS)
|
|
{
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
WLog_ERR(TAG, "Openssl fips mode not available on openssl versions less than 1.0.1!");
|
|
return FALSE;
|
|
#else
|
|
WLog_DBG(TAG, "Ensuring openssl fips mode is enabled");
|
|
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
s_winpr_openssl_provider_fips = OSSL_PROVIDER_load(NULL, "fips");
|
|
if (s_winpr_openssl_provider_fips == NULL)
|
|
{
|
|
WLog_WARN(TAG, "OpenSSL FIPS provider failled to load");
|
|
}
|
|
if (!EVP_default_properties_is_fips_enabled(NULL))
|
|
#else
|
|
if (FIPS_mode() != 1)
|
|
#endif
|
|
{
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
if (EVP_set_default_properties(NULL, "fips=yes"))
|
|
#else
|
|
if (FIPS_mode_set(1))
|
|
#endif
|
|
WLog_INFO(TAG, "Openssl fips mode enabled!");
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "Openssl fips mode enable failed!");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void winpr_openssl_cleanup(void)
|
|
{
|
|
winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT);
|
|
}
|
|
|
|
static BOOL CALLBACK winpr_openssl_initialize(PINIT_ONCE once, PVOID param, PVOID* context)
|
|
{
|
|
DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
|
|
|
|
if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
|
|
|
|
if (flags & WINPR_SSL_INIT_ENABLE_LOCKING)
|
|
{
|
|
if (!_winpr_openssl_initialize_locking())
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
/* SSL_load_error_strings() is void */
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
SSL_load_error_strings();
|
|
/* SSL_library_init() always returns "1" */
|
|
SSL_library_init();
|
|
OpenSSL_add_all_digests();
|
|
OpenSSL_add_all_ciphers();
|
|
#else
|
|
|
|
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
|
|
OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
|
|
OPENSSL_INIT_ENGINE_ALL_BUILTIN,
|
|
NULL) != 1)
|
|
return FALSE;
|
|
|
|
#endif
|
|
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
/* The legacy provider is needed for MD4. */
|
|
s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(NULL, "legacy");
|
|
if (s_winpr_openssl_provider_legacy == NULL)
|
|
{
|
|
WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!");
|
|
}
|
|
s_winpr_openssl_provider_default = OSSL_PROVIDER_load(NULL, "default");
|
|
if (s_winpr_openssl_provider_default == NULL)
|
|
{
|
|
WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load");
|
|
}
|
|
#endif
|
|
|
|
atexit(winpr_openssl_cleanup);
|
|
g_winpr_openssl_initialized_by_winpr = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* exported functions */
|
|
|
|
BOOL winpr_InitializeSSL(DWORD flags)
|
|
{
|
|
static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
|
|
|
|
if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, NULL))
|
|
return FALSE;
|
|
|
|
return winpr_enable_fips(flags);
|
|
}
|
|
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
static int unload(OSSL_PROVIDER* provider, void* data)
|
|
{
|
|
if (!provider)
|
|
return 1;
|
|
const char* name = OSSL_PROVIDER_get0_name(provider);
|
|
if (!name)
|
|
return 1;
|
|
|
|
OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
|
|
const int rc = OSSL_PROVIDER_available(ctx, name);
|
|
if (rc < 1)
|
|
return 1;
|
|
OSSL_PROVIDER_unload(provider);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
BOOL winpr_CleanupSSL(DWORD flags)
|
|
{
|
|
if (flags & WINPR_SSL_CLEANUP_GLOBAL)
|
|
{
|
|
if (!g_winpr_openssl_initialized_by_winpr)
|
|
{
|
|
WLog_WARN(TAG, "ssl was not initialized by winpr");
|
|
return FALSE;
|
|
}
|
|
|
|
g_winpr_openssl_initialized_by_winpr = FALSE;
|
|
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
|
|
_winpr_openssl_cleanup_locking();
|
|
#endif
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
CRYPTO_cleanup_all_ex_data();
|
|
ERR_free_strings();
|
|
EVP_cleanup();
|
|
#endif
|
|
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
|
|
flags |= WINPR_SSL_CLEANUP_THREAD;
|
|
#endif
|
|
}
|
|
|
|
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
|
|
|
|
if (flags & WINPR_SSL_CLEANUP_THREAD)
|
|
{
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
ERR_remove_state(0);
|
|
#else
|
|
ERR_remove_thread_state(NULL);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
|
|
OSSL_PROVIDER_do_all(ctx, unload, NULL);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL winpr_FIPSMode(void)
|
|
{
|
|
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
|
|
return FALSE;
|
|
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
|
return (EVP_default_properties_is_fips_enabled(NULL) == 1);
|
|
#else
|
|
return (FIPS_mode() == 1);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
|
|
BOOL winpr_InitializeSSL(DWORD flags)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL winpr_CleanupSSL(DWORD flags)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL winpr_FIPSMode(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|