/src/strongswan/src/libstrongswan/library.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2009-2018 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 | | /** |
99 | | * Register plugins if built statically |
100 | | */ |
101 | | #ifdef STATIC_PLUGIN_CONSTRUCTORS |
102 | | #include "plugin_constructors.c" |
103 | | #endif |
104 | | |
105 | | /** |
106 | | * library instance |
107 | | */ |
108 | | library_t *lib = NULL; |
109 | | |
110 | | #ifdef LEAK_DETECTIVE |
111 | | /** |
112 | | * Default leak report callback |
113 | | */ |
114 | | CALLBACK(report_leaks, void, |
115 | | private_library_t *this, int count, size_t bytes, backtrace_t *bt, |
116 | | bool detailed) |
117 | | { |
118 | | fprintf(this->ld_out, "%zu bytes total, %d allocations, %zu bytes average:\n", |
119 | | bytes, count, bytes / count); |
120 | | bt->log(bt, this->ld_out, detailed); |
121 | | } |
122 | | |
123 | | /** |
124 | | * Default leak report summary callback |
125 | | */ |
126 | | CALLBACK(sum_leaks, void, |
127 | | private_library_t *this, int count, size_t bytes, int whitelisted) |
128 | | { |
129 | | switch (count) |
130 | | { |
131 | | case 0: |
132 | | fprintf(this->ld_out, "No leaks detected"); |
133 | | break; |
134 | | case 1: |
135 | | fprintf(this->ld_out, "One leak detected"); |
136 | | break; |
137 | | default: |
138 | | fprintf(this->ld_out, "%d leaks detected, %zu bytes", count, bytes); |
139 | | break; |
140 | | } |
141 | | fprintf(this->ld_out, ", %d suppressed by whitelist\n", whitelisted); |
142 | | } |
143 | | #endif /* LEAK_DETECTIVE */ |
144 | | |
145 | | /** |
146 | | * Deinitialize library |
147 | | */ |
148 | | void library_deinit() |
149 | 3.51k | { |
150 | 3.51k | private_library_t *this = (private_library_t*)lib; |
151 | 3.51k | bool detailed; |
152 | | |
153 | 3.51k | if (!this || !ref_put(&this->ref)) |
154 | 0 | { /* have more users */ |
155 | 0 | return; |
156 | 0 | } |
157 | | |
158 | 3.51k | detailed = lib->settings->get_bool(lib->settings, |
159 | 3.51k | "%s.leak_detective.detailed", TRUE, lib->ns); |
160 | | |
161 | | /* make sure the cache is clear before unloading plugins */ |
162 | 3.51k | lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); |
163 | | |
164 | 3.51k | this->public.streams->destroy(this->public.streams); |
165 | 3.51k | this->public.watcher->destroy(this->public.watcher); |
166 | 3.51k | this->public.scheduler->destroy(this->public.scheduler); |
167 | 3.51k | this->public.processor->destroy(this->public.processor); |
168 | 3.51k | this->public.plugins->destroy(this->public.plugins); |
169 | 3.51k | this->public.hosts->destroy(this->public.hosts); |
170 | 3.51k | this->public.settings->destroy(this->public.settings); |
171 | 3.51k | this->public.credmgr->destroy(this->public.credmgr); |
172 | 3.51k | this->public.creds->destroy(this->public.creds); |
173 | 3.51k | this->public.encoding->destroy(this->public.encoding); |
174 | 3.51k | this->public.ocsp->destroy(this->public.ocsp); |
175 | 3.51k | this->public.metadata->destroy(this->public.metadata); |
176 | 3.51k | this->public.crypto->destroy(this->public.crypto); |
177 | 3.51k | this->public.caps->destroy(this->public.caps); |
178 | 3.51k | this->public.proposal->destroy(this->public.proposal); |
179 | 3.51k | this->public.fetcher->destroy(this->public.fetcher); |
180 | 3.51k | this->public.resolver->destroy(this->public.resolver); |
181 | 3.51k | this->public.db->destroy(this->public.db); |
182 | 3.51k | this->public.printf_hook->destroy(this->public.printf_hook); |
183 | 3.51k | this->objects->destroy(this->objects); |
184 | 3.51k | if (this->public.integrity) |
185 | 0 | { |
186 | 0 | this->public.integrity->destroy(this->public.integrity); |
187 | 0 | } |
188 | | |
189 | 3.51k | if (lib->leak_detective) |
190 | 0 | { |
191 | 0 | lib->leak_detective->report(lib->leak_detective, detailed); |
192 | 0 | lib->leak_detective->destroy(lib->leak_detective); |
193 | 0 | lib->leak_detective = NULL; |
194 | 0 | } |
195 | | #ifdef LEAK_DETECTIVE |
196 | | if (this->ld_out && this->ld_out != stderr) |
197 | | { |
198 | | fclose(this->ld_out); |
199 | | } |
200 | | #endif /* LEAK_DETECTIVE */ |
201 | | |
202 | 3.51k | backtrace_deinit(); |
203 | 3.51k | arrays_deinit(); |
204 | 3.51k | utils_deinit(); |
205 | 3.51k | threads_deinit(); |
206 | | |
207 | 3.51k | free(this->public.conf); |
208 | 3.51k | free((void*)this->public.ns); |
209 | 3.51k | free(this); |
210 | 3.51k | lib = NULL; |
211 | 3.51k | } |
212 | | |
213 | | METHOD(library_t, get, void*, |
214 | | private_library_t *this, char *name) |
215 | 0 | { |
216 | 0 | return this->objects->get(this->objects, name); |
217 | 0 | } |
218 | | |
219 | | METHOD(library_t, set, bool, |
220 | | private_library_t *this, char *name, void *object) |
221 | 0 | { |
222 | 0 | if (object) |
223 | 0 | { |
224 | 0 | if (this->objects->get(this->objects, name)) |
225 | 0 | { |
226 | 0 | return FALSE; |
227 | 0 | } |
228 | 0 | this->objects->put(this->objects, name, object); |
229 | 0 | return TRUE; |
230 | 0 | } |
231 | 0 | return this->objects->remove(this->objects, name) != NULL; |
232 | 0 | } |
233 | | |
234 | | /** |
235 | | * Hashtable hash function |
236 | | */ |
237 | | static u_int hash(char *key) |
238 | 0 | { |
239 | 0 | return chunk_hash(chunk_create(key, strlen(key))); |
240 | 0 | } |
241 | | |
242 | | /** |
243 | | * Hashtable equals function |
244 | | */ |
245 | | static bool equals(char *a, char *b) |
246 | 0 | { |
247 | 0 | return streq(a, b); |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * Number of words we write and memwipe() in memwipe check |
252 | | */ |
253 | | #define MEMWIPE_WIPE_WORDS 16 |
254 | | |
255 | | #ifndef NO_CHECK_MEMWIPE |
256 | | |
257 | | /** |
258 | | * Write magic to memory, and try to clear it with memwipe() |
259 | | */ |
260 | | __attribute__((noinline)) |
261 | | static void do_magic(int *magic, int **out) |
262 | | { |
263 | | int buf[MEMWIPE_WIPE_WORDS], i; |
264 | | |
265 | | *out = buf; |
266 | | for (i = 0; i < countof(buf); i++) |
267 | | { |
268 | | buf[i] = *magic; |
269 | | } |
270 | | /* passing buf to dbg should make sure the compiler can't optimize out buf. |
271 | | * we use directly dbg(3), as DBG3() might be stripped with DEBUG_LEVEL. */ |
272 | | dbg(DBG_LIB, 3, "memwipe() pre: %b", buf, sizeof(buf)); |
273 | | memwipe(buf, sizeof(buf)); |
274 | | } |
275 | | |
276 | | /** |
277 | | * Check if memwipe works as expected |
278 | | */ |
279 | | static bool check_memwipe() |
280 | | { |
281 | | int magic = 0xCAFEBABE, *buf, i; |
282 | | |
283 | | do_magic(&magic, &buf); |
284 | | |
285 | | for (i = 0; i < MEMWIPE_WIPE_WORDS; i++) |
286 | | { |
287 | | if (buf[i] == magic) |
288 | | { |
289 | | DBG1(DBG_LIB, "memwipe() check failed: stackdir: %b", |
290 | | buf, MEMWIPE_WIPE_WORDS * sizeof(int)); |
291 | | return FALSE; |
292 | | } |
293 | | } |
294 | | return TRUE; |
295 | | } |
296 | | |
297 | | #endif |
298 | | |
299 | | /* |
300 | | * see header file |
301 | | */ |
302 | | bool library_init(char *settings, const char *namespace) |
303 | 3.51k | { |
304 | 3.51k | private_library_t *this; |
305 | 3.51k | printf_hook_t *pfh; |
306 | 3.51k | int i; |
307 | | |
308 | 3.51k | if (lib) |
309 | 0 | { /* already initialized, increase refcount */ |
310 | 0 | this = (private_library_t*)lib; |
311 | 0 | ref_get(&this->ref); |
312 | 0 | return !this->init_failed; |
313 | 0 | } |
314 | | |
315 | 3.51k | chunk_hash_seed(); |
316 | | |
317 | 3.51k | INIT(this, |
318 | 3.51k | .public = { |
319 | 3.51k | .get = _get, |
320 | 3.51k | .set = _set, |
321 | 3.51k | .ns = strdup(namespace ?: "libstrongswan"), |
322 | 3.51k | .conf = strdupnull(settings ?: (getenv("STRONGSWAN_CONF") ?: STRONGSWAN_CONF)), |
323 | 3.51k | }, |
324 | 3.51k | .ref = 1, |
325 | 3.51k | ); |
326 | 3.51k | lib = &this->public; |
327 | | |
328 | 3.51k | threads_init(); |
329 | 3.51k | utils_init(); |
330 | 3.51k | arrays_init(); |
331 | 3.51k | backtrace_init(); |
332 | | |
333 | | #ifdef LEAK_DETECTIVE |
334 | | { |
335 | | FILE *out = NULL; |
336 | | char *log; |
337 | | |
338 | | log = getenv("LEAK_DETECTIVE_LOG"); |
339 | | if (log) |
340 | | { |
341 | | out = fopen(log, "a"); |
342 | | } |
343 | | this->ld_out = out ?: stderr; |
344 | | } |
345 | | lib->leak_detective = leak_detective_create(); |
346 | | if (lib->leak_detective) |
347 | | { |
348 | | lib->leak_detective->set_report_cb(lib->leak_detective, |
349 | | report_leaks, sum_leaks, this); |
350 | | } |
351 | | #endif /* LEAK_DETECTIVE */ |
352 | | |
353 | 3.51k | pfh = printf_hook_create(); |
354 | 3.51k | this->public.printf_hook = pfh; |
355 | | |
356 | 3.51k | pfh->add_handler(pfh, 'b', mem_printf_hook, |
357 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT, |
358 | 3.51k | PRINTF_HOOK_ARGTYPE_END); |
359 | 3.51k | pfh->add_handler(pfh, 'B', chunk_printf_hook, |
360 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END); |
361 | 3.51k | pfh->add_handler(pfh, 'H', host_printf_hook, |
362 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END); |
363 | 3.51k | pfh->add_handler(pfh, 'N', enum_printf_hook, |
364 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT, |
365 | 3.51k | PRINTF_HOOK_ARGTYPE_END); |
366 | 3.51k | pfh->add_handler(pfh, 'T', time_printf_hook, |
367 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_INT, |
368 | 3.51k | PRINTF_HOOK_ARGTYPE_END); |
369 | 3.51k | pfh->add_handler(pfh, 'V', time_delta_printf_hook, |
370 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_POINTER, |
371 | 3.51k | PRINTF_HOOK_ARGTYPE_END); |
372 | 3.51k | pfh->add_handler(pfh, 'Y', identification_printf_hook, |
373 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END); |
374 | 3.51k | pfh->add_handler(pfh, 'R', traffic_selector_printf_hook, |
375 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END); |
376 | 3.51k | pfh->add_handler(pfh, 'P', proposal_printf_hook, |
377 | 3.51k | PRINTF_HOOK_ARGTYPE_POINTER, PRINTF_HOOK_ARGTYPE_END); |
378 | | |
379 | 3.51k | this->objects = hashtable_create((hashtable_hash_t)hash, |
380 | 3.51k | (hashtable_equals_t)equals, 4); |
381 | | |
382 | 3.51k | this->public.settings = settings_create(NULL); |
383 | 3.51k | if (!this->public.settings->load_files(this->public.settings, |
384 | 3.51k | this->public.conf, FALSE)) |
385 | 3.51k | { |
386 | 3.51k | DBG1(DBG_LIB, "abort initialization due to invalid configuration"); |
387 | 3.51k | this->init_failed = TRUE; |
388 | 3.51k | } |
389 | | |
390 | | /* add registered aliases */ |
391 | 3.51k | for (i = 0; i < ns_count; ++i) |
392 | 0 | { |
393 | 0 | lib->settings->add_fallback(lib->settings, lib->ns, namespaces[i]); |
394 | 0 | } |
395 | | /* all namespace settings may fall back to libstrongswan */ |
396 | 3.51k | lib->settings->add_fallback(lib->settings, lib->ns, "libstrongswan"); |
397 | | |
398 | 3.51k | this->public.hosts = host_resolver_create(); |
399 | 3.51k | this->public.proposal = proposal_keywords_create(); |
400 | 3.51k | this->public.caps = capabilities_create(); |
401 | 3.51k | this->public.crypto = crypto_factory_create(); |
402 | 3.51k | this->public.creds = credential_factory_create(); |
403 | 3.51k | this->public.credmgr = credential_manager_create(); |
404 | 3.51k | this->public.encoding = cred_encoding_create(); |
405 | 3.51k | this->public.ocsp = ocsp_responders_create(); |
406 | 3.51k | this->public.metadata = metadata_factory_create(); |
407 | 3.51k | this->public.fetcher = fetcher_manager_create(); |
408 | 3.51k | this->public.resolver = resolver_manager_create(); |
409 | 3.51k | this->public.db = database_factory_create(); |
410 | 3.51k | this->public.processor = processor_create(); |
411 | 3.51k | this->public.scheduler = scheduler_create(); |
412 | 3.51k | this->public.watcher = watcher_create(); |
413 | 3.51k | this->public.streams = stream_manager_create(); |
414 | 3.51k | this->public.plugins = plugin_loader_create(); |
415 | | |
416 | | #ifndef NO_CHECK_MEMWIPE |
417 | | if (!check_memwipe()) |
418 | | { |
419 | | return FALSE; |
420 | | } |
421 | | #endif |
422 | | |
423 | 3.51k | if (lib->settings->get_bool(lib->settings, |
424 | 3.51k | "%s.integrity_test", FALSE, lib->ns)) |
425 | 0 | { |
426 | | #ifdef INTEGRITY_TEST |
427 | | this->public.integrity = integrity_checker_create(CHECKSUM_LIBRARY); |
428 | | if (!lib->integrity->check(lib->integrity, "libstrongswan", library_init)) |
429 | | { |
430 | | DBG1(DBG_LIB, "integrity check of libstrongswan failed"); |
431 | | this->init_failed = TRUE; |
432 | | } |
433 | | #else /* !INTEGRITY_TEST */ |
434 | 0 | DBG1(DBG_LIB, "integrity test enabled, but not supported"); |
435 | 0 | this->init_failed = TRUE; |
436 | 0 | #endif /* INTEGRITY_TEST */ |
437 | 0 | } |
438 | | |
439 | 3.51k | diffie_hellman_init(); |
440 | | |
441 | 3.51k | return !this->init_failed; |
442 | 3.51k | } |