Coverage Report

Created: 2026-04-12 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/utils/ssl.c
Line
Count
Source
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/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
42
static BOOL g_winpr_openssl_initialized_by_winpr = FALSE;
43
44
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
45
static OSSL_PROVIDER* s_winpr_openssl_provider_fips = nullptr;
46
static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = nullptr;
47
static OSSL_PROVIDER* s_winpr_openssl_provider_default = nullptr;
48
#endif
49
50
/**
51
 * Note from OpenSSL 1.1.0 "CHANGES":
52
 * OpenSSL now uses a new threading API. It is no longer necessary to
53
 * set locking callbacks to use OpenSSL in a multi-threaded environment.
54
 */
55
56
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
57
58
#define WINPR_OPENSSL_LOCKING_REQUIRED 1
59
60
static int g_winpr_openssl_num_locks = 0;
61
static HANDLE* g_winpr_openssl_locks = nullptr;
62
63
struct CRYPTO_dynlock_value
64
{
65
  HANDLE mutex;
66
};
67
68
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
69
static unsigned long _winpr_openssl_id(void)
70
{
71
  return (unsigned long)GetCurrentThreadId();
72
}
73
#endif
74
75
static 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
87
static 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
103
static 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
116
static 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
123
static 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
198
static 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
245
static BOOL winpr_enable_fips(DWORD flags)
246
1.99k
{
247
1.99k
  if (flags & WINPR_SSL_INIT_ENABLE_FIPS)
248
0
  {
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
0
    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
0
    if (FIPS_mode() != 1)
264
0
#endif
265
0
    {
266
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
267
      if (EVP_set_default_properties(nullptr, "fips=yes"))
268
#else
269
0
      if (FIPS_mode_set(1))
270
0
#endif
271
0
        WLog_INFO(TAG, "Openssl fips mode enabled!");
272
0
      else
273
0
      {
274
0
        WLog_ERR(TAG, "Openssl fips mode enable failed!");
275
0
        return FALSE;
276
0
      }
277
0
    }
278
279
0
#endif
280
0
  }
281
282
1.99k
  return TRUE;
283
1.99k
}
284
285
static void winpr_openssl_cleanup(void)
286
1
{
287
1
  winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT);
288
1
}
289
290
static BOOL CALLBACK winpr_openssl_initialize(WINPR_ATTR_UNUSED PINIT_ONCE once, PVOID param,
291
                                              WINPR_ATTR_UNUSED PVOID* context)
292
1
{
293
1
  DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
294
295
1
  if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
296
0
  {
297
0
    return TRUE;
298
0
  }
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
1
  if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
321
1
                           OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
322
1
                           OPENSSL_INIT_ENGINE_ALL_BUILTIN,
323
1
                       nullptr) != 1)
324
0
    return FALSE;
325
326
1
#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
1
  (void)winpr_atexit(winpr_openssl_cleanup);
343
1
  g_winpr_openssl_initialized_by_winpr = TRUE;
344
1
  return TRUE;
345
1
}
346
347
/* exported functions */
348
349
BOOL winpr_InitializeSSL(DWORD flags)
350
1.99k
{
351
1.99k
  static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
352
353
1.99k
  if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, nullptr))
354
0
    return FALSE;
355
356
1.99k
  return winpr_enable_fips(flags);
357
1.99k
}
358
359
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
360
static 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
377
BOOL winpr_CleanupSSL(DWORD flags)
378
1
{
379
1
  if (flags & WINPR_SSL_CLEANUP_GLOBAL)
380
0
  {
381
0
    if (!g_winpr_openssl_initialized_by_winpr)
382
0
    {
383
0
      WLog_WARN(TAG, "ssl was not initialized by winpr");
384
0
      return FALSE;
385
0
    }
386
387
0
    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
0
  }
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
1
  return TRUE;
420
1
}
421
422
BOOL winpr_FIPSMode(void)
423
0
{
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
0
  return (FIPS_mode() == 1);
430
0
#endif
431
0
}
432
433
#else
434
435
BOOL winpr_InitializeSSL(DWORD flags)
436
{
437
  return TRUE;
438
}
439
440
BOOL winpr_CleanupSSL(DWORD flags)
441
{
442
  return TRUE;
443
}
444
445
BOOL winpr_FIPSMode(void)
446
{
447
  return FALSE;
448
}
449
450
#endif