Don't use union members directly in plugin_feature_t.
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include "plugin_loader.h"
19
20 #include <string.h>
21 #include <dlfcn.h>
22 #include <limits.h>
23 #include <stdio.h>
24
25 #include <debug.h>
26 #include <library.h>
27 #include <integrity_checker.h>
28 #include <utils/linked_list.h>
29 #include <plugins/plugin.h>
30
31 typedef struct private_plugin_loader_t private_plugin_loader_t;
32 typedef struct plugin_entry_t plugin_entry_t;
33
34 /**
35 * private data of plugin_loader
36 */
37 struct private_plugin_loader_t {
38
39 /**
40 * public functions
41 */
42 plugin_loader_t public;
43
44 /**
45 * List of plugins, as plugin_entry_t
46 */
47 linked_list_t *plugins;
48 };
49
50 /**
51 * Entry for a plugin
52 */
53 struct plugin_entry_t {
54
55 /**
56 * Plugin instance
57 */
58 plugin_t *plugin;
59
60 /**
61 * dlopen handle, if in separate lib
62 */
63 void *handle;
64
65 /**
66 * List of loaded features
67 */
68 linked_list_t *loaded;
69 };
70
71 /**
72 * Destroy a plugin entry
73 */
74 static void plugin_entry_destroy(plugin_entry_t *entry)
75 {
76 DESTROY_IF(entry->plugin);
77 if (entry->handle)
78 {
79 dlclose(entry->handle);
80 }
81 entry->loaded->destroy(entry->loaded);
82 free(entry);
83 }
84
85 /**
86 * create a plugin
87 * returns: NOT_FOUND, if the constructor was not found
88 * FAILED, if the plugin could not be constructed
89 */
90 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
91 char *name, bool integrity, plugin_entry_t **entry)
92 {
93 char create[128];
94 plugin_t *plugin;
95 plugin_constructor_t constructor;
96
97 if (snprintf(create, sizeof(create), "%s_plugin_create",
98 name) >= sizeof(create))
99 {
100 return FAILED;
101 }
102 translate(create, "-", "_");
103 constructor = dlsym(handle, create);
104 if (constructor == NULL)
105 {
106 return NOT_FOUND;
107 }
108 if (integrity && lib->integrity)
109 {
110 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
111 {
112 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
113 return FAILED;
114 }
115 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
116 name);
117 }
118 plugin = constructor();
119 if (plugin == NULL)
120 {
121 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
122 create);
123 return FAILED;
124 }
125 INIT(*entry,
126 .plugin = plugin,
127 .loaded = linked_list_create(),
128 );
129 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
130 return SUCCESS;
131 }
132
133 /**
134 * load a single plugin
135 */
136 static bool load_plugin(private_plugin_loader_t *this, char *name, char *file)
137 {
138 plugin_entry_t *entry;
139 void *handle;
140
141 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry))
142 {
143 case SUCCESS:
144 this->plugins->insert_last(this->plugins, entry);
145 return TRUE;
146 case NOT_FOUND:
147 /* try to load the plugin from a file */
148 break;
149 default:
150 return FALSE;
151 }
152 if (lib->integrity)
153 {
154 if (!lib->integrity->check_file(lib->integrity, name, file))
155 {
156 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
157 name, file);
158 return FALSE;
159 }
160 }
161 handle = dlopen(file, RTLD_LAZY);
162 if (handle == NULL)
163 {
164 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
165 return FALSE;
166 }
167 if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS)
168 {
169 dlclose(handle);
170 return FALSE;
171 }
172 entry->handle = handle;
173 this->plugins->insert_last(this->plugins, entry);
174 return TRUE;
175 }
176
177 /**
178 * Convert enumerated entries to plugin_t
179 */
180 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
181 void *in, linked_list_t **list)
182 {
183 *plugin = (*entry)->plugin;
184 if (list)
185 {
186 *list = (*entry)->loaded;
187 }
188 return TRUE;
189 }
190
191 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
192 private_plugin_loader_t *this)
193 {
194 return enumerator_create_filter(
195 this->plugins->create_enumerator(this->plugins),
196 (void*)plugin_filter, NULL, NULL);
197 }
198
199 /**
200 * Check if a plugin is already loaded
201 */
202 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
203 {
204 enumerator_t *enumerator;
205 bool found = FALSE;
206 plugin_t *plugin;
207
208 enumerator = create_plugin_enumerator(this);
209 while (enumerator->enumerate(enumerator, &plugin, NULL))
210 {
211 if (streq(plugin->get_name(plugin), name))
212 {
213 found = TRUE;
214 break;
215 }
216 }
217 enumerator->destroy(enumerator);
218 return found;
219 }
220
221 /**
222 * Check if a feature of a plugin is already loaded
223 */
224 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
225 plugin_feature_t *feature)
226 {
227 return entry->loaded->find_first(entry->loaded, NULL,
228 (void**)&feature) == SUCCESS;
229 }
230
231 /**
232 * Check if dependencies are satisfied
233 */
234 static bool dependencies_satisfied(private_plugin_loader_t *this, char *name,
235 bool soft, bool report, plugin_feature_t *features, int count)
236 {
237 int i;
238
239 /* first entry is provided feature, followed by dependencies */
240 for (i = 1; i < count; i++)
241 {
242 enumerator_t *entries, *loaded;
243 plugin_feature_t *feature;
244 plugin_entry_t *entry;
245 bool found = FALSE;
246
247 if (features[i].kind != FEATURE_DEPENDS &&
248 features[i].kind != FEATURE_SDEPEND)
249 { /* end of dependencies */
250 break;
251 }
252 entries = this->plugins->create_enumerator(this->plugins);
253 while (entries->enumerate(entries, &entry))
254 {
255 loaded = entry->loaded->create_enumerator(entry->loaded);
256 while (loaded->enumerate(loaded, &feature))
257 {
258 if (plugin_feature_matches(&features[i], feature))
259 {
260 found = TRUE;
261 break;
262 }
263 }
264 loaded->destroy(loaded);
265 }
266 entries->destroy(entries);
267
268 if (!found && (features[i].kind != FEATURE_SDEPEND || soft))
269 {
270 if (report)
271 {
272 char *provide, *depend;
273
274 provide = plugin_feature_get_string(&features[0]);
275 depend = plugin_feature_get_string(&features[i]);
276 DBG1(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
277 "dependency: %s", provide, name, depend);
278 free(provide);
279 free(depend);
280 }
281 return FALSE;
282 }
283 }
284 return TRUE;
285 }
286
287 /**
288 * Load a plugin feature
289 */
290 static bool load_feature(private_plugin_loader_t *this, plugin_entry_t *entry,
291 char *name, plugin_feature_t *feature, plugin_feature_t *reg)
292 {
293 char *str;
294
295 str = plugin_feature_get_string(feature);
296 switch (feature->type)
297 {
298 case FEATURE_CRYPTER:
299 case FEATURE_AEAD:
300 case FEATURE_SIGNER:
301 case FEATURE_HASHER:
302 case FEATURE_PRF:
303 case FEATURE_DH:
304 case FEATURE_RNG:
305 case FEATURE_PRIVKEY:
306 case FEATURE_PRIVKEY_GEN:
307 case FEATURE_PUBKEY:
308 case FEATURE_CERT_DECODE:
309 case FEATURE_CERT_ENCODE:
310 case FEATURE_DATABASE:
311 case FEATURE_FETCHER:
312 /* require a registration function */
313 if (!reg ||
314 (reg->kind == FEATURE_REGISTER && reg->type != feature->type))
315 {
316 DBG1(DBG_LIB, "loading '%s' plugin feature %s failed: "
317 "invalid registration function", name, str);
318 free(str);
319 return FALSE;
320 }
321 break;
322 default:
323 break;
324 }
325 if (reg && reg->kind == FEATURE_CALLBACK)
326 {
327 if (!reg->arg.cb.f(entry->plugin, feature, TRUE, reg->arg.cb.data))
328 {
329 DBG1(DBG_LIB, "loading '%s' plugin feature %s with callback failed",
330 name, str);
331 free(str);
332 return FALSE;
333 }
334 }
335 else
336 {
337 switch (feature->type)
338 {
339 case FEATURE_CRYPTER:
340 lib->crypto->add_crypter(lib->crypto, feature->arg.crypter.alg,
341 name, reg->arg.reg.f);
342 break;
343 case FEATURE_AEAD:
344 lib->crypto->add_aead(lib->crypto, feature->arg.aead.alg,
345 name, reg->arg.reg.f);
346 break;
347 case FEATURE_SIGNER:
348 lib->crypto->add_signer(lib->crypto, feature->arg.signer,
349 name, reg->arg.reg.f);
350 break;
351 case FEATURE_HASHER:
352 lib->crypto->add_hasher(lib->crypto, feature->arg.hasher,
353 name, reg->arg.reg.f);
354 break;
355 case FEATURE_PRF:
356 lib->crypto->add_prf(lib->crypto, feature->arg.prf,
357 name, reg->arg.reg.f);
358 break;
359 case FEATURE_DH:
360 lib->crypto->add_dh(lib->crypto, feature->arg.dh_group,
361 name, reg->arg.reg.f);
362 break;
363 case FEATURE_RNG:
364 lib->crypto->add_rng(lib->crypto, feature->arg.rng_quality,
365 name, reg->arg.reg.f);
366 break;
367 case FEATURE_PRIVKEY:
368 case FEATURE_PRIVKEY_GEN:
369 lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY,
370 feature->arg.privkey, reg->arg.reg.final,
371 reg->arg.reg.f);
372 break;
373 case FEATURE_PUBKEY:
374 lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY,
375 feature->arg.pubkey, reg->arg.reg.final,
376 reg->arg.reg.f);
377 break;
378 case FEATURE_CERT_DECODE:
379 case FEATURE_CERT_ENCODE:
380 lib->creds->add_builder(lib->creds, CRED_CERTIFICATE,
381 feature->arg.cert, reg->arg.reg.final,
382 reg->arg.reg.f);
383 break;
384 case FEATURE_DATABASE:
385 lib->db->add_database(lib->db, reg->arg.reg.f);
386 break;
387 case FEATURE_FETCHER:
388 lib->fetcher->add_fetcher(lib->fetcher, reg->arg.reg.f,
389 feature->arg.fetcher);
390 break;
391 default:
392 break;
393 }
394 }
395 DBG2(DBG_LIB, "loaded '%s' plugin feature %s", name, str);
396 free(str);
397 entry->loaded->insert_last(entry->loaded, feature);
398 return TRUE;
399 }
400
401 /**
402 * Load plugin features in correct order
403 */
404 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
405 {
406 enumerator_t *enumerator;
407 plugin_feature_t *features, *reg = NULL;
408 plugin_entry_t *entry;
409 int count, i, loaded = 0;
410 char *name;
411
412 enumerator = this->plugins->create_enumerator(this->plugins);
413 while (enumerator->enumerate(enumerator, &entry))
414 {
415 if (!entry->plugin->get_features)
416 { /* feature interface not supported */
417 continue;
418 }
419 name = entry->plugin->get_name(entry->plugin);
420 count = entry->plugin->get_features(entry->plugin, &features);
421 for (i = 0; i < count; i++)
422 {
423 switch (features[i].kind)
424 {
425 case FEATURE_PROVIDE:
426 if (!feature_loaded(this, entry, &features[i]) &&
427 dependencies_satisfied(this, name, soft, report,
428 &features[i], count - i) &&
429 load_feature(this, entry, name, &features[i], reg))
430 {
431 loaded++;
432 }
433 break;
434 case FEATURE_REGISTER:
435 case FEATURE_CALLBACK:
436 reg = &features[i];
437 break;
438 default:
439 break;
440 }
441 }
442 }
443 enumerator->destroy(enumerator);
444 return loaded;
445 }
446
447 /**
448 * Remove plugins that we were not able to load any features from.
449 */
450 static void purge_plugins(private_plugin_loader_t *this)
451 {
452 enumerator_t *enumerator;
453 plugin_entry_t *entry;
454
455 enumerator = this->plugins->create_enumerator(this->plugins);
456 while (enumerator->enumerate(enumerator, &entry))
457 {
458 if (!entry->plugin->get_features)
459 { /* feature interface not supported */
460 continue;
461 }
462 if (!entry->loaded->get_count(entry->loaded))
463 {
464 this->plugins->remove_at(this->plugins, enumerator);
465 plugin_entry_destroy(entry);
466 }
467 }
468 enumerator->destroy(enumerator);
469 }
470
471 METHOD(plugin_loader_t, load_plugins, bool,
472 private_plugin_loader_t *this, char *path, char *list)
473 {
474 enumerator_t *enumerator;
475 char *token;
476 bool critical_failed = FALSE;
477
478 if (path == NULL)
479 {
480 path = PLUGINDIR;
481 }
482
483 enumerator = enumerator_create_token(list, " ", " ");
484 while (!critical_failed && enumerator->enumerate(enumerator, &token))
485 {
486 bool critical = FALSE;
487 char file[PATH_MAX];
488 int len;
489
490 token = strdup(token);
491 len = strlen(token);
492 if (token[len-1] == '!')
493 {
494 critical = TRUE;
495 token[len-1] = '\0';
496 }
497 if (plugin_loaded(this, token))
498 {
499 free(token);
500 continue;
501 }
502 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
503 path, token) >= sizeof(file))
504 {
505 return NULL;
506 }
507 if (!load_plugin(this, token, file) && critical)
508 {
509 critical_failed = TRUE;
510 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
511 }
512 free(token);
513 }
514 enumerator->destroy(enumerator);
515 if (!critical_failed)
516 {
517 while (load_features(this, TRUE, FALSE))
518 {
519 /* try load new features until we don't get new ones */
520 }
521 while (load_features(this, FALSE, FALSE))
522 {
523 /* second round, ignoring soft dependencies */
524 }
525 /* report missing dependencies */
526 load_features(this, FALSE, TRUE);
527 /* unload plugins that we were not able to load any features for */
528 purge_plugins(this);
529 }
530 return !critical_failed;
531 }
532
533 METHOD(plugin_loader_t, unload, void,
534 private_plugin_loader_t *this)
535 {
536 plugin_entry_t *entry;
537
538 /* unload plugins in reverse order */
539 while (this->plugins->remove_last(this->plugins,
540 (void**)&entry) == SUCCESS)
541 {
542 if (lib->leak_detective)
543 { /* keep handle to report leaks properly */
544 entry->handle = NULL;
545 }
546 plugin_entry_destroy(entry);
547 }
548 }
549
550 /**
551 * Reload a plugin by name, NULL for all
552 */
553 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
554 {
555 u_int reloaded = 0;
556 enumerator_t *enumerator;
557 plugin_t *plugin;
558
559 enumerator = create_plugin_enumerator(this);
560 while (enumerator->enumerate(enumerator, &plugin, NULL))
561 {
562 if (name == NULL || streq(name, plugin->get_name(plugin)))
563 {
564 if (plugin->reload && plugin->reload(plugin))
565 {
566 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
567 plugin->get_name(plugin));
568 reloaded++;
569 }
570 }
571 }
572 enumerator->destroy(enumerator);
573 return reloaded;
574 }
575
576 METHOD(plugin_loader_t, reload, u_int,
577 private_plugin_loader_t *this, char *list)
578 {
579 u_int reloaded = 0;
580 enumerator_t *enumerator;
581 char *name;
582
583 if (list == NULL)
584 {
585 return reload_by_name(this, NULL);
586 }
587 enumerator = enumerator_create_token(list, " ", "");
588 while (enumerator->enumerate(enumerator, &name))
589 {
590 reloaded += reload_by_name(this, name);
591 }
592 enumerator->destroy(enumerator);
593 return reloaded;
594 }
595
596 METHOD(plugin_loader_t, destroy, void,
597 private_plugin_loader_t *this)
598 {
599 unload(this);
600 this->plugins->destroy(this->plugins);
601 free(this);
602 }
603
604 /*
605 * see header file
606 */
607 plugin_loader_t *plugin_loader_create()
608 {
609 private_plugin_loader_t *this;
610
611 INIT(this,
612 .public = {
613 .load = _load_plugins,
614 .reload = _reload,
615 .unload = _unload,
616 .create_plugin_enumerator = _create_plugin_enumerator,
617 .destroy = _destroy,
618 },
619 .plugins = linked_list_create(),
620 );
621
622 return &this->public;
623 }
624