FreeRDP
Loading...
Searching...
No Matches
ssl.c
1
21#include <winpr/config.h>
22
23#include <winpr/atexit.h>
24#include <winpr/crt.h>
25#include <winpr/synch.h>
26#include <winpr/ssl.h>
27#include <winpr/thread.h>
28#include <winpr/crypto.h>
29
30#ifdef WITH_OPENSSL
31
32#include <openssl/ssl.h>
33#include <openssl/err.h>
34
35#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
36#include <openssl/provider.h>
37#endif
38
39#include "../log.h"
40#define TAG WINPR_TAG("utils.ssl")
41
42static BOOL g_winpr_openssl_initialized_by_winpr = FALSE;
43
44#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
45static OSSL_PROVIDER* s_winpr_openssl_provider_fips = nullptr;
46static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = nullptr;
47static OSSL_PROVIDER* s_winpr_openssl_provider_default = nullptr;
48#endif
49
56#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
57
58#define WINPR_OPENSSL_LOCKING_REQUIRED 1
59
60static int g_winpr_openssl_num_locks = 0;
61static HANDLE* g_winpr_openssl_locks = nullptr;
62
63struct CRYPTO_dynlock_value
64{
65 HANDLE mutex;
66};
67
68#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
69static unsigned long _winpr_openssl_id(void)
70{
71 return (unsigned long)GetCurrentThreadId();
72}
73#endif
74
75static void _winpr_openssl_locking(int mode, int type, const char* file, int line)
76{
77 if (mode & CRYPTO_LOCK)
78 {
79 (void)WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE);
80 }
81 else
82 {
83 (void)ReleaseMutex(g_winpr_openssl_locks[type]);
84 }
85}
86
87static struct CRYPTO_dynlock_value* _winpr_openssl_dynlock_create(const char* file, int line)
88{
89 struct CRYPTO_dynlock_value* dynlock;
90
91 if (!(dynlock = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value))))
92 return nullptr;
93
94 if (!(dynlock->mutex = CreateMutex(nullptr, FALSE, nullptr)))
95 {
96 free(dynlock);
97 return nullptr;
98 }
99
100 return dynlock;
101}
102
103static void _winpr_openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value* dynlock,
104 const char* file, int line)
105{
106 if (mode & CRYPTO_LOCK)
107 {
108 (void)WaitForSingleObject(dynlock->mutex, INFINITE);
109 }
110 else
111 {
112 (void)ReleaseMutex(dynlock->mutex);
113 }
114}
115
116static void _winpr_openssl_dynlock_destroy(struct CRYPTO_dynlock_value* dynlock, const char* file,
117 int line)
118{
119 (void)CloseHandle(dynlock->mutex);
120 free(dynlock);
121}
122
123static BOOL _winpr_openssl_initialize_locking(void)
124{
125 int count;
126
127 /* OpenSSL static locking */
128
129 if (CRYPTO_get_locking_callback())
130 {
131 WLog_WARN(TAG, "OpenSSL static locking callback is already set");
132 }
133 else
134 {
135 if ((count = CRYPTO_num_locks()) > 0)
136 {
137 HANDLE* locks;
138
139 if (!(locks = calloc(count, sizeof(HANDLE))))
140 {
141 WLog_ERR(TAG, "error allocating lock table");
142 return FALSE;
143 }
144
145 for (int i = 0; i < count; i++)
146 {
147 if (!(locks[i] = CreateMutex(nullptr, FALSE, nullptr)))
148 {
149 WLog_ERR(TAG, "error creating lock #%d", i);
150
151 while (i--)
152 {
153 if (locks[i])
154 (void)CloseHandle(locks[i]);
155 }
156
157 free(locks);
158 return FALSE;
159 }
160 }
161
162 g_winpr_openssl_locks = locks;
163 g_winpr_openssl_num_locks = count;
164 CRYPTO_set_locking_callback(_winpr_openssl_locking);
165 }
166 }
167
168 /* OpenSSL dynamic locking */
169
170 if (CRYPTO_get_dynlock_create_callback() || CRYPTO_get_dynlock_lock_callback() ||
171 CRYPTO_get_dynlock_destroy_callback())
172 {
173 WLog_WARN(TAG, "dynamic locking callbacks are already set");
174 }
175 else
176 {
177 CRYPTO_set_dynlock_create_callback(_winpr_openssl_dynlock_create);
178 CRYPTO_set_dynlock_lock_callback(_winpr_openssl_dynlock_lock);
179 CRYPTO_set_dynlock_destroy_callback(_winpr_openssl_dynlock_destroy);
180 }
181
182 /* Use the deprecated CRYPTO_get_id_callback() if building against OpenSSL < 1.0.0 */
183#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
184
185 if (CRYPTO_get_id_callback())
186 {
187 WLog_WARN(TAG, "OpenSSL id_callback is already set");
188 }
189 else
190 {
191 CRYPTO_set_id_callback(_winpr_openssl_id);
192 }
193
194#endif
195 return TRUE;
196}
197
198static BOOL _winpr_openssl_cleanup_locking(void)
199{
200 /* undo our static locking modifications */
201 if (CRYPTO_get_locking_callback() == _winpr_openssl_locking)
202 {
203 CRYPTO_set_locking_callback(nullptr);
204
205 for (int i = 0; i < g_winpr_openssl_num_locks; i++)
206 {
207 (void)CloseHandle(g_winpr_openssl_locks[i]);
208 }
209
210 g_winpr_openssl_num_locks = 0;
211 free(g_winpr_openssl_locks);
212 g_winpr_openssl_locks = nullptr;
213 }
214
215 /* unset our dynamic locking callbacks */
216
217 if (CRYPTO_get_dynlock_create_callback() == _winpr_openssl_dynlock_create)
218 {
219 CRYPTO_set_dynlock_create_callback(nullptr);
220 }
221
222 if (CRYPTO_get_dynlock_lock_callback() == _winpr_openssl_dynlock_lock)
223 {
224 CRYPTO_set_dynlock_lock_callback(nullptr);
225 }
226
227 if (CRYPTO_get_dynlock_destroy_callback() == _winpr_openssl_dynlock_destroy)
228 {
229 CRYPTO_set_dynlock_destroy_callback(nullptr);
230 }
231
232#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
233
234 if (CRYPTO_get_id_callback() == _winpr_openssl_id)
235 {
236 CRYPTO_set_id_callback(nullptr);
237 }
238
239#endif
240 return TRUE;
241}
242
243#endif /* OpenSSL < 1.1.0 */
244
245static BOOL winpr_enable_fips(DWORD flags)
246{
247 if (flags & WINPR_SSL_INIT_ENABLE_FIPS)
248 {
249#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
250 WLog_ERR(TAG, "Openssl fips mode not available on openssl versions less than 1.0.1!");
251 return FALSE;
252#else
253 WLog_DBG(TAG, "Ensuring openssl fips mode is enabled");
254
255#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
256 s_winpr_openssl_provider_fips = OSSL_PROVIDER_load(nullptr, "fips");
257 if (s_winpr_openssl_provider_fips == nullptr)
258 {
259 WLog_WARN(TAG, "OpenSSL FIPS provider failed to load");
260 }
261 if (!EVP_default_properties_is_fips_enabled(nullptr))
262#else
263 if (FIPS_mode() != 1)
264#endif
265 {
266#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
267 if (EVP_set_default_properties(nullptr, "fips=yes"))
268#else
269 if (FIPS_mode_set(1))
270#endif
271 WLog_INFO(TAG, "Openssl fips mode enabled!");
272 else
273 {
274 WLog_ERR(TAG, "Openssl fips mode enable failed!");
275 return FALSE;
276 }
277 }
278
279#endif
280 }
281
282 return TRUE;
283}
284
285static void winpr_openssl_cleanup(void)
286{
287 winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT);
288}
289
290static BOOL CALLBACK winpr_openssl_initialize(WINPR_ATTR_UNUSED PINIT_ONCE once, PVOID param,
291 WINPR_ATTR_UNUSED PVOID* context)
292{
293 DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
294
295 if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
296 {
297 return TRUE;
298 }
299
300#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
301
302 if (flags & WINPR_SSL_INIT_ENABLE_LOCKING)
303 {
304 if (!_winpr_openssl_initialize_locking())
305 {
306 return FALSE;
307 }
308 }
309
310#endif
311 /* SSL_load_error_strings() is void */
312#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
313 SSL_load_error_strings();
314 /* SSL_library_init() always returns "1" */
315 SSL_library_init();
316 OpenSSL_add_all_digests();
317 OpenSSL_add_all_ciphers();
318#else
319
320 if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
321 OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
322 OPENSSL_INIT_ENGINE_ALL_BUILTIN,
323 nullptr) != 1)
324 return FALSE;
325
326#endif
327
328#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
329 /* The legacy provider is needed for MD4. */
330 s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(nullptr, "legacy");
331 if (s_winpr_openssl_provider_legacy == nullptr)
332 {
333 WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!");
334 }
335 s_winpr_openssl_provider_default = OSSL_PROVIDER_load(nullptr, "default");
336 if (s_winpr_openssl_provider_default == nullptr)
337 {
338 WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load");
339 }
340#endif
341
342 (void)winpr_atexit(winpr_openssl_cleanup);
343 g_winpr_openssl_initialized_by_winpr = TRUE;
344 return TRUE;
345}
346
347/* exported functions */
348
349BOOL winpr_InitializeSSL(DWORD flags)
350{
351 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
352
353 if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, nullptr))
354 return FALSE;
355
356 return winpr_enable_fips(flags);
357}
358
359#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
360static int unload(OSSL_PROVIDER* provider, WINPR_ATTR_UNUSED void* data)
361{
362 if (!provider)
363 return 1;
364 const char* name = OSSL_PROVIDER_get0_name(provider);
365 if (!name)
366 return 1;
367
368 OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
369 const int rc = OSSL_PROVIDER_available(ctx, name);
370 if (rc < 1)
371 return 1;
372 OSSL_PROVIDER_unload(provider);
373 return 1;
374}
375#endif
376
377BOOL winpr_CleanupSSL(DWORD flags)
378{
379 if (flags & WINPR_SSL_CLEANUP_GLOBAL)
380 {
381 if (!g_winpr_openssl_initialized_by_winpr)
382 {
383 WLog_WARN(TAG, "ssl was not initialized by winpr");
384 return FALSE;
385 }
386
387 g_winpr_openssl_initialized_by_winpr = FALSE;
388#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
389 _winpr_openssl_cleanup_locking();
390#endif
391#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
392 CRYPTO_cleanup_all_ex_data();
393 ERR_free_strings();
394 EVP_cleanup();
395#endif
396#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
397 flags |= WINPR_SSL_CLEANUP_THREAD;
398#endif
399 }
400
401#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
402
403 if (flags & WINPR_SSL_CLEANUP_THREAD)
404 {
405#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
406 ERR_remove_state(0);
407#else
408 ERR_remove_thread_state(nullptr);
409#endif
410 }
411
412#endif
413#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
414 OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
415 if (ctx)
416 OSSL_PROVIDER_do_all(ctx, unload, nullptr);
417#endif
418
419 return TRUE;
420}
421
422BOOL winpr_FIPSMode(void)
423{
424#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
425 return FALSE;
426#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
427 return (EVP_default_properties_is_fips_enabled(nullptr) == 1);
428#else
429 return (FIPS_mode() == 1);
430#endif
431}
432
433#else
434
435BOOL winpr_InitializeSSL(DWORD flags)
436{
437 return TRUE;
438}
439
440BOOL winpr_CleanupSSL(DWORD flags)
441{
442 return TRUE;
443}
444
445BOOL winpr_FIPSMode(void)
446{
447 return FALSE;
448}
449
450#endif