Coverage Report

Created: 2025-11-24 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libstrongswan/library.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2009-2025 Tobias Brunner
3
 * Copyright (C) 2008 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#include "library.h"
19
20
#include <stdlib.h>
21
22
#include <utils/debug.h>
23
#include <threading/thread.h>
24
#include <utils/identification.h>
25
#include <networking/host.h>
26
#include <collections/array.h>
27
#include <collections/hashtable.h>
28
#include <utils/backtrace.h>
29
#include <selectors/traffic_selector.h>
30
#include <crypto/proposal/proposal.h>
31
32
#define CHECKSUM_LIBRARY IPSEC_LIB_DIR"/libchecksum.so"
33
34
#ifndef STRONGSWAN_CONF
35
#define STRONGSWAN_CONF NULL
36
#endif
37
38
typedef struct private_library_t private_library_t;
39
40
/**
41
 * private data of library
42
 */
43
struct private_library_t {
44
45
  /**
46
   * public functions
47
   */
48
  library_t public;
49
50
  /**
51
   * Hashtable with registered objects (name => object)
52
   */
53
  hashtable_t *objects;
54
55
  /**
56
   * Integrity check failed?
57
   */
58
  bool init_failed;
59
60
#ifdef LEAK_DETECTIVE
61
  /**
62
   * Where to write leak detective output to
63
   */
64
  FILE *ld_out;
65
#endif
66
67
  /**
68
   * Number of times we have been initialized
69
   */
70
  refcount_t ref;
71
};
72
73
0
#define MAX_NAMESPACES 5
74
75
/**
76
 * Additional namespaces registered using __attribute__((constructor))
77
 */
78
static char *namespaces[MAX_NAMESPACES];
79
static int ns_count;
80
81
/**
82
 * Described in header
83
 */
84
void library_add_namespace(char *ns)
85
0
{
86
0
  if (ns_count < MAX_NAMESPACES - 1)
87
0
  {
88
0
    namespaces[ns_count] = ns;
89
0
    ns_count++;
90
0
  }
91
0
  else
92
0
  {
93
0
    fprintf(stderr, "failed to register additional namespace alias, please "
94
0
        "increase MAX_NAMESPACES");
95
0
  }
96
0
}
97
98
0
#define MAX_LIBSTRONGSWAN_INIT_FUNCTIONS 10
99
100
/**
101
 * Static array for init function registration using __attribute__((constructor))
102
 */
103
static library_init_t init_functions[MAX_LIBSTRONGSWAN_INIT_FUNCTIONS];
104
static int init_function_count;
105
106
/**
107
 * Described in header
108
 */
109
void library_init_register(library_init_t init)
110
0
{
111
0
  if (init_function_count < MAX_LIBSTRONGSWAN_INIT_FUNCTIONS - 1)
112
0
  {
113
0
    init_functions[init_function_count++] = init;
114
0
  }
115
0
  else
116
0
  {
117
0
    fprintf(stderr, "failed to register init function, please increase "
118
0
        "MAX_LIBSTRONGSWAN_INIT_FUNCTIONS");
119
0
  }
120
0
}
121
122
/**
123
 * Register plugins if built statically
124
 */
125
#ifdef STATIC_PLUGIN_CONSTRUCTORS
126
#include "plugin_constructors.c"
127
#endif
128
129
/**
130
 * library instance
131
 */
132
library_t *lib = NULL;
133
134
#ifdef LEAK_DETECTIVE
135
/**
136
 * Default leak report callback
137
 */
138
CALLBACK(report_leaks, void,
139
  private_library_t *this, int count, size_t bytes, backtrace_t *bt,
140
  bool detailed)
141
{
142
  fprintf(this->ld_out, "%zu bytes total, %d allocations, %zu bytes average:\n",
143
      bytes, count, bytes / count);
144
  bt->log(bt, this->ld_out, detailed);
145
}
146
147
/**
148
 * Default leak report summary callback
149
 */
150
CALLBACK(sum_leaks, void,
151
  private_library_t *this, int count, size_t bytes, int whitelisted)
