Coverage Report

Created: 2025-07-01 06:46

/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