diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index b22e94a0e..fd857b9b0 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -1343,8 +1344,7 @@ rdpTls* tls_new(rdpSettings* settings) if (!tls) return NULL; - SSL_load_error_strings(); - SSL_library_init(); + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); tls->settings = settings; tls->certificate_store = certificate_store_new(settings); diff --git a/winpr/include/winpr/ssl.h b/winpr/include/winpr/ssl.h new file mode 100644 index 000000000..647c0e478 --- /dev/null +++ b/winpr/include/winpr/ssl.h @@ -0,0 +1,45 @@ +/** + * WinPR: Windows Portable Runtime + * OpenSSL Library Initialization + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * 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. + */ + +#ifndef WINPR_SSL_H +#define WINPR_SSL_H + +#include + +#define WINPR_SSL_INIT_DEFAULT 0x00 +#define WINPR_SSL_INIT_ALREADY_INITIALIZED 0x01 +#define WINPR_SSL_INIT_ENABLE_LOCKING 0x2 + +#define WINPR_SSL_CLEANUP_GLOBAL 0x01 +#define WINPR_SSL_CLEANUP_THREAD 0x02 + +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API BOOL winpr_InitializeSSL(DWORD flags); +WINPR_API BOOL winpr_CleanupSSL(DWORD flags); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_SSL_H */ + diff --git a/winpr/include/winpr/synch.h b/winpr/include/winpr/synch.h index fce97e9a5..04f4268e4 100644 --- a/winpr/include/winpr/synch.h +++ b/winpr/include/winpr/synch.h @@ -3,7 +3,7 @@ * Synchronization Functions * * Copyright 2012 Marc-Andre Moreau - * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Thincast Technologies GmbH * Copyright 2014 Norbert Federa * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/winpr/libwinpr/sspi/Schannel/schannel_openssl.c b/winpr/libwinpr/sspi/Schannel/schannel_openssl.c index 5ad6e600e..6f7abe806 100644 --- a/winpr/libwinpr/sspi/Schannel/schannel_openssl.c +++ b/winpr/libwinpr/sspi/Schannel/schannel_openssl.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "schannel_openssl.h" @@ -456,10 +457,7 @@ SCHANNEL_OPENSSL* schannel_openssl_new() if (context != NULL) { ZeroMemory(context, sizeof(SCHANNEL_OPENSSL)); - - SSL_load_error_strings(); - SSL_library_init(); - + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); context->connected = FALSE; } diff --git a/winpr/libwinpr/sspi/sspi_winpr.c b/winpr/libwinpr/sspi/sspi_winpr.c index dee803b6b..dcfb4e2b2 100644 --- a/winpr/libwinpr/sspi/sspi_winpr.c +++ b/winpr/libwinpr/sspi/sspi_winpr.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -463,8 +464,7 @@ void sspi_GlobalInit() { if (!sspi_initialized) { - SSL_load_error_strings(); - SSL_library_init(); + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); sspi_ContextBufferAllocTableNew(); sspi_initialized = TRUE; diff --git a/winpr/libwinpr/synch/init.c b/winpr/libwinpr/synch/init.c index 89c6427aa..5f81cf802 100644 --- a/winpr/libwinpr/synch/init.c +++ b/winpr/libwinpr/synch/init.c @@ -3,7 +3,7 @@ * Synchronization Functions * * Copyright 2012 Marc-Andre Moreau - * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Thincast Technologies GmbH * Copyright 2014 Norbert Federa * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/winpr/libwinpr/utils/CMakeLists.txt b/winpr/libwinpr/utils/CMakeLists.txt index 9756a61f4..d8b758bbe 100644 --- a/winpr/libwinpr/utils/CMakeLists.txt +++ b/winpr/libwinpr/utils/CMakeLists.txt @@ -77,7 +77,8 @@ set(${MODULE_PREFIX}_SRCS ntlm.c print.c stream.c - cmdline.c) + cmdline.c + ssl.c) winpr_module_add(${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_COLLECTIONS_SRCS} diff --git a/winpr/libwinpr/utils/ssl.c b/winpr/libwinpr/utils/ssl.c new file mode 100644 index 000000000..2a0463977 --- /dev/null +++ b/winpr/libwinpr/utils/ssl.c @@ -0,0 +1,275 @@ +/** + * WinPR: Windows Portable Runtime + * OpenSSL Library Initialization + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa + * + * 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 +#include +#include + +#include +#include + +static int g_winpr_openssl_num_locks = 0; +static HANDLE* g_winpr_openssl_locks = NULL; +static BOOL g_winpr_openssl_initialized_by_winpr = FALSE; + +struct CRYPTO_dynlock_value +{ + HANDLE mutex; +}; + + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) +static unsigned long _winpr_openssl_id() +{ + 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 = (struct CRYPTO_dynlock_value *) + malloc(sizeof(struct CRYPTO_dynlock_value)); + + if (dynlock) { + dynlock->mutex = CreateMutex(NULL, FALSE, 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() +{ + int i, count; + + /* OpenSSL static locking */ + + if (CRYPTO_get_locking_callback()) + { + fprintf(stderr, "%s: warning: OpenSSL static locking callback is already set\n", __FUNCTION__); + } + else + { + if ((count = CRYPTO_num_locks()) > 0) + { + HANDLE *locks; + if (!(locks = calloc(count, sizeof(HANDLE)))) + { + fprintf(stderr, "%s: error allocating lock table\n", __FUNCTION__); + return FALSE; + } + + for (i = 0; i < count; i++) + { + if (!(locks[i] = CreateMutex(NULL, FALSE, NULL))) + { + fprintf(stderr, "%s: error creating lock #%d\n", __FUNCTION__, i); + while (i--) + { + CloseHandle(g_winpr_openssl_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()) + { + fprintf(stderr, "%s: warning: dynamic locking callbacks are already set\n", __FUNCTION__); + } + 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) + if (CRYPTO_get_id_callback()) + { + fprintf(stderr, "%s: warning OpenSSL id_callback is already set\n", __FUNCTION__); + } + else + { + CRYPTO_set_id_callback(_winpr_openssl_id); + } +#endif + + return TRUE; +} + +static BOOL _winpr_openssl_cleanup_locking() +{ + /* 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) + if (CRYPTO_get_id_callback() == _winpr_openssl_id) + { + CRYPTO_set_id_callback(NULL); + } +#endif + + return TRUE; +} + +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; + } + + if (flags & WINPR_SSL_INIT_ENABLE_LOCKING) + { + if (!_winpr_openssl_initialize_locking(FALSE)) + { + return FALSE; + } + } + + /* SSL_load_error_strings() is void */ + SSL_load_error_strings(); + + /* SSL_library_init() always returns "1" */ + SSL_library_init(); + + g_winpr_openssl_initialized_by_winpr = TRUE; + + return TRUE; +} + + +/* exported functions */ + +BOOL winpr_InitializeSSL(DWORD flags) +{ + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + return InitOnceExecuteOnce(&once, _winpr_openssl_initialize, &flags, NULL); +} + +BOOL winpr_CleanupSSL(DWORD flags) +{ + if (flags & WINPR_SSL_CLEANUP_GLOBAL) + { + if (!g_winpr_openssl_initialized_by_winpr) + { + fprintf(stderr, "%s: warning: ssl was not initialized by winpr\n", __FUNCTION__); + return FALSE; + } + g_winpr_openssl_initialized_by_winpr = FALSE; + _winpr_openssl_cleanup_locking(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + EVP_cleanup(); + flags |= WINPR_SSL_CLEANUP_THREAD; + } + + if (flags & WINPR_SSL_CLEANUP_THREAD) + { +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) + ERR_remove_state(0); +#else + ERR_remove_thread_state(NULL); +#endif + } + + return TRUE; +}