152
{
153
  switch (count)
154
  {
155
    case 0:
156
      fprintf(this->ld_out, "No leaks detected");
157
      break;
158
    case 1:
159
      fprintf(this->ld_out, "One leak detected");
160
      break;
161
    default:
162
      fprintf(this->ld_out, "%d leaks detected, %zu bytes", count, bytes);
163
      break;
164
  }
165
  fprintf(this->ld_out, ", %d suppressed by whitelist\n", whitelisted);
166
}
167
#endif /* LEAK_DETECTIVE */
168
169
/**
170
 * Deinitialize library
171
 */
172
void library_deinit()
173
2.25k
{
174
2.25k
  private_library_t *this = (private_library_t*)lib;
175
2.25k
  bool detailed;
176
2.25k
  int i;
177
178
2.25k
  if (!this || !ref_put(&this->ref))
179
0
  { /* have more users */
180
0
    return;
181
0
  }
182
183
2.25k
  detailed = lib->settings->get_bool(lib->settings,
184
2.25k
                "%s.leak_detective.detailed", TRUE, lib->ns);
185
186
  /* make sure the cache is clear before unloading plugins */
187
2.25k
  lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
188
189
2.25k
  for (i = 0; i < init_function_count; ++i)
190
0
  {
191
0
    init_functions[i](FALSE);
192
0
  }
193
194
2.25k
  key_exchange_deinit();
195
196
2.25k
  this->public.streams->destroy(this->public.streams);
197
2.25k
  this->public.watcher->destroy(this->public.watcher);
198
2.25k
  this->public.scheduler->destroy(this->public.scheduler);
199
2.25k
  this->public.processor->destroy(this->public.processor);
200
2.25k
  this->public.plugins->destroy(this->public.plugins);
201
2.25k
  this->public.hosts->destroy(this->public.hosts);
202
2.25k
  this->public.settings->destroy(this->public.settings);
203
2.25k
  this->public.credmgr->destroy(this->public.credmgr);
204
2.25k
  this->public.creds->destroy(this->public.creds);
205
2.25k
  this->public.encoding->destroy(this->public.encoding);
206
2.25k
  this->public.ocsp->destroy(this->public.ocsp);
207
2.25k
  this->public.metadata->destroy(this->public.metadata);
208
2.25k
  this->public.crypto->destroy(this->public.crypto);
209
2.25k
  this->public.caps->destroy(this->public.caps);
210
2.25k
  this->public.proposal->destroy(this->public.proposal);
211
2.25k
  this->public.fetcher->destroy(this->public.fetcher);
212
2.25k
  this->public.resolver->destroy(this->public.resolver);
213
2.25k
  this->public.db->destroy(this->public.db);
214
2.25k
  this->public.printf_hook->destroy(this->public.printf_hook);
215
2.25k
  this->objects->destroy(this->objects);
216
2.25k
  if (this->public.integrity)
217
0
  {
218
0
    this->public.integrity->destroy(this->public.integrity);
219
0
  }
220
221
2.25k
  if (lib->leak_detective)
222
0
  {
223
0
    lib->leak_detective->report(lib->leak_detective, detailed);
224
0
    lib->leak_detective->destroy(lib->leak_detective);
225
0
    lib->leak_detective = NULL;
226
0
  }
227
#ifdef LEAK_DETECTIVE
228
  if (this->ld_out && this->ld_out != stderr)
229
  {
230
    fclose(this->ld_out);
231
  }
232
#endif /* LEAK_DETECTIVE */
233
234
2.25k
  backtrace_deinit();
235
2.25k
  arrays_deinit();
236
2.25k
  utils_deinit();
237
2.25k
  threads_deinit();
238
239
2.25k
  free(this->public.conf);
240
2.25k
  free((void*)this->public.ns);
241
2.25k
  free(this);
242
2.25k
  lib = NULL;
243
2.25k
}
244
245
METHOD(library_t, get, void*,
246
  private_library_t *this, char *name)
247
0
{
248
0
  return this->objects->get(this->objects, name);
249
0
}
250
251
METHOD(library_t, set, bool,
252
  private_library_t *this, char *name, void *object)
253
0
{
254
0
  if (object)
255
0
  {
256
0
    if (this->objects->get(this->objects, name))
257
0
    {
258
0
      return FALSE;
259
0
    }
260
0
    this->objects->put(this->objects, name, object);
261
0
    return TRUE;
262
0
  }
263
0
  return this->objects->remove(this->objects, name) != NULL;
264
0
}
265
266
/**
267
 * Hashtable hash function
268
 */
