/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 | } |