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