269
static u_int hash(char *key)
270
0
{
271
0
  return chunk_hash(chunk_create(key, strlen(key)));
272
0
}
273
274
/**
275
 * Hashtable equals function
276
 */
277
static bool equals(char *a, char *b)
278
0
{
279
0
  return streq(a, b);
280
0
}
281
282
/**
283
 * Number of words we write and memwipe() in memwipe check
284
 */
285
#define MEMWIPE_WIPE_WORDS 16
286
287
#ifndef NO_CHECK_MEMWIPE
288
289
/**
290
 * Write magic to memory, and try to clear it with memwipe()
291
 */
292
__attribute__((noinline))
293
static void do_magic(int *magic, int **out)
294
{
295
  int buf[MEMWIPE_WIPE_WORDS], i;
296
297
  *out = buf;
298
  for (i = 0; i < countof(buf); i++)
299
  {
300
    buf[i] = *magic;
301
  }
302
  /* passing buf to dbg should make sure the compiler can't optimize out buf.
303
   * we use directly dbg(3), as DBG3() might be stripped with DEBUG_LEVEL. */
304
  dbg(DBG_LIB, 3, "memwipe() pre: %b", buf, sizeof(buf));
305
  memwipe(buf, sizeof(buf));
306
}
307
308
/**
309
 * Check if memwipe works as expected
310
 */
311
ADDRESS_SANITIZER_EXCLUDE
312
static bool check_memwipe()
313
{
314
  int magic = 0xCAFEBABE, *buf, i;
315
316
  do_magic(&magic, &buf);
317
318
  for (i = 0; i < MEMWIPE_WIPE_WORDS; i++)
319
  {
320
    if (buf[i] == magic)
321
    {
322
      DBG1(DBG_LIB, "memwipe() check failed: stackdir: %b",
323
         buf, MEMWIPE_WIPE_WORDS * sizeof(int));
324
      return FALSE;
325
    }
326
  }
327
  return TRUE;
328
}
329
330
#endif
331
332
/*
333
 * see header file
334
 */
