Coverage Report

Created: 2024-05-20 06:11

/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
    WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE);
79
  }
80
  else
81
  {
82
    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
    WaitForSingleObject(dynlock->mutex, INFINITE);
108
  }
109
  else
110
  {
111
    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
  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
              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
      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
1.70k
{
246
1.70k
  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 failled 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
1.70k
  return TRUE;
282
1.70k
}
283
284
static void winpr_openssl_cleanup(void)
285
1
{
286
1
  winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT);
287
1
}
288
289
static BOOL CALLBACK winpr_openssl_initialize(PINIT_ONCE once, PVOID param, PVOID* context)
290
1
{
291
1
  DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
292
293
1
  if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
294
0
  {
295
0
    return TRUE;
296
0
  }
297
298
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
299
300
  if (flags & WINPR_SSL_INIT_ENABLE_LOCKING)
301
  {
302
    if (!_winpr_openssl_initialize_locking())
303
    {
304
      return FALSE;
305
    }
306
  }
307
308
#endif
309
  /* SSL_load_error_strings() is void */
310
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
311
  SSL_load_error_strings();
312
  /* SSL_library_init() always returns "1" */
313
  SSL_library_init();
314
  OpenSSL_add_all_digests();
315
  OpenSSL_add_all_ciphers();
316
#else
317
318
1
  if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
319
1
                           OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
320
1
                           OPENSSL_INIT_ENGINE_ALL_BUILTIN,
321
1
                       NULL) != 1)
322
0
    return FALSE;
323
324
1
#endif
325
326
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
327
  /* The legacy provider is needed for MD4. */
328
  s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(NULL, "legacy");
329
  if (s_winpr_openssl_provider_legacy == NULL)
330
  {
331
    WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!");
332
  }
333
  s_winpr_openssl_provider_default = OSSL_PROVIDER_load(NULL, "default");
334
  if (s_winpr_openssl_provider_default == NULL)
335
  {
336
    WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load");
337
  }
338
#endif
339
340
1
  atexit(winpr_openssl_cleanup);
341
1
  g_winpr_openssl_initialized_by_winpr = TRUE;
342
1
  return TRUE;
343
1
}
344
345
/* exported functions */
346
347
BOOL winpr_InitializeSSL(DWORD flags)
348
1.70k
{
349
1.70k
  static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
350
351
1.70k
  if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, NULL))
352
0
    return FALSE;
353
354
1.70k
  return winpr_enable_fips(flags);
355
1.70k
}
356
357
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
358
static int unload(OSSL_PROVIDER* provider, void* data)
359
{
360
  if (!provider)
361
    return 1;
362
  const char* name = OSSL_PROVIDER_get0_name(provider);
363
  if (!name)
364
    return 1;
365
366
  OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
367
  const int rc = OSSL_PROVIDER_available(ctx, name);
368
  if (rc < 1)
369
    return 1;
370
  OSSL_PROVIDER_unload(provider);
371
  return 1;
372
}
373
#endif
374
375
BOOL winpr_CleanupSSL(DWORD flags)
376
1
{
377
1
  if (flags & WINPR_SSL_CLEANUP_GLOBAL)
378
0
  {
379
0
    if (!g_winpr_openssl_initialized_by_winpr)
380
0
    {
381
0
      WLog_WARN(TAG, "ssl was not initialized by winpr");
382
0
      return FALSE;
383
0
    }
384
385
0
    g_winpr_openssl_initialized_by_winpr = FALSE;
386
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
387
    _winpr_openssl_cleanup_locking();
388
#endif
389
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
390
    CRYPTO_cleanup_all_ex_data();
391
    ERR_free_strings();
392
    EVP_cleanup();
393
#endif
394
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
395
    flags |= WINPR_SSL_CLEANUP_THREAD;
396
#endif
397
0
  }
398
399
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
400
401
  if (flags & WINPR_SSL_CLEANUP_THREAD)
402
  {
403
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
404
    ERR_remove_state(0);
405
#else
406
    ERR_remove_thread_state(NULL);
407
#endif
408
  }
409
410
#endif
411
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
412
  OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
413
  OSSL_PROVIDER_do_all(ctx, unload, NULL);
414
#endif
415
416
1
  return TRUE;
417
1
}
418
419
BOOL winpr_FIPSMode(void)
420
0
{
421
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
422
  return FALSE;
423
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
424
  return (EVP_default_properties_is_fips_enabled(NULL) == 1);
425
#else
426
0
  return (FIPS_mode() == 1);
427
0
#endif
428
0
}
429
430
#else
431
432
BOOL winpr_InitializeSSL(DWORD flags)
433
{
434
  return TRUE;
435
}
436
437
BOOL winpr_CleanupSSL(DWORD flags)
438
{
439
  return TRUE;
440
}
441
442
BOOL winpr_FIPSMode(void)
443
{
444
  return FALSE;
445
}
446
447
#endif