diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/utils/ssl.c | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/utils/ssl.c')
-rw-r--r-- | winpr/libwinpr/utils/ssl.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/ssl.c b/winpr/libwinpr/utils/ssl.c new file mode 100644 index 0000000..8764969 --- /dev/null +++ b/winpr/libwinpr/utils/ssl.c @@ -0,0 +1,447 @@ +/** + * 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 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 (int 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) + { + CRYPTO_set_locking_callback(NULL); + + for (int 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 |