335
bool library_init(char *settings, const char *namespace)
336
2.25k
{
337
2.25k
  private_library_t *this;
338
2.25k
  printf_hook_t *pfh;
339
2.25k
  int i;
340
341
2.25k
  if (lib)
342
0
  { /* already initialized, increase refcount */
343
0
    this = (private_library_t*)lib;
344
0
    ref_get(&this->ref);
345
0
    return !this->init_failed;
346
0
  }
347
348
2.25k
  chunk_hash_seed();
349
350
2.25k
  INIT(this,
351
2.25k
    .public = {
352
2.25k
      .get = _get,
353
2.25k
      .set = _set,
354
2.25k
      .ns = strdup(namespace ?: "libstrongswan"),
355
2.25k
      .conf = strdupnull(settings ?: (getenv("STRONGSWAN_CONF") ?: STRONGSWAN_CONF)),
356
2.25k
    },
357
2.25k
    .ref = 1,
358
2.25k
  );
359
2.25k
  lib = &this->public;
360
361
2.25k
  threads_init();
362
2.25k
  utils_init();
363
2.25k
  arrays_init();
364
2.25k
  backtrace_init();
365
366
#ifdef LEAK_DETECTIVE
367
  {
368
    FILE *out = NULL;
369
    char *log;
370
371
    log = getenv("LEAK_DETECTIVE_LOG");
372
    if (log)
373
    {
374
      out = fopen(log, "a");
375
    }
376
    this->ld_out = out ?: stderr;
377
  }
378
  lib->leak_detective = leak_detective_create();
379
  if (lib->leak_detective)
380
  {
381
    lib->leak_detective->set_report_cb(lib->leak_detective,
382
                       report_leaks, sum_leaks, this);
383
  }
384
#endif /* LEAK_DETECTIVE */
385
386
2.25k
  pfh = printf_hook_create();
387
2.25k
  this->public.printf_hook = pfh;
388
389
2.25k
  pfh->add_handler(pfh, 'b', mem_printf_hook,
390
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT,
391
2.25k
           PRINTF_HOOK_ARGTYPE_END);
392
2.25k
  pfh->add_handler(pfh, 'B', chunk_printf_hook,
393
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END);
394
2.25k
  pfh->add_handler(pfh, 'H', host_printf_hook,
395
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END);
396
2.25k
  pfh->add_handler(pfh, 'N', enum_printf_hook,
397
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT,
398
2.25k
           PRINTF_HOOK_ARGTYPE_END);
399
2.25k
  pfh->add_handler(pfh, 'T', time_printf_hook,
400
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT,
401
2.25k
           PRINTF_HOOK_ARGTYPE_END);
402
2.25k
  pfh->add_handler(pfh, 'V', time_delta_printf_hook,
403
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_POINTER,
404
2.25k
           PRINTF_HOOK_ARGTYPE_END);
405
2.25k
  pfh->add_handler(pfh, 'Y', identification_printf_hook,
406
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END);
407
2.25k
  pfh->add_handler(pfh, 'R', traffic_selector_printf_hook,
408
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END);
409
2.25k
  pfh->add_handler(pfh, 'P', proposal_printf_hook,
410
2.25k
           PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END);
411
412
2.25k
  this->objects = hashtable_create((hashtable_hash_t)hash,
413
2.25k
                   (hashtable_equals_t)equals, 4);
414
415
2.25k
  this->public.settings = settings_create(NULL);
416
2.25k
  if (!this->public.settings->load_files(this->public.settings,
417
2.25k
                       this->public.conf, FALSE))
418
2.25k
  {
419
2.25k
    DBG1(DBG_LIB, "abort initialization due to invalid configuration");
420
2.25k
    this->init_failed = TRUE;
421
2.25k
  }
422
423
  /* add registered aliases */
424
2.25k
  for (i = 0; i < ns_count; ++i)
425
0
  {
426
0
    lib->settings->add_fallback(lib->settings, lib->ns, namespaces[i]);
427
0
  }
428
  /* all namespace settings may fall back to libstrongswan */
429
2.25k
  lib->settings->add_fallback(lib->settings, lib->ns, "libstrongswan");
430
431
2.25k
  this->public.hosts = host_resolver_create();
432
2.25k
  this->public.proposal = proposal_keywords_create();
433
2.25k
  this->public.caps = capabilities_create();
434
2.25k
  this->public.crypto = crypto_factory_create();
435
2.25k
  this->public.creds = credential_factory_create();
436
2.25k
  this->public.credmgr = credential_manager_create();
437
2.25k
  this->public.encoding = cred_encoding_create();
438
2.25k
  this->public.ocsp = ocsp_responders_create();
439
2.25k
  this->public.metadata = metadata_factory_create();
440
2.25k
  this->public.fetcher = fetcher_manager_create();
441
2.25k
  this->public.resolver = resolver_manager_create();
442
2.25k
  this->public.db = database_factory_create();
443
2.25k
  this->public.processor = processor_create();
444
2.25k
  this->public.scheduler = scheduler_create();
445
2.25k
  this->public.watcher = watcher_create();
446
2.25k
  this->public.streams = stream_manager_create();
447
2.25k
  this->public.plugins = plugin_loader_create();
448
449
#ifndef NO_CHECK_MEMWIPE
450
  if (!check_memwipe())
451
  {
452
    return FALSE;
453
  }
454
#endif
455
456
2.25k
  if (lib->settings->get_bool(lib->settings,
457
2.25k
                "%s.integrity_test", FALSE, lib->ns))
458
0
  {
459
#ifdef INTEGRITY_TEST
460
    this->public.integrity = integrity_checker_create(CHECKSUM_LIBRARY);
461
    if (!lib->integrity->check(lib->integrity, "libstrongswan", library_init))
462
    {
463
      DBG1(DBG_LIB, "integrity check of libstrongswan failed");
464
      this->init_failed = TRUE;
465
    }
466
#else /* !INTEGRITY_TEST */
467
0
    DBG1(DBG_LIB, "integrity test enabled, but not supported");
468
0
    this->init_failed = TRUE;
469
0
#endif /* INTEGRITY_TEST */
470
0
  }
471
472
2.25k
  key_exchange_init();
473
474
2.25k
  for (i = 0; i < init_function_count; ++i)
475
0
  {
476
0
    if (!init_functions[i](TRUE))
477
0
    {
478
      this->init_failed = TRUE;
479
0
    }
480
0
  }
481
2.25k
  return !this->init_failed;
482
2.25